Почему длина строки должна быть плюс одна ее емкость в C?

Длина строки должна быть больше, чем максимальное количество символов, которое вы хотите удерживать. Достаточно логично: строки заканчиваются символом NULL.

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

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


Чтобы убедиться, что я прав, см. Этот фрагмент:

char str[9];
scanf("%s", str);
printf("%d", strlen(str));

Сделайте это полноценной программой и запустите ее. Тип 123456789, гарантированный 9-символьный длинный текст и посмотреть результаты. Он может содержать строку и, конечно же, длину строки - 9.


Я даже был свидетелем многих экспертов-программистов, говорящих, что размер строки должен быть плюс одна его емкость. Этот совет в значительной степени является мифом, или я где-то ошибаюсь?

РЕДАКТИРОВАТЬ

Скажем, я хочу создать целочисленный массив Arr который может содержать x количество элементов. Значение индекса элемента Arr last будет меньше, чем x поскольку значения индекса начинаются с 0 а не 1. Таким образом, его длина равна x-1.

Как бы вы это заявили? Я бы сделал так: int Arr[x-1]; , Я не думаю, что с этим возникают проблемы.

Теперь, если Arr был массивом типа char (т. Arr), длина Arr была бы больше, чем длина его Arr int поскольку в конце он имеет дополнительный символ NULL. Это будет (x-1)+1=x как: (x-1)+1=x.

Код для демонстрации этого

Итак, почему декларация на этот раз должна быть char Arr[x+1] а не просто char Arr[x]?

-8
источник поделиться
7 ответов

Вы правы в индексировании. Однако:

char str[9];

Когда вы объявляете строку таким образом, число 9 является длиной массива. Минус NULL может содержать только 8 символов, а не 9. Длина массива - это количество элементов в массиве, а не максимальное значение индекса, как вы думаете. Вы смешиваете эти термины.

Почему ваша программа работает, уже объясняется многими другими ответами и даже комментариями.

+3
источник

Согласно стандарту C относительно описания спецификатора преобразования s (7.21.6.2 Функция fscanf)

s Соответствует последовательности символов небелого пробела.279) Если нет модификатора длины l, соответствующий аргумент должен быть указателем на исходный элемент массива символов, достаточно большим, чтобы принять последовательность и завершающий нулевой символ, который будет добавляться автоматически.

Поэтому, если ввести последовательность символов 123456789 тогда будет предпринята попытка написать следующие символы:

{ '1', '2', '3', '4', '5', '6', '7', '8', '9', '\0' }'

в объявленном массиве

char str[9];

Как видно, последовательность содержит 10 символов, в то время как массив может вместить только 9 символов. Таким образом, память за пределами массива будет перезаписана, и в результате программа имеет неопределенное поведение.

В C, противоположном C++, вы можете инициализировать массив символов следующим образом

char str[3] = "Bye";

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

{ 'B', 'y', 'e' }

Однако вы не можете применять стандартную функцию C strlen к этому массиву, потому что функция подсчитывает символы до тех пор, пока встречный нуль не встретится, и массив не имеет такого символа.

Вы должны отличить значение, возвращаемое оператором sizeof и значение, возвращаемое стандартной функцией C strlen.

Например, если у вас есть такая декларация

char str[10] = "Hello";

то sizeof operator sizeof( str ) возвращает 10, то есть массив имеет 10 элементов размером равным 1 (sizeof( char) всегда равен 1).

Однако, если вы примените стандартную функцию C strlen возвращаемое значение будет равно 5, потому что функция подсчитывает все символы до нулевого окончания.

Вы можете написать, например

str[8] = 'A';

Тем не менее, если ypu применяет функцию strlen вы снова получите значение 5, потому что перед элементом str[8] со значением 'A' есть нулевой конец.

+4
источник

Индексирование любого типа массива, будь то int или char, начинается с 0.

Да, это правда.

Поэтому все размеры массива на единицу меньше их числовых значений.

Нет. Первое значение, используемое для индексации, влияет только на индексирование, а не на размер. Например, массив 1-го размера имеет только один индекс, 0. Это максимальное значение индекса, которое меньше размера, а не наоборот.

В объявлении char str[9]; значение 9 - это размер массива, а не максимальное значение индекса.

Причина, по которой ваш пример работает, заключается в том, что неопределенное поведение не должно приводить к сбою или сообщению об ошибке.

+2
источник

Вы правы, что индекс массива начинается с 0, но char str [9] имеет длину 9, поэтому самый высокий индекс - 8. Ваш пример, похоже, работает, но он может легко создать ошибку. Вы также можете ввести 1234567890 в свой код, и он будет выводить 10, потому что программа не может знать длину массива.

Когда вы определяете этот массив символов, вы создаете для него 9-байтовое пространство в стеке, но когда вы передаете его в scanf, char [] преобразуется в char * указатель на первый элемент в массиве. Поэтому scanf не может знать длину массива и записывает ввод в память, начиная с места, где str указывает. Он записывает символ \0 вне пространства, зарезервированного для массива! Но снова, передавая его в strlen, он не может видеть размер массива и продолжает сканировать память для \0, которую он находит после 10 байтов, поэтому он принимает длину 10.

Подобно тому, как @Ajay Brahmakshatriya показал в своем ответе, это может привести к ошибкам, поскольку пространство за пределами строки может использоваться для другой переменной, например, другой строки, которая затем может записывать разные данные в байты, где был \0.

+1
источник

Смотрите это → Ideone

int main(void) {
    char a[16];
    char b[16];
    scanf("%s",a);
    b[0]='a';
    b[1]='\0';
    printf("%s %d %p %p", a, strlen(a), a, b);
    return 0;  
}

Это почти копия кода, который вы показали. Для данного ввода 16 длин (размер массива также 16), длина печати равна 17.

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

Вы создали массив am из размера 9 (выделено 9 байтов). Затем вы сохранили в нем 9 байтов данных и завершили его '\0' который написал на десятом байте. Поскольку это пространство не использовалось ничем (к счастью) важным, данные подходят.

Затем, когда вы назвали strlen, это дало вам 9.

Теперь я сделал массив из 16 байтов и последовал за ним с другим массивом, который помещается после него. Теперь, когда он считывает 16 байтов и завершает его с помощью '\0' он записывается в b.

Я перезаписал его снова, написав b. Таким образом, '\0' написанный scanf исчез.

Тогда strlen при подсчете длины, переполненной в b, и остановился, когда увидел '\0' в b[1].

Все это, конечно, Неопределенное Поведение.

0
источник

... Итак, длина строки совпадает с количеством символов в ней.

Это утверждение верно, если мы видим, что завершающий нуль не является символом. Однако хранилище, необходимое для хранения строки, больше, чем количество символов в ней. (Акцент на "строка" заключается в том, что для строки в качестве типа данных требуется дополнительный завершающий нуль, который требует хранения.)

0
источник

Попытка доказать свою точку зрения:

Код

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

int main()
{
    char str[23];
    scanf("%s", str);
    printf("String length = %d\n", strlen(str));
    printf("String element  ---  Index number");
    int index=0;

    while (str[i]!='\0')
    {
        printf("\n%c  ---  %d", str[i], index);
        i++;
    }

    printf("\nNULL  ===  %d", index);

    return 0;
}

Пример ввода

graphing

Образец вывода

String length = 8
String element  ---  Index number
g  ---  0
r  ---  1
a  ---  2
p  ---  3
h  ---  4
i  ---  5
n  ---  6
g  ---  7
NULL  ===  8
-1
источник

Посмотрите другие вопросы по меткам или Задайте вопрос