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'
Инициализация массива 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.)
Предпочтительный способ объявления строкового литерала обычно:
char a[] = "Network";
printf("size of a: %d\n", sizeof a); // The compiler 'knows' the size of a.
// this prints '8'
Позвольте компилятору понять это. Трудно вручную указать размер массива и синхронизировать его с фактической длиной строкового литерала …
Так что я думаю, что GCC на самом деле не беспокоится ни о чем, кроме предупреждения.
В ранние времена C и Unix память и диск были маленькими, поэтому использование метода NUL для хранения байта NUL в конце строки не было. Если строковая переменная имеет длину семь символов, вы можете хранить в ней строку из семи символов, а поскольку длина семерки была максимальной, вы знали, что строка заканчивается там, даже без символа-терминатора. Вот почему strncpy работает так, как работает.
В то время как ответ раскрутки объясняет почему 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
предупредить об этой проблеме. Вы можете написать короткий сценарий, чтобы быстро вывести варианты предупреждений из gcc
man 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