Почему мой C компилятор не предупреждает, когда я присваиваю строковый литерал неконстантному указателю? - PullRequest
3 голосов
/ 07 февраля 2020

Следующий код прекрасно компилируется, например, с настройками по умолчанию в Xcode 11.3.1:

#include <stdio.h>

int main(int argc, const char * argv[]) {
    char* thing = "123";
    thing[2] = '4';
    printf("%s\n", thing);
    return 0;
}

Однако во время выполнения код прерывается с EXC_BAD_ACCESS на thing[2] = '4'. Я предполагаю, что это потому, что память для байтов, представляющих "123", скомпилирована в двоичный файл моей программы, который на современном процессоре / ОС помечается как для кода , а не для данных . ( Этот ответ подтверждает, что - не говоря уже о том, что в разборке есть строка leaq 0x4d(%rip), %rsi ; "123", передающая указатель на адрес относительно указателя инструкции *1012*!)

Это просто исторический артефакт, который C допускает это, начиная с эпохи самоизменяющегося кода? Я замечаю, что могу также назначить void* x = main; без каких-либо жалоб на то, что я отменяю модификаторы.

В этом ответе говорится:

Согласно обоснованию C99 В комитете были люди, которые хотели, чтобы строковые литералы были модифицируемыми, поэтому стандарт прямо не запрещает это.

Можно ли еще что-нибудь обсудить по этому поводу? С практической точки зрения, есть ли способ сказать clang и / или g cc to пометить такие назначения (даже если они на самом деле не запрещены) с предупреждением , без компиляции как C ++

Ответы [ 3 ]

2 голосов
/ 07 февраля 2020

Ответ, который вы цитировали, - это мнение без цитирования и откровенно бессмысленный ответ Это ни что иное, как не сломать огромное количество существующего унаследованного C кода, который желательно оставить компилируемым в современном компиляторе.

Однако многие компиляторы будут выдавать предупреждение, если Вы устанавливаете необходимый уровень предупреждения или параметры. В G CC, например:

-Wwrite-strings

При компиляции C присвойте строковым константам тип const char[length], чтобы копировать адрес одного в неконстантный char* указатель выдает предупреждение. Эти предупреждения помогут вам найти во время компиляции код, который может попытаться записать в строковую константу, но только если вы очень внимательно относились к использованию const в объявлениях и прототипах. В противном случае это просто неприятность. Вот почему мы не заставляли -Wall запрашивать эти предупреждения.

При компиляции C ++ предупреждайте об устаревшем преобразовании строковых литералов в char *. Это предупреждение по умолчанию включено для программ на C ++.

CLANG также имеет -Wwrite-strings, где является синонимом -Wwriteable-strings

-Wwritable-strings

Эта диагностика c включена по умолчанию.

Также контролирует -Wdeprecated-writable-strings.

Диагностика c текст:

warning: ISO C++11 does not allow conversion from string literal to A

Диагностический текст c отличается для C компиляции - я просто цитирую руководство.

В G CC с -Wwrite-strings:

int main()
{
    char* x = "hello" ;
    return 0;
}

производит:

main.c:3:15: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]    

CLANG производит:

source_file.c:3:15: warning: initializing 'char *' with an expression of type 'const char [6]' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
1 голос
/ 07 февраля 2020

У вас есть -Wwrite-strings:

При компиляции C присвойте строковым константам тип const char [length], чтобы копировать адрес единицы в неконстантный указатель char * выдает предупреждение. Эти предупреждения помогут вам найти во время компиляции код, который может попытаться записать в строковую константу, но только если вы очень внимательно относились к использованию const в объявлениях и прототипах. В противном случае это просто неприятность. Вот почему мы не сделали - все запросят эти предупреждения.

https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Warning-Options.html

1 голос
/ 07 февраля 2020

В противоположность C ++ В C строковые литералы имеют типы неконстантных символьных массивов.

Однако согласно стандарту C любая попытка изменить строковый литерал приводит к неопределенному поведению.

Исторически в языке C не было квалификатора const. Квалификатор const сначала появился в C ++. Так что для строковых литералов обратной совместимости в C есть типы массивов непостоянных символов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...