Const char что это
C++ поддерживает две нотации неизменности:
constexpr функция должна быть достаточно простой, чтобы вычисляться компилятором, а также возвращать вычисленное значение. constexpr функции могут вызываться неконстантыми аргументами в контексте которых не требуются константные выражения.
const
Объекты со спецификатором const не могут быть изменены, а также должны быть инициализированы.
Поскольку объекты со спецификаторов const не могут быть изменены, то следующий код будет ошибочным:
Обратите внимание, что const изменяет тип объекта, а не указание того, как должна быть назначена переменная. const ограничивает способы работы с объектом.
При использовании указателя задействуются два объекта: сам указатель и объект, на который указывает. Префиксное’ объявление указателя с const делает константным объект, а не указатель. Чтобы объявить как const сам указатель, а не объект, на который он указывает, необходимо поместить const после символа указателя. Например:
Местоположение const относительно базового типа не принципиально, поскольку не существует типа данных const*. Принципиальным является положение const относительно символа *. Поэтому возможны следующие записи:
Первая версия используется для строк, элементы которых не должны быть изменены функцией и возвращает указатель на const, который не позволяет изменять результат. Вторая версия используется для изменяемых строк.
Вы можете назначить адрес неконстантной переменной указателю на константу, потому что это не может нанести никакого вреда. Однако адрес константы нельзя назначить неконстантному указателю, поскольку это позволит изменить значение объекта. Например:
constexpr
Константное выражение является выражением, которое вычисляется во время компиляции. Константные выражения не могут использовать значения и переменные, которые не известны во время компиляции.
Существует множество причин, по которым кому-то может понадобиться именованная константа, а не буква или значение, хранящееся в переменной:
Значение constexpr вычисляется во время выполнения компиляции, и если оно не может быть вычислено, то компилятор выдаст ошибку.
Разница между char* и const char*?
в чем разница между
что указывает на постоянный строковый литерал и
9 ответов
char* Это mutable указатель на mutable символ/строку.
const char* это mutable указатель на неизменяемые символ/строку. Вы не можете изменить содержимое местоположения, на которое указывает этот указатель. Кроме того, компиляторы должны выдавать сообщения об ошибках при попытке сделать это. По той же причине, преобразование из const char * to char* устарела.
char* const это неизменяемые указатель (он не может указывать на любое другое место)но содержимое местоположение точки mutable.
const char* const это неизменяемые указатель на неизменяемые символ/строку.
вы можете изменить символ, который name указывает, а также символ, на который он указывает.
EDIT:
из комментариев ваш вопрос, похоже, спрашивает о разнице между двумя объявлениями, когда указатель указывает на строковый литерал.
в этом случае не должен изменить символ, к которому name баллов, так как это может привести к неопределено Поведение. Строковые литералы могут быть выделены в областях памяти только для чтения (реализация определена), и пользовательская программа не должна изменять его в любом случае. Любая попытка сделать это приводит к неопределенному поведению.
так что единственная разница в этом случае (использования со строковыми литералами) заключается в том, что второе объявление дает вам небольшое преимущество. Компиляторы обычно дают вам предупреждение, если вы попытаетесь изменить строковый литерал во втором случай.
выход:
cc1: предупреждения рассматриваются как ошибки
еда.c: в функции ‘ main’:
еда.c: 9: ошибка: передача аргумента 1 ‘strcpy’ отбрасывает квалификаторы из целевого типа указателя
обратите внимание, что компилятор предупреждает о втором случае, но не о первом.
Const char что это
В действительности, в соответствии со стандартом на языки C и C++ (правило чтения операторов «Read Declarations Right-to-Left«, т. е. надо читать всегда оператор справа налево), модификатор const влияет на элемент, слева от которого стоит const. Поэтому если использовать const перед типом, то это фактически ничего не значит, и используется просто для удобства запоминания.
К примеру, следующие два оператора совершенно эквивалентны:
Оба указателя УказательНаНеизменяемоеСодержимое1 и УказательНаНеизменяемоеСодержимое2 указывают на строку, являющуюся константой, и оба эти указателя можно модифицировать runtime в коде программы.
Если же Вы хотели получить от указателя другое, чтобы это был постоянный указатель, который нельзя модифицировать, то нужно поместить const после звездочки (тогда получится наоборот, строка может быть изменяемой, а сам указатель изменить нельзя):
Чтобы защитить от изменения как сам указатель, так и содержимое, на которое он указывает, то нужно использовать два ключевых слова const:
[Примеры ключевого слова const при использовании с указателями]
В этом простом примере если мы попробуем в коде присвоить значение переменной j, то это будет обнаружено на этапе компиляции программы, компилятор выдаст ошибку.
Указатель на константу. Когда ключевое слово const используется с указателями, то важно помнить, что реальный смысл определения зависит от положения ключевого слова const.
Указатель на константу указывает на данные, которые должны быть постоянными. Это просто означает, что мы не можем поменять данные, на которые ссылается указатель. Вот несколько примеров кода, иллюстрирущие то, что мы подразумеваем указателем на константу:
Постоянный указатель. В таком указателе находящийся в нем адрес является константой. Но сама переменная, на которую ссылается указатель может быть изменена, т. е. она может не являться константой. Вот несколько примеров:
Постоянный указатель на константу. Можно думать об этом случае как о комбинации двух примеров выше. Т. е. нельзя поменять ни сам адрес (значение указателя), ни данные, на которые он указывает. Пример:
[Ссылки]
Комментарии
Но позвольте, вы же говорите что const влияет только на содержимое слева от const, а сами используете const самым левым (т.е. не влияющим ни на что).
/*const int j = 10;
В этом простом примере если мы попробуем в коде присвоить значение переменной j, то это будет обнаружено на этапе компиляции программы, компилятор выдаст ошибку.» */
Строковые и символьные литералы (C++)
В C++ поддерживаются различные типы строк и символов, а также доступны различные способы выражения значений литералов каждого из этих типов. В исходном коде содержимое символьных и строковых литералов выражается с помощью кодировки. Универсальные имена символов и escape-символы позволяют представить любую строку, используя только основную кодировку исходного кода. Необработанные строковые литералы позволяют не использовать escape-символы и могут применяться для выражения всех типов строковых литералов. Можно также создавать std::string литералы без необходимости выполнения дополнительных действий по созданию или преобразованию.
Символьные литералы
Символьный литерал состоит из символьной константы. Он представляется символом, заключенным в одинарные кавычки. Существует пять типов символьных литералов:
Символьные литералы UTF-8 типа char ( char8_t в c++ 20), например u8’a’
Символ, используемый для символьного литерала, может быть любым символом, за исключением символов обратной косой черты ( \ ), одинарной кавычки ( ‘ ) или новой строки. Зарезервированные символы можно указывать с помощью escape-последовательности. Символы можно указывать с помощью универсальных имен символов, при условии что тип является достаточно крупным для размещения символа.
Кодирование
Символьные литералы кодируются по-разному в соответствии с их префиксом.
Символьный литерал, начинающийся с L префикса, является литералом расширенных символов. Значение литерала расширенных символов, содержащего один символ, escape-последовательность или универсальное имя символа, имеет значение, равное числовому значению его кодировки в наборе расширенных символов выполнения, если только символьный литерал не имеет представления в наборе расширенных символов выполнения, в этом случае значение определяется реализацией. Значение литерала расширенных символов, содержащего несколько символов, escape-последовательностями или универсальных имен символов, определяется реализацией. дополнительные MSVC см. в разделе, относящемся к корпорации майкрософт ниже.
Символьный литерал, начинающийся с u8 префикса, является символьным литералом UTF-8. Значение символьного литерала UTF-8, содержащего один символ, escape-последовательность или универсальное имя символа, имеет значение, равное значению его кодовой точки ISO 10646, если оно может быть представлено в одной единице кода UTF-8 (соответствующее элементам управления C0 и основному регистру символов латиницы). Если значение не может быть представлено одной единицей кода UTF-8, программа неправильно сформирована. Символьный литерал в кодировке UTF-8, содержащий более одного символа, escape-последовательности или универсального имени символа, имеет неправильный формат.
Символьный литерал, начинающийся с u префикса, является символьным литералом UTF-16. Значение символьного литерала UTF-16, содержащего один символ, escape-последовательность или универсальное имя символа, имеет значение, равное значению его кодовой точки ISO 10646, если оно может быть представлено одной единицей кода UTF-16 (соответствующей базовой многоязыковой плоскости). Если значение не может быть представлено одной единицей кода UTF-16, программа неправильно сформирована. Символьный литерал UTF-16, содержащий более одного символа, escape-последовательности или универсального имени символа, имеет неправильный формат.
Символьный литерал, начинающийся с U префикса, является символьным литералом UTF-32. Значение символьного литерала UTF-32, содержащего один символ, escape-последовательность или универсальное имя символа, имеет значение, равное значению кодовой точки ISO 10646. Символьный литерал в кодировке UTF-32, содержащий более одного символа, escape-последовательности или универсального имени символа, имеет неправильный формат.
Escape-последовательности
Существует три вида escape-последовательностей: простая, восьмеричная и шестнадцатеричная. Escape-последовательностями могут быть следующие значения:
Значение | Escape-последовательность |
---|---|
новая строка | \n |
обратная косая черта | \\ |
горизонтальная табуляция | \t |
вопросительный знак | ? или \? |
вертикальная табуляция | \v |
одинарная кавычка | \’ |
BACKSPACE | \b |
двойная кавычка | \» |
Возврат каретки | \r |
нуль-символ | \0 |
Смена страницы | \f |
восьмеричный | \ooo |
оповещение (колокольчик) | \a |
шестнадцатеричный | \xhhh |
В этом примере кода показаны некоторые примеры экранированных символов с помощью обычных символьных литералов. Один и тот же синтаксис escape-последовательности допустим для других типов символьных литералов.
Обратная косая черта ( \ ) — это символ продолжения строки, когда он помещается в конец строки. Если символ обратной косой черты требуется использовать как символьный литерал, необходимо ввести две косые черты подряд ( \\ ). Дополнительные сведения о символе продолжения строки см. в разделе Phases of Translation.
Специально для систем Майкрософт
Чтобы создать значение из короткого многосимвольного литерала, компилятор преобразует символ или последовательность символов между одинарными кавычками в 8-битные значения в пределах 32-разрядного целого числа. Несколько символов в литерале заполняют соответствующие байты по мере необходимости от высокого до низкого порядка. Затем компилятор преобразует целое число в целевой тип после обычных правил. Например, чтобы создать char значение, компилятор принимает байт нижнего порядка. Для создания значения wchar_t или char16_t компилятор принимает младшее слово. Компилятор выдает предупреждение о том, что результат усекается, если какие-либо биты заданы выше назначенного байта или слова.
Восьмеричная escape-последовательность, которая содержит более трех цифр, рассматривается как восьмеричная последовательность из 3 цифр, за которой следуют последующие цифры как символы в многосимвольном литерале, что может привести к неудивительному результату. Пример:
Escape-последовательности, которые содержат невосьмеричные символы, вычисляются в виде восьмеричной последовательности вплоть до последнего восьмеричного символа, за которыми следуют оставшиеся символы в виде последующих символов в многосимвольном литерале. Предупреждение C4125 создается, если первый невосьмеричный символ является десятичной цифрой. Пример:
Восьмеричная escape-последовательность, которая имеет большее значение, чем \377 Ошибка C2022: » \377 «: слишком большое для символа.
Escape-последовательность, которая содержит шестнадцатеричные и нешестнадцатеричные символы, вычисляется как многосимвольный литерал, содержащий шестнадцатеричную escape-последовательность вплоть до последнего шестнадцатеричного символа, за которыми следуют нешестнадцатеричные символы. Шестнадцатеричная escape-последовательность, которая не содержит шестнадцатеричных цифр, приводит к ошибке компилятора C2153: «шестнадцатеричные литералы должны содержать по крайней мере одну шестнадцатеричную цифру».
Если в расширенном символьном литерале с префиксом L содержится последовательность из множества символов, значение берется из первого символа, а компилятор выдает предупреждение C4066. Последующие символы игнорируются, в отличие от поведения эквивалентного обычного многосимвольного литерала.
универсальные имена символов
В символьных литералах и машинных (не являющихся необработанными) строковых литералах любой символ может быть представлен универсальным именем символа. Универсальные имена символов формируются с помощью префикса, \U за которым следует 8-значная кодовая точка Юникода или префикс, \u за которым следует 4-значная кодовая точка Юникода. Все восемь или четыре знака, соответственно, должны присутствовать для создания корректного универсального имени символа.
Суррогатные пары
В C++03 языком допускалось, чтобы универсальными именами символов представлялось лишь определенное подмножество символов. Также могли существовать универсальные имена символов, не представляющие никаких допустимых символов Юникода. Эта ошибка была исправлена в стандарте C++ 11. В C++11 в символьных и строковых литералах и идентификаторах можно использовать универсальные имена символов. Дополнительные сведения об универсальных именах символов см. в разделе Character Sets. Дополнительные сведения о Юникоде см. в статье Unicode. Дополнительные сведения о суррогатных парах см. в статье Surrogate Pairs and Supplementary Characters(Суррогатные пары и дополнительные символы).
Строковые литералы
Строковый литерал представляет последовательность символов, которые вместе образуют строку с завершающим нулем. Символы должны быть заключены в двойные кавычки. Существуют следующие типы строковых литералов.
Узкие строковые литералы
Строки в кодировке UTF-8
Широкие строковые литералы
char16_t и char32_t (C++11)
В C++11 доступны символьные типы char16_t (портативный, 16-разрядный Юникод) и char32_t (32-разрядный Юникод):
Необработанные строковые литералы (C++ 11)
Необработанный строковый литерал — это массив с завершающим нулем (любой символьный тип), содержащий любой графический символ, включая двойные кавычки ( « ), обратную косую черту ( \ ) или символ новой строки. Необработанные строковые литералы часто применяются в регулярных выражениях, которые используют классы символов, а также в строках HTML и XML. Примеры см. в следующей статье: Bjarne Stroustrup’s FAQ on C++11(Вопросы и ответы о C++11 от Бьерна Страуструпа).
Однако ошибку можно устранить с помощью разделителя:
Можно создать необработанный строковый литерал, содержащий символ новой строки (не экранированный символ) в источнике:
литералы std:: String (C++ 14)
s Суффикс можно также использовать для необработанных строковых литералов:
Размер строковых литералов
Обратите внимание, что strlen() и wcslen() не включайте размер завершающего нуль-символа, размер которого равен размеру элемента строкового типа: один байт в char* char8_t* строке или, два байта wchar_t* или char16_t* строки и четыре байта в char32_t* строках.
Максимальная длина строкового литерала составляет 65 535 байт. Это ограничение применимо как к узким, так и к расширенным строковым литералам.
Изменение строковых литералов
Поскольку строковые литералы (не включая std::string литералы) являются константами, попытка их изменить, например, str[2] = ‘A’ приводит к ошибке компилятора.
Специально для систем Майкрософт
В некоторых случаях идентичные строковые литералы могут быть объединены в пул для экономии места в исполняемом файле. При объединении строковых литералов в пулы компилятор делает так, что все ссылки на определенный строковый литерал указывают на одну и ту же область в памяти, вместо того чтобы каждая ссылка указывала на отдельный экземпляр строкового литерала. Чтобы включить объединение строк, используйте /GF параметр компилятора.
Сцепление смежных строковых литералов
Все смежные расширенные и узкие строковые литералы соединяются. Данное объявление:
идентично следующему объявлению:
и следующему объявлению:
Использование внедренных шестнадцатеричных escape-кодов для задания строковых литералов может привести к непредвиденным результатам. В следующем примере выполняется попытка создать строковый литерал, содержащий символ ASCII 5, за которым следуют символы f, i, v и e:
Фактический результат (шестнадцатеричное значение 5F) является кодом ASCII для символа подчеркивания, за которым следуют символы i, v и e. Чтобы получить правильный результат, можно использовать одну из следующих escape-последовательностей:
Строковые литералы с универсальными именами символов
Машинные (не являющиеся необработанными) строковые литералы могут использовать универсальные имена символов для представления любого символа, при условии что универсальные имена можно кодировать как один или несколько символов в строковом типе. Например, универсальное имя символа, представляющее расширенный символ, не может быть закодировано в виде короткой строки с помощью кодовой страницы ANSI, но может быть закодировано в виде узких строк в некоторых многобайтовых кодовых страницах или в строках UTF-8 или в расширенной строке. В C++ 11 Поддержка Юникода расширена с помощью char16_t* char32_t* строковых типов и, а c++ 20 расширяет его до char8_t типа:
Многоликий const
Ключевое слово const — одно из самых многозначных в C++. Правильно использование const позволяет организовать множество проверок ещё на этапе компиляции и избежать многих ошибок из числа тех, которые бывает трудно найти при помощи отладчиков и/или анализа кода.
Первая половина заметки рассчитана скорее на начинающих (надеюсь мнемоническое правило поможет вам запомнить, где и для чего используется const), но, возможно, и опытные программисты смогут почерпнуть интересную информацию о перегрузке методов по const.
Константы и данные
Самый простой случай — константные данные. Возможно несколько вариантов записи:
Все они правильные и делают одно и тоже — создают переменную, значение которой изменить нельзя.
Константы и указатели
При использовании const с указателями, действие модификатора может распространяться либо на значение указателя, либо на данные на которые указывает указатель.
Работает (const относится к данным):
Тоже самое и тоже работает:
А вот это уже не работает:
Если бы операция присвоения изменяла бы не указатель, а данные:
то ситуация была бы диаметрально противоположной.
Существует мнемоническое правило, позволяющее легко запомнить, к чему относится const. Надо провести черту через «*», если const слева, то оно относится к значению данных; если справа — к значению указателя.
Ну и конечно, const можно написать дважды:
Константы и аргументы/результаты функций
C функциями слово const используется по тем же правилам, что при описании обычных данных.
Константы и методы (перегрузка)
А вот с методами есть одна тонкость.
Во-первых, для методов допустимо использование const, применительно к this. Синтаксис таков:
Кроме того, этот const позволяет перегружать методы. Таким образом, вы можете писать оптимизированные варианты методов для константных объектов.
То есть для константного объекта (с x=2) был вызван соответствующий метод.
Осталось только добавить, что если вы планируете использовать const-объекты, то вам надо обязательно реализовать const-методы. Если вы в этом случае не реализуете не-const-методы, то во всех случаях будут молча использоваться const-методы. Одним словом, const лучше использовать там, где это возможно.
И ещё… я собрался в отпуск… возможно, не смогу ответить на комментарии до понедельника-вторника. Не сочтите за невнимание 🙂