C крошечный, предварительно выделенный массив не переполняется - PullRequest
1 голос
/ 22 марта 2011

Я ожидал segfault с этим кодом:

char * foo (char my_ascii[10])
{
  strcpy (my_ascii, "0123456789");

  return my_ascii;
}

char bar[2];

printf("%s\n", foo (bar));

Поскольку bar резервирует массив из 2 символов в стеке, а foo () пытается записать 10 символов.Однако printf () записывает в стандартный вывод 10 символов, и ошибок не возникает.Почему это происходит?

Кроме того, если я изменю функцию foo () следующим образом:

char * foo (char my_ascii[1])
{
  strcpy (my_ascii, "0123456789");

  return my_ascii;
}

Поведение точно такое же: 10 символов копируются в my_ascii.Любое объяснение?

Большое спасибо заранее.

Ответы [ 5 ]

2 голосов
/ 22 марта 2011

Указание длины параметра массива, например

char * foo (char my_ascii[1]) ...

, не имеет значения, поскольку оно опущено (массив распадается в указатель внутри функции).

Более того, переполнение буфера составляет неопределенное поведение , что означает именно это: нет никаких гарантий, что программа потерпит крах.Он может вполне законно выглядеть так, как если бы не было проблем ... или генерировать segfault только по четвергам, когда полнолуние ... или молча удалять все записи из вашей БД.На самом деле, ничего.

1 голос
/ 22 марта 2011

Во-первых, эти определения абсолютно идентичны:

char *foo1(char arr[10]) { /* ... */ }
char *foo2(char arr[1]) { /* ... */ }
char *foo3(char arr[]) { /* ... */ }
char *foo4(char *arr) { /* ... */ }

Во-вторых, запись за пределы объекта равна Неопределенное поведение . Все может случиться! Если вам повезет, ваш тестовый запуск рухнет, и вы все исправите; если вам не повезет, ваш тестовый прогон сработает, так как вы ожидаете неудачу только тогда, когда продемонстрируете его клиенту (или своему боссу).

1 голос
/ 22 марта 2011

char * foo (char my_ascii[10]) и char * foo (char my_ascii[1]) эквивалентны char * foo (char *my_ascii)

Примечание: тип массива распадается в тип указателя (на первый элемент массива) при передаче в функцию.

Поскольку bar резервирует массив из 2 символов в стеке, а foo() пытается записать 10 символов.Однако printf() записывает в стандартный вывод 10 символов, и ошибок не возникает.Почему это происходит?

Это потому, что неопределенное поведение означает, что все может случиться.

Только для записи

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

ПРИМЕЧАНИЕ. Возможные неопределенные значения поведения варьируются от , полностью игнорируя ситуацию с непредсказуемыми результатами , до поведения во время перевода или выполнения программы документированным образом, характерным для среды (с выдачей или без нее).диагностического сообщения), чтобы прекратить перевод или выполнение (с выдачей диагностического сообщения).

0 голосов
/ 22 марта 2011

К сожалению, неопределенное поведение означает, что все может случиться, включая отсутствие симптомов ошибки. В этом случае вы перезаписали часть стека, но это ни на что не повлияло.

0 голосов
/ 22 марта 2011

Это правда, что bar зарезервировал 2 символа, и вы заполняете его на 8 символов больше, чем он может обработать.

Это не означает автоматическиошибка.

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

Это хороший пример неопределенное поведение .Undefined не означает, что WILL не удастся, это действительно означает, что поведение undefined ;это может сработать, может не получиться, обезьяны могут вылететь из порта USB ... все может случиться.В этом случае это на самом деле работает, но вы не можете полагаться на это поведение, потому что оно может измениться при следующем запуске программы.

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


Кстати: в вашем коде есть еще одна ошибка.
Вы описываете my_ascii как 10 символов, но пытаетесь скопировать в него 11 символов.
Не забывайте о NULL-терминаторе в конце строки!
Это означает, что строка "0123456789" на самом деле требует 11 символов памяти.

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