Почему gcc допускает инициализацию массива char со строковым литералом, превышающим массив?

int main()
{
char a[7] = "Network";
return 0;
}

строковый литерал в C заканчивается внутренне нуль персонаж. Таким образом, приведенный выше код должен выдавать ошибку компиляции, так как фактическая длина строкового литерала Network 8, и это не может вписаться в char[7] массив.

Тем не мение, НКУ (даже с -Wall) в Ubuntu компилирует этот код без каких-либо ошибок или предупреждений.
Почему gcc допускает это и не помечает это как ошибку компиляции?

gcc выдает предупреждение (все еще без ошибок!), когда размер массива char меньше строкового литерала. Например, он предупреждает:

char a[6] = "Network";
[Связанные с] Visual C ++ 2012 выдает ошибку компиляции для char a[7]:

1>d:\main.cpp(3): error C2117: 'a' : array bounds overflow
1> d:\main.cpp(3) : see declaration of 'a'

16

Решение

Инициализация массива char строковым литералом, который больше, чем в C, но неправильн в C ++. Это объясняет разницу в поведении между gcc и VC ++.

Вы не получите ошибки, если вы скомпилируете то же самое, что и файл C с VC ++. И вы получите ошибку, если скомпилируете ее как файл C ++ с g ++.

Стандарт C гласит:

Массив символьного типа может быть инициализирован символьной строкой
литерал или строковый литерал UTF-8, необязательно заключенный в фигурные скобки.
Последовательные байты строкового литерала (включая завершающий ноль)
символ, если есть место или если массив имеет неизвестный размер)
инициализировать элементы массива.

[…]

Пример 8

Декларация

char s[] = "abc", t[3] = "abc";

определяет «простые» объекты массива символов s а также t чьи элементы инициализированы
с символьными строковыми литералами.
Эта декларация идентична

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

(Раздел 6.7.9 Проект стандарта C11, Фактическая формулировка в окончательном стандарте может отличаться.)

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

Напротив, стандарт C ++ гласит:

Инициализаторов не должно быть больше, чем элементов массива.

Пример:

 char cv[4] = "asdf"; // error

плохо сформирован, так как нет места для подразумеваемого трейлинга ‘\ 0’.

(8.5.2 проекта C ++ 2011 n3242.)

25

Другие решения

Предпочтительный способ объявления строкового литерала обычно:

   char a[] = "Network";
printf("size of a: %d\n", sizeof a); // The compiler 'knows' the size of a.
// this prints '8'

Позвольте компилятору понять это. Трудно вручную указать размер массива и синхронизировать его с фактической длиной строкового литерала …

Так что я думаю, что GCC на самом деле не беспокоится ни о чем, кроме предупреждения.

3

В ранние времена C и Unix память и диск были маленькими, поэтому использование метода NUL для хранения байта NUL в конце строки не было. Если строковая переменная имеет длину семь символов, вы можете хранить в ней строку из семи символов, а поскольку длина семерки была максимальной, вы знали, что строка заканчивается там, даже без символа-терминатора. Вот почему strncpy работает так, как работает.

3

В то время как ответ раскрутки объясняет почему gcc не предупреждает об этом, не говорит, что вы можете с этим поделать.

gcc«s -Wc++-compat Опция Warning обнаружит эту конкретную проблему с сообщением:

foo.c: In function ‘main’:
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat]

Это единственный вариант, который приведет к gcc предупредить об этой проблеме. Вы можете написать короткий сценарий, чтобы быстро вывести варианты предупреждений из gccman man page, попробуйте скомпилировать каждую и посмотреть, жалуется ли она.

$ time for F in $(man gcc | grep -o -- '-W[^= ]*')
do if gcc -c "${F}" foo.c |& grep :3 >& /dev/null; then
echo "${F}"; gcc -c "${F}" foo.c
fi
done
man gcc | grep -o -- '-W[^= ]*')
man gcc | grep -o -- '-W[^= ]*'
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wc++-compat
foo.c: In function ‘main’:
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused-variable
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused-variable
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused-variable
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]
-Wc++-compat
foo.c: In function ‘main’:
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]

real    0m26.399s
user    0m5.128s
sys 0m15.329s

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

foo.c:3:17: String literal with 8 characters is assigned to char [7] (no room
for null terminator): "Network"A string literal is assigned to a char array that is not big enough to hold
the null terminator. (Use -stringliteralnoroom to inhibit warning)
foo.c:3:10: Variable a declared but not used
2
По вопросам рекламы [email protected]