Каковы последствия не использования нулевого завершения в строках? - PullRequest
0 голосов
/ 09 октября 2018

Возьмем, к примеру, следующий код:

int main(){
    char string[] = { 'h' , 'e', 'l', 'l', 'o' };
    printf(string);
    printf("\n%d", strlen(string));
}

Вывод будет:

hello
6

Итак, с самого начала я вижу, что значение strlen отключено на1, но это не кажется таким препятствием, если мы учтем это.

Когда не завершение строки создает проблему?

Ответы [ 3 ]

0 голосов
/ 09 октября 2018

По определению в C строка должна заканчиваться на null termination character, в противном случае это не строка.

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

Так что вам может повезти, и у вас будет нулевой символ завершения, потому что в памяти уже есть символ, или вы можетев конечном итоге вывести на консоль тарабарщину и даже потерпеть крах (если вы начнете получать доступ к запрещенному месту, прежде чем найдете termination character).

Например:

#include <stdio.h>
#include <string.h>

int main(){
    char string[5];

    memset(string, 'z', 6);

    string[0] = 'h';
    string[1] = 'e';
    string[2] = 'l';
    string[3] = 'l';
    string[4] = 'o';

    printf("%s\n", string);
    printf("%d\n", (int)strlen(string));
}

В коде, который я установилсимвол после памяти, выделенной для массива символу z.Если вы запустите это, то в большинстве случаев произойдет сбой, поскольку printf начинает печатать до тех пор, пока не будет найден нулевой символ завершения (а после hello будет z и нет завершения!).

0 голосов
/ 09 октября 2018

Хорошо, давайте рассмотрим более простую программу, без какого-либо неопределенного поведения.Это печатает 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
0 голосов
/ 09 октября 2018

Каковы последствия не использования нулевого завершения в строках?

Технически нет, потому что строки C - по определению - оканчиваются нулевым символом:

Строка - это непрерывная последовательность символов, оканчивающаяся и включающая первый нулевой символ.

Поэтому, если она не завершена должным образом, это не строка.

Если вы передадите строку non в код, который ожидает строку, вы вызовете неопределенное поведение.

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