Различия строковых литералов между C и C++

Насколько я могу судить, до C++11 строковые литералы обрабатывались почти точно так же, как между C и C++.

Теперь я признаю, что существуют различия между C и C++ в обработке широких строковых литералов.

Единственные отличия, которые мне удалось найти, заключаются в инициализации массива строковым литералом.

char str[3] = "abc"; /* OK in C but not in C++ */
char str[4] = "abc"; /* OK in C and in C++. Terminating zero at str[3] */

И техническое отличие, которое имеет значение только в C++. В C++ "abc" равно const char [4], а в C — char [4]. Однако в C++ есть специальное правило, разрешающее преобразование в const char *, а затем в char * для сохранения совместимости с C до C++11, когда это специальное правило больше не применяется.

И разница в допустимых длинах литералов. Однако на практике любой компилятор, который компилирует код как на C, так и на C++, не будет применять нижний предел C.

У меня есть несколько интересных ссылок, которые применимы:

Есть ли какие-либо другие различия?


person Zan Lynx    schedule 18.04.2014    source источник
comment
В контексте нашего другого обсуждения const char[N] против char[N] — огромная разница. Правило, запрещающее модификацию строковых литералов в C++, — это правило, запрещающее модификацию const объектов. Вы не найдете никаких особых случаев C++, таких как правило C, специально запрещающее запись в память, где хранятся строковые литералы.   -  person Ben Voigt    schedule 18.04.2014
comment
@BenVoigt: Это все, что у тебя есть? С моей точки зрения, специальное правило C, согласно которому char [4] на самом деле недоступно для записи, и правило C++, согласно которому const char [4] преобразуется в char*, но на самом деле недоступно для записи, имеют один и тот же результат и вообще не отличаются.   -  person Zan Lynx    schedule 18.04.2014
comment
Код char str[4] = "abc"; это не присваивание, это инициализация.   -  person Yu Hao    schedule 18.04.2014
comment
C и C++ используют одни и те же правила для строковых литералов, но C добавляет два дополнения для обратной совместимости: тип неявно распадается на char*, даже если объект является константным литералом, и можно инициализировать массив, который может содержать все, кроме терминатора, с помощью строковый литерал.   -  person Deduplicator    schedule 18.04.2014
comment
Как ни странно, в С++ есть такое же правило. 2.14.5п12. Но это избыточно с типом const.   -  person Ben Voigt    schedule 18.04.2014
comment
@YuHao: исправлено с помощью редактирования.   -  person Zan Lynx    schedule 18.04.2014
comment
@Deduplicator: в C99 литеральный тип — char [N], как сказал Зан в вопросе.   -  person Ben Voigt    schedule 18.04.2014
comment
В стандартном C++ строковые литералы являются константами и имеют тип const char[].   -  person FoggyDay    schedule 18.04.2014
comment
@BenVoigt: Как я уже сказал, постоянный литерал, тип которого не отражает константу. (Быть константным литералом и не иметь идентичности важно для постоянного объединения)   -  person Deduplicator    schedule 18.04.2014
comment
@Zan: Быть const в C ++ имеет гораздо больше эффектов, чем просто альтернативный способ указать правило C, что они не могут быть изменены. По крайней мере, я думаю, что это должно иметь значение для целочисленных константных выражений и удобства использования с constexpr инициализацией.   -  person Ben Voigt    schedule 18.04.2014
comment
C++(11) также предоставляет необработанные строковые литералы (но вы, вероятно, уже знали об этом).   -  person user657267    schedule 18.04.2014
comment
Также универсальные имена символов считаются escape-последовательностью в C, но в C++ они являются обычными c-chars. Педантично, но это все, что я смог найти.   -  person user657267    schedule 18.04.2014
comment
@BenVoigt: const действительно имеет значение в C ++, что, я думаю, я признаю, но между C и C ++ я не думаю, что это так, потому что, хотя терминология может немного отличаться, результаты точно такие же.   -  person Zan Lynx    schedule 18.04.2014
comment
У меня нет доступных стандартов C, но я сомневаюсь, что C может поддерживать пользовательские литералы. Вероятно, даже не литералы Unicode (префиксы u и U, я думаю, что C поддерживает префикс L).   -  person Cheers and hth. - Alf    schedule 18.04.2014
comment
Может быть неважно, но конкатенация строк для литералов широких строк в C++11 отличается от C. Например, L"Hello, " "world" недопустимо в C, но допустимо в C++.   -  person Mohit Jain    schedule 14.05.2014
comment
@Mohit Jain: Это не тот случай, который показан в ОП. Это недопустимо в C, потому что нет правила для автоматического преобразования строкового литерала в широкий строковый литерал, а не потому, что вы не можете конкатенировать.   -  person xryl669    schedule 21.05.2014
comment
Кроме того, константная часть означает, что массив байтов для строки находится в разделе данных только для чтения/разделяемых данных окончательного двоичного файла в обычной ОС. Если вы попытаетесь написать в эту часть, вы получите SIGBUS/SIGFAULT в системе Posix и нарушение прав доступа на платформе Win32.   -  person xryl669    schedule 21.05.2014
comment
Когда вы пишете char x[4] = abc; на самом деле вы делаете копию из массива const (только для чтения) в неконстантный массив (на основе стека). Это семантически эквивалентно memcpy(x, abc, 4);. Надеюсь, компилятор проверит размер во время компиляции и предотвратит переполнение.   -  person xryl669    schedule 21.05.2014
comment
Это больше похоже на вопрос об инициализации массивов char, чем на вопрос о строковых литералах.   -  person tenfour    schedule 21.05.2014
comment
@БенВойт. Да извини. Пропущенный. :D   -  person Shoe    schedule 22.05.2014


Ответы (2)


Необработанные строки

Заметным отличием является то, что строковые литералы C++ являются надмножеством строковых литералов C. В частности, C++ теперь поддерживает необработанные строки. (не поддерживается в C), технически определено в §2.14.15 и обычно используется в HTML и XML, где часто встречается ".

Необработанные строки позволяют указать собственный разделитель (до 16 символов) в виде:

R"delimiter(char sequence)delimiter"

Это особенно полезно, чтобы избежать ненужных экранирующих символов, предоставляя собственный разделитель строк. В следующих двух примерах показано, как избежать экранирования " и ( соответственно:

std::cout << R"(a"b"c")";      // empty delimiter
std::cout << '\n';
std::cout << R"aa(a("b"))aa";  // aa delimiter
// a"b"c"
// a("b")

Текущая демонстрация


char vs const char

Еще одно отличие, указанное в комментариях, заключается в том, что строковые литералы имеют тип char [n] в C, как указано в §6.4.5/6:

Для литералов символьных строк элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов.

в то время как в C++ они имеют тип const char [n], как определено в §2.14.5/8:

Обычные строковые литералы и строковые литералы UTF-8 также называются узкими строковыми литералами. Узкий строковый литерал имеет тип «массив из n const char», где n — размер строки, как определено ниже, и имеет статическую продолжительность хранения (3.7).

Это не меняет того факта, что в обоих стандартах (в §6.4.5/7 и 2.14.5/13 для C и C++ соответственно) попытка изменить строковый литерал приводит к неопределенному поведению.


Неуказанные и определенные реализации (ref)

Еще одно тонкое отличие заключается в том, что в C не указано, отличаются ли массивы символов строковых литералов, согласно §6.4.5/7:

Не указано, являются ли эти массивы различными, если их элементы имеют соответствующие значения.

в то время как в С++ это определяется реализацией в соответствии с §2.14.5/13:

Все ли строковые литералы различны (т. е. хранятся в непересекающихся объектах), определяется реализацией.

person Shoe    schedule 22.05.2014
comment
Не могли бы вы уточнить практическую разницу между неуказанным и определяемым реализацией? Конечно, есть, иначе не было бы двух разных формулировок... - person rodrigo; 22.05.2014
comment
@rodrigo, Техническая разница, если я правильно понимаю формулировку, заключается в том, что при undefined реализация может выбрать любую из возможностей и не обязана это документировать; в то время как при определении реализации требуется предоставить документацию относительно сделанного выбора. В любом случае, рядом с третьим заголовком есть ссылка на один из вопросов здесь, на SO, относительно этой разницы. :) - person Shoe; 22.05.2014

Лучший способ ответить на ваш вопрос — переписать его как программу, которая идентично компилируется при использовании компилятора «C» или «C++». Я предполагаю, что вы используете GCC, но другой (правильно написанный) компилятор Цепочки инструментов должны давать аналогичные результаты.

Сначала я рассмотрю каждый пункт, который вы изложили, затем я дам программу, которая дает ответ (и доказательство).

  • Насколько я могу судить, до C++11 строковые литералы обрабатывались почти точно так же, как между C и C++.

Их по-прежнему можно обрабатывать таким же образом, используя различные параметры командной строки, в этом примере я буду использовать «-fpermissive» (обман). Вам лучше выяснить, почему вы получаете предупреждения, и написать НОВЫЙ код, чтобы избежать ЛЮБОГО предупреждения; используйте только «читы» CLP для компиляции СТАРОГО кода.

Правильно пишите новый код (никаких читов и предупреждений, чтобы не было Ошибок).

  • Теперь я признаю, что существуют различия между C и C++ в обработке широких строковых литералов.

Там не должно быть (много различий), так как вы можете обмануть большинство или все из них в зависимости от обстоятельств. Обманывать нехорошо, учитесь программировать правильно и следуйте современным Стандартам, а не ошибкам (или неловкости) прошлого. Вещи делаются определенным образом, чтобы быть полезными как для вас, так и для компилятора в некоторых случаях (помните, что ВЫ не единственный, кто «видит» ваш код).

В этом случае компилятору требуется достаточно места, чтобы выделить строку с "0" (нулевым байтом). Это позволяет использовать функцию печати (и некоторые другие) без указания длины строки.

ЕСЛИ вы просто пытаетесь скомпилировать существующую Программу, полученную откуда-то, и не хотите ее переписывать, вы просто хотите ее скомпилировать и запустить, затем используйте читы (если необходимо), чтобы обойти предупреждения и принудительно компиляция в исполняемый файл.

  • Остальное, что вы написали...

No.

.

См. этот пример программы. Я немного изменил ваш вопрос, чтобы превратить его в программу. Результат компиляции этой Программы компилятором "C" или C++ идентичен.

Скопируйте и вставьте приведенный ниже пример текста программы в файл с именем «test.c», затем следуйте инструкциям в начале. Просто «пометьте» файл, чтобы вы могли прокрутить его назад (и увидеть его), не открывая текстовый редактор, затем скопируйте и вставьте каждую строку, начиная с команд компилятора (следующие три).

Обратите внимание, что, как указано в комментариях, выполнение этой строки "g++ -S -o test_c++.s test.c" приводит к ошибке (с использованием современного компилятора g++), поскольку длина контейнера недостаточна для хранения строки.

Вы должны уметь читать эту Программу, и на самом деле вам не нужно ее компилировать, чтобы увидеть Ответ, но она скомпилирует и выдаст вывод, который вы можете изучить, если захотите.

Как вы можете видеть, длина переменной «str1» недостаточна для хранения строки, когда она завершается нулем, что приводит к ошибке на современном (и правильно написанном) компиляторе g++.


/* Answer for: http://stackoverflow.com/questions/23145793/string-literal-differences-between-c-and-c
 *
 * cat test.c
 * gcc -S -o test_c.s test.c
 * g++ -S -o test_c++.s test.c
 * g++ -S -fpermissive -o test_c++.s test.c
 *
 */

char str1[3] = "1ab";
char str2[4] = "2ab";
char str3[]  = "3ab";

main(){return 0;}


/* Comment: Executing "g++ -S -o test_c++.s test.c" produces this Error:
 *
 * test.c:10:16: error: initializer-string for array of chars is too long [-fpermissive]
 * char str1[3] = "1ab";
 *                ^
 *
 */


/* Resulting Assembly Language Output */

/*      .file   "test.c"
 *      .globl  _str1
 *      .data
 * _str1:
 *      .ascii "1ab"
 *      .globl  _str2
 * _str2:
 *      .ascii "2ab\0"
 *      .globl  _str3
 * _str3:
 *      .ascii "3ab\0"
 *      .def    ___main;    .scl    2;  .type   32; .endef
 *      .text
 *      .globl  _main
 *      .def    _main;  .scl    2;  .type   32; .endef
 * _main:
 * LFB0:
 *      .cfi_startproc
 *      pushl   %ebp
 *      .cfi_def_cfa_offset 8
 *      .cfi_offset 5, -8
 *      movl    %esp, %ebp
 *      .cfi_def_cfa_register 5
 *      andl    $-16, %esp
 *      call    ___main
 *      movl    $0, %eax
 *      leave
 *      .cfi_restore 5
 *      .cfi_def_cfa 4, 4
 *      ret
 *      .cfi_endproc
 * LFE0:
 *      .ident  "GCC: (GNU) 4.8.2"
 *
 */
person Rob    schedule 22.05.2014
comment
Хорошее объяснение. Но, к сожалению, вы упускаете тему. - person dhein; 22.05.2014
comment
Я не согласен, я точно ответил на вопрос. - person Rob; 22.05.2014
comment
ОП запросил дополнительные отличия от них, заявил он. Вы просто объясняете, как он может доказать то, что он уже знает, как он сказал. Так что, если бы вы спросили меня, что отсутствует тема. - person dhein; 22.05.2014
comment
ОП ограничил объем своего вопроса строковыми литералами и не расширил свой вопрос до каждого возможного использования строки (IE: новые функции или изменения в старых функциях, которые заставляют строки обрабатываться по-разному ИЛИ требуют, чтобы литералы отличались от I описано). Поэтому я сказал нет, возможно, недостаточно буквально для вас, за исключением того, что я описал. Спасибо, что нашли время объяснить, почему вы не согласны с моим ответом. Раньше я давал более длинные ответы, но обнаружил, что они редактировались для краткости, поэтому с тех пор я старался избегать излишнего многословия. - person Rob; 24.05.2014