Почему это намеренно неправильное использование strcpy не терпит неудачу?

Почему приведенный ниже код C, использующий strcpy отлично подходит для меня? Я попытался сделать это неудачно двумя способами:

1) Я попробовал strcpy из строкового литерала в выделенную память, которая была слишком мала, чтобы ее содержать. Он скопировал все это и не жаловался.

2) Я попробовал strcpy из массива, который не был NUL переименован. strcpy и printf работали отлично. Я думал, что strcpy скопировал char s до тех пор, пока не будет найден NUL , но ни один из них не присутствовал и все еще остановился.

Почему это не так? Я просто получаю «повезло» каким-то образом, или я не понимаю, как работает эта функция? Является ли это конкретным для моей платформы (OS X Lion), или работают ли большинство современных платформ?

 #include  #include  #include  int main() { char *src1 = "123456789"; char *dst1 = (char *)malloc( 5 ); char src2[5] = {'h','e','l','l','o'}; char *dst2 = (char *)malloc( 6 ); printf("src1: %s\n", src1); strcpy(dst1, src1); printf("dst1: %s\n", dst1); strcpy(dst2, src2); printf("src2: %s\n", src2); dst2[5] = '\0'; printf("dst2: %s\n", dst2); return 0; } 

Результатом запуска этого кода является:

 $ ./a.out src1: 123456789 dst1: 123456789 src2: hello dst2: hello 

Во-первых, копирование в массив, который слишком мал:

C не имеет защиты для прохождения границ массива, поэтому, если в dst1[5..9] нет ничего чувствительного, то вам повезет, и копия переходит в память, которую вы не правы, но она не сбой или. Однако эта память небезопасна, поскольку она не была назначена вашей переменной. Другая переменная может иметь выделенную для нее память и позже перезаписывать данные, которые вы там помещаете, и впоследствии разрушает вашу строку.

Во-вторых, копирование из массива, который не заканчивается на нуль:

Несмотря на то, что нас обычно учат, что память полна произвольных данных, огромные куски ее нулевые. Несмотря на то, что вы не поместили нулевой ограничитель в src2 , вероятность того, что src[5] окажется \0 любом случае. Это делает копию успешной. Обратите внимание, что это НЕ гарантировано и может быть сбой при любом запуске на любой платформе в любое время. Но на этот раз вам повезло (и, вероятно, большую часть времени), и это сработало.

Overwriting за пределами выделенной памяти вызывает Undefined Behavior .
Так что да, тебе повезло.

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

РЕДАКТИРОВАТЬ:
Во-вторых, я бы сказал, что вам действительно не повезло, что программа работает нормально и не падает. Он работает сейчас, это не значит, что он будет работать всегда, на самом деле это бомба, тихая, чтобы сдуться.

Согласно Закону Мерфи :
« Все, что может пойти не так, пойдет не так » [ «и, скорее всего, в самый неподходящий момент» ]

[ ] – Мое редактирование в законе 🙂

Да, вам просто повезло.

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

Вероятно, вы получаете NUL потому что память заполняется нулями (что не гарантируется).

Как сказал @Als, это неопределенное поведение . Это может привести к сбою, но это не обязательно .

Многие диспетчеры памяти выделяют в больших кусках памяти, а затем передают ее «пользователю» в более мелкие куски, возможно, это mutliple из 4 или 8 байтов. Таким образом, ваша запись поверх границы, возможно, просто записывается в дополнительные байты. Или он перезаписывает одну из других переменных, которые у вас есть.

Вы не располагаете достаточным количеством байтов. Первая строка, "123456789" составляет 10 байтов (присутствует нулевой ограничитель), а {'h','e','l','l','o'} – 6 байт (опять же, место для нулевой ограничитель). В настоящее время вы клонируете память этим кодом, что приводит к неопределенному (т.е. нечетному) поведению.

Давайте будем гением компьютера.