Почему компилятор не обнаруживает за пределы в инициализации строковой константы?

class A {
    public void f(String s) {...}
    public void f(Integer i) {...}
}

class B extends A {
    public void f(Object o) {...} // Which A.f should this override?
}
8
задан Bill the Lizard 19 September 2012 в 01:59
поделиться

6 ответов

В стандарте C ++ для символьных массивов 8.5.2 / 2 указано:

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

В стандарте C99: 6.7.8 / 2 Инициализация говорит:

Ни один инициализатор не должен пытаться предоставить значение для объекта, не содержащегося в сущности. инициализируется

C90 6.5.7 Инициализаторы говорят о том же.

Однако обратите внимание, что для C (как C90, так и C99) завершающий символ '\ 0' будет помещен в массив , если есть место . Это не ошибка, если терминатор не подходит (C99 6.7.8 / 14: «Последовательные символы литерала символьной строки (включая завершающий нулевой символ, если есть место или если массив неизвестного размера) инициализируют элементы массив ").

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

в любом случае это должно быть диагностировано как ошибка во всех компиляторах:

char str[5] = "fast enough";

] Возможно, компиляторы до ANSI не были такими строгими, но любой достаточно современный компилятор должен это диагностировать.

6
ответ дан 5 December 2019 в 12:59
поделиться

Ответ на процитированный вами вопрос неверен. Правильный ответ: «Нет. Код не будет компилироваться» , если предположить, что компилятор C формально правильный (в отличие от особенностей какого-то конкретного компилятора).

Язык C не позволяет использовать слишком длинные строки литерал для инициализации массива символов определенного размера. Единственная гибкость, допускаемая здесь языком, - это завершающий символ \ 0 . Если массив слишком короткий для размещения завершающего \ 0 , завершающий \ 0 отбрасывается без уведомления. Но фактические буквенные строковые символы нельзя отбросить. Если литерал слишком длинный, это нарушение ограничения, и компилятор должен выдать диагностическое сообщение.

char s1[5] = "abc"; /* OK */
char s2[5] = "abcd"; /* OK */
char s3[5] = "abcde"; /* OK, zero at the end is dropped (ERROR in C++) */
char s4[5] = "abcdef"; /* ERROR, initializer is too long (ERROR in C++ as well) */

Кто бы ни написал вашу "книгу" действительно знал, о чем они говорили (по крайней мере, по этой конкретной теме). То, что они заявляют в ответе, совершенно неверно.

Примечание: Использование слишком длинных инициализаторов строк запрещено в C89 / 90, C99 и C ++. Однако C ++ в этом отношении еще более ограничен. C ++ запрещает отбрасывать завершающий символ \ 0 , а C позволяет отбрасывать его, как описано выше.

2
ответ дан 5 December 2019 в 12:59
поделиться

Ваша книга должна быть довольно старой, потому что gcc выдает предупреждение даже без -Wall включен:

$ gcc c.c
c.c: In function `main':
c.c:6: warning: initializer-string for array of chars is too long

Если мы немного обновим программу:

#include <stdio.h>

int main(int argc, char **argv)
{

        char str[5] = "1234567890";
        printf("%s\n", str);
        return 0;
}

Мы видно, что gcc обрезает строку до указанной вами длины; Я предполагаю , что там происходит как '\ 0' , где str [6] будет, потому что в противном случае мы должны видеть мусор после 5; но, возможно, gcc неявно делает str массивом длины 6 и автоматически вставляет туда '\ 0' - я не уверен.

$ gcc c.c && ./a.exe
c.c: In function `main':
c.c:6: warning: initializer-string for array of chars is too long
12345
5
ответ дан 5 December 2019 в 12:59
поделиться

Потому что «достаточно быстро» просто указатель на строку с завершающим нулем. Компилятору слишком сложно определить, выйдет ли когда-либо присваивание char * или char [] за пределы массива.

0
ответ дан 5 December 2019 в 12:59
поделиться

Проверка привязки к массиву происходит во время выполнения, а не во время компиляции. Компилятор не имеет возможности выполнить статический анализ приведенного выше кода, который был бы необходим для предотвращения ошибки.

ОБНОВЛЕНИЕ: По-видимому, приведенное выше утверждение верно для некоторых компиляторов, а не для других. Если в вашей книге написано, что он будет компилироваться, значит, это относится к компилятору, который не выполняет проверку.

0
ответ дан 5 December 2019 в 12:59
поделиться

Что происходит? Вы пытаетесь инициализировать символьный массив с большим количеством символов, чем в массиве есть место. Вот как это работает:

char str[5];

Объявляет символьный массив из пяти символов.

char str[5] = "fast enough";

Вторая часть '= "достаточно быстро";' затем пытается инициализировать этот массив значением «достаточно быстро». Это не сработает, потому что «достаточно быстро» длиннее, чем массив.

Однако он будет компилироваться. Компиляторы C и C ++ обычно не могут выполнять проверку границ массивов за вас, и переполнение массива является одной из наиболее распространенных причин ошибок сегментации. [edit] Как заметил Марк Рушаков, очевидно, что в некоторых случаях более новые версии выдают предупреждения. [/ edit] Это может привести к сбою при попытке запустить его, скорее я думаю, что массив просто инициализируется "быстро".

0
ответ дан 5 December 2019 в 12:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: