Хорошо, давайте рассмотрим более простую программу, без какого-либо неопределенного поведения.Это печатает 5 .
#include <stdio.h>
#include <string.h>
int main(void) {
char string[6] = "hello";
printf("%zu\n", strlen(string));
}
И эта программа имеет неопределенное поведение
#include <stdio.h>
#include <string.h>
int main(void) {
char string[5] = "hello";
printf("%zu\n", strlen(string));
}
, поскольку нулевой терминатор не вписывается в string
и strlen
требует, чтобы ввод был нулевым. C11 7.1.1
[...] Строка - это непрерывная последовательность символов, оканчивающаяся первым нулевым символом и включающая его. [...]
и
C11 7.24.6.3 Функция strlen :
Описание
2Функция strlen
вычисляет длину строки, на которую указывает s.
Возвращает
3 Функция strlen
возвращает количество символов, предшествующих завершающему нулюсимвол.
Какая разница в практике здесь?Когда я компилирую с -S -O3
для генерации сборки, в первом случае номер 5 был жестко закодирован вместо вызова strlen
.Во втором случае оптимизатор действительно понял, что он не знает, какой будет длина строки, и что для этого ему нужно вызвать strlen
.Однако вместо того, чтобы предоставить строку в памяти, он создал ее следующим образом:
movl $1819043176, 2(%rsp)
Т.е. переместил 64-битное постоянное значение 0x6c6c6568
в стек ... который имеет младший порядок для hello\0\0\0
.И код снова печатает 5. Однако компилятор мог диагностировать это и отказаться вообще от компиляции вашей программы, поскольку она бессмысленна, имея неопределенное поведение.
Угадайте, что говорит программа ниже при выполнении, когда она скомпилирована с gcc -O3 -Werror
?
#include <stdio.h>
#include <string.h>
int main(){
char string[5];
strcpy(string, "Hello world!!!");
printf("%zu\n", strlen(string));
}
Ничего!Потому что он не компилируется, а выплевывает
In file included from /usr/include/string.h:494:0,
from strcpyexample.c:2:
In function ‘strcpy’,
inlined from ‘main’ at strcpyexample.c:6:5:
/usr/include/x86_64-linux-gnu/bits/string_fortified.h:90:10: error: ‘__builtin___memcpy_chk’ writing 15 bytes into a region of size 5 overflows the destination [-Werror=stringop-overflow=]
return __builtin___strcpy_chk (__dest, __src, __bos (__dest));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors