'\ 0' и printf () в C

21

Во вводном курсе C я узнал, что при хранении строки хранятся с нулевым символом \0в конце. Но что, если я хочу напечатать строку, скажем, printf("hello")хотя я обнаружил, что это не заканчивается \0следующим утверждением

printf("%d", printf("hello"));

Output: 5

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

Аджай Мишра
источник
1
Помимо того, что ваш код, по крайней мере, отсутствует );, что вы намереваетесь показать этим кодом? Как вы доказали, что это не заканчивается \0?
glglgl
И при чем тут память, в которой она хранится?
Цакироглу Фотис
В Си все литеральные строки на самом деле являются массивами символов, которые включают в себя нулевой терминатор.
Какой-то программист чувак
@glglgl Я думаю, printf () возвращает количество символов, которое должно быть напечатано на экране.
Аджай Мишра
4
@ AjayMishra Да, и он действительно должен был печатать 5 символов. Завершающий 0 байт не печатается на экране.
glglgl

Ответы:

13

Нулевой байт отмечает конец строки. Он не учитывается по длине строки и не печатается, когда строка печатается с помощью printf. По сути, нулевой байт сообщает функциям, которые выполняют манипуляции со строками, когда останавливаться.

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

char str[] = "hello";
printf("len=%zu\n", strlen(str));     // prints 5
printf("size=%zu\n", sizeof(str));    // prints 6
dbush
источник
Я думал, что история будет отличаться printf(). TBH Я понятия не имею, как printf()работает.
Аджай Мишра
8

printfвозвращает количество напечатанных символов. '\0'не печатается - это просто сигнал о том, что в этой строке больше нет символов. Также не учитывается длина строки

int main()
{
    char string[] = "hello";

    printf("szieof(string) = %zu, strlen(string) = %zu\n", sizeof(string), strlen(string));
}

https://godbolt.org/z/wYn33e

sizeof(string) = 6, strlen(string) = 5
P__J__
источник
6

Ваше предположение неверно. Ваша строка действительно заканчивается на \0.

Он содержит 5 символов h, e, l, l, oи символ 0.

«Внутренний» print()вывод выводит количество напечатанных символов, и это 5.

glglgl
источник
6

В Си все литеральные строки на самом деле являются массивами символов, которые включают в себя нулевой терминатор.

Однако нулевой терминатор не учитывается в длине строки (литеральной или нет) и не печатается. Печать останавливается, когда нулевой терминатор найден.

Какой-то программист чувак
источник
Есть ли способ проверить это без сохранения строки в массиве?
Аджай Мишра
1
@AjayMishra Ну, строка уже находится в массиве (как уже упоминалось) ... Но если бы не было нулевого терминатора, то printfвыходило бы за пределы строки, печатать «случайные» или «мусорные» символы и возвращать число отличается от длины строки. Если вы уже знаете длину строки, вы также можете проверить, соответствует ли символ в этом индексе '\0', что будет работать, но технически неопределенное поведение, если размер массива не включает терминатор (как char arr[5] = "hello";, например, который не будет добавлять терминатор в массив).
Какой-то программист чувак
@AjayMishra Да, например, вы можете char * p = "Hello"; int i = 0; while (p[i] != '\0') { printf("%d: %c", i, p[i]); i++; }посмотреть и посмотреть, как он работает: он показывает строки с индексами и содержимое в этой строке. После индекса 4 он находит символ 0 и прерывает цикл while. Там вы видите, что есть символ 0.
glglgl
6

Все ответы действительно хороши, но я хотел бы добавить еще один пример, чтобы завершить все эти

#include <stdio.h>

int main()
{
    char a_char_array[12] = "Hello world";

    printf("%s", a_char_array);
    printf("\n");

    a_char_array[4] = 0; //0 is ASCII for null terminator

    printf("%s", a_char_array);
    printf("\n");

    return 0;
}

Для тех, кто не хочет попробовать это на онлайн GDB, вывод:

Привет мир

Ад

https://linux.die.net/man/3/printf

Полезно ли понимать, что делает escape-терминатор? Это не граница для массива символов или строки. Это персонаж, который скажет парню, который разбирает -STOP, (печатает), разбирает, пока здесь.

PS: А если разбирать и печатать как массив символов

for(i=0; i<12; i++)
{
    printf("%c", a_char_array[i]);
}
printf("\n");

ты получаешь:

Мир ада

где, пробел после двойного l, является нулевым терминатором, однако, анализируя массив char, будет просто значение char каждого байта. Если вы выполните другой анализ и напечатаете значение int каждого байта ("% d%, char_array [i]), вы увидите, что (вы получаете представление ASCII code-int), пробел имеет значение 0.

Цакироглу Фотис
источник
4

В Cфункции printf()возвращает количество напечатанных символов, \0является nullтерминатором, который используется для обозначения конца строки на языке c, и встроенного stringтипа на данный момент не существует c++, однако размер вашего массива должен быть как минимум больше числа, которое charвы хотите хранить.

Вот ссылка: cpp ref printf ()

какой-то пользователь
источник
3

Но что, если я хочу напечатать строку, скажем printf ("привет"), хотя я обнаружил, что она не заканчивается на \ 0 следующим выражением

printf("%d", printf("hello"));

Output: 5

Вы неправы. Это утверждение не подтверждает, что строковый литерал "hello"не заканчивается завершающим нулевым символом '\0'. Это утверждение подтвердило, что функция printfвыводит элементы строки, пока не встретится завершающий нулевой символ.

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

Так на самом деле это выражение

printf("hello")

обрабатывается компилятором что-то вроде следующего

static char string_literal_hello[] = { 'h', 'e', 'l', 'l', 'o', '\0' };
printf( string_literal_hello );

Действие функции printf при этом можно представить следующим образом

int printf( const char *string_literal )
{
    int result = 0;

    for ( ; *string_literal != '\0'; ++string_literal )
    {    
        putchar( *string_literal );
        ++result;
    }

    return result;
}

Чтобы получить количество символов, хранящихся в строковом литерале "hello", вы можете запустить следующую программу

#include <stdio.h>

int main(void) 
{
    char literal[] = "hello";

    printf( "The size of the literal \"%s\" is %zu\n", literal, sizeof( literal ) );

    return 0;
}

Выход программы

The size of the literal "hello" is 6
Влад из Москвы
источник
0

Сначала вы должны очистить свою концепцию. Поскольку она будет очищена при работе с массивом, используемая вами команда печати просто подсчитывает символы, помещенные в парантез. В строке массива необходимо, чтобы она заканчивалась на \ 0

Мухаммед Кашиф
источник
0

Строка - это вектор символов. Содержит последовательность символов, образующих строку, за которой следует специальная конечная строка символов: '\ 0'

Пример: char str [10] = {'H', 'e', ​​'l', 'l', 'o', '\ 0'};

Пример: следующий символьный вектор не является одной строкой, потому что он не заканчивается на «\ 0»

char str [2] = {'h', 'e'};

Никола Брогелли
источник
Думаю, в Си нет векторов.
Аджай Мишра