'\ 0' і printf () в C


21

У вступному курсі C я дізнався, що під час зберігання рядків зберігаються з нульовим символом \0в кінці. Але що робити, якщо я хотів надрукувати рядок, скажіть, printf("hello")хоча я виявив, що це не закінчується \0наступним твердженням

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

Output: 5

але це здається непослідовним, наскільки я знаю, що такі змінні, як рядки, зберігаються в основній пам'яті, і я припускаю, що друкуючи щось, воно також може зберігатися в основній пам'яті, то чому різниця?


1
Окрім того, що у вашому коді принаймні відсутній );, що ви маєте намір показати з цим кодом? Як ви довели, що це не закінчується \0?
glglgl

І що пам'ять, в якій вона зберігається, пов'язана з цим?
Цакіроглоу Фотіс

У C всі буквальні рядки - це дійсно масиви символів, до яких входить нульовий термінатор.
Якийсь програміст чувак

@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

Я думав, що історія буде інакше 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

6

Ваше припущення неправильне. Ваш рядок дійсно закінчується символом a \0.

Він містить 5 символів h, e, l, l, oі символ 0.

"Внутрішній" print()вихідний дзвінок - це кількість друкованих символів, і це 5.


6

У C всі буквальні рядки - це дійсно масиви символів, до яких входить нульовий термінатор.

Однак нульовий термінатор не враховується в довжину рядка (буквально чи ні) і не друкується. Друк припиняється, коли знайдено нульовий термінатор.


Чи існує можливість перевірити це, не зберігаючи рядок у масиві?
Аджай Мішра

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

Чи корисно це зрозуміти, що робить термінатор втечі? Це не межа для масиву char або рядка. Це персонаж, який скаже хлопцеві, який розбирає -STOP, (print) розбирає дотепер.

PS: А якщо розібратися і роздрукувати його як масив char

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

Ви отримуєте:

Пекло світу

де пробіл після подвійного l є нульовим термінатором, однак аналіз масиву знаків буде просто значущим значенням кожного байта. Якщо ви зробите інший синтаксичний розбір і друкуєте значення int кожного байта ("% d%, char_array [i]), ви побачите, що (ви отримуєте представлення коду ASCII) пробіл має значення 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;
}

Щоб отримати кількість символів, що зберігаються в прямому рядку "привіт", можна запустити наступну програму

#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'};


Я думаю, що векторів у С немає.
Аджай Мішра
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.