„\ 0” i printf () w C

21

W kursie wprowadzającym do języka C nauczyłem się, że podczas przechowywania łańcuchy są przechowywane \0na końcu ze znakiem zerowym . Ale co, jeśli chciałbym wydrukować ciąg, powiedz, printf("hello")chociaż zauważyłem, że nie kończy się \0na poniższym stwierdzeniu

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

Output: 5

ale wydaje się to niespójne, o ile wiem, że zmienne, takie jak łańcuchy, są przechowywane w pamięci głównej i wydaje mi się, że podczas drukowania coś może być również przechowywane w pamięci głównej, to dlaczego różnica?

Ajay Mishra
źródło
1
Poza tym, że Twój kod przynajmniej nie trafia );, co zamierzasz pokazać za pomocą tego kodu? Jak udowodniłeś, że to nie kończy się na \0?
glglgl
I co ma z tym wspólnego pamięć?
Tsakiroglou Fotis
W C wszystkie ciągi literalne są tak naprawdę tablicami znaków, które zawierają null-terminator.
Jakiś programista koleś
@glglgl Myślę, że printf () zwraca liczbę znaków, które powinien wydrukować na ekranie.
Ajay Mishra
4
@AjayMishra Tak, i rzeczywiście powinien mieć wydrukowane 5 znaków. Końcowy bajt 0 nie jest drukowany na ekranie.
glglgl

Odpowiedzi:

13

Bajt zerowy oznacza koniec łańcucha. Nie jest liczony do długości łańcucha i nie jest drukowany, gdy łańcuch jest drukowany printf. Zasadniczo bajt zerowy mówi funkcjom, które wykonują manipulacje ciągami, kiedy zatrzymać.

Różnica polega na tym, że utworzysz chartablicę zainicjowaną łańcuchem. Użycie sizeofoperatora odzwierciedla rozmiar tablicy, w tym bajt zerowy. Na przykład:

char str[] = "hello";
printf("len=%zu\n", strlen(str));     // prints 5
printf("size=%zu\n", sizeof(str));    // prints 6
dbush
źródło
Myślałem, że historia będzie inna printf(). TBH Nie mam pojęcia, jak to printf()działa.
Ajay Mishra
8

printfzwraca liczbę wydrukowanych znaków. '\0'nie jest drukowany - oznacza to tylko, że w tym ciągu nie ma już znaków. Nie jest również liczony do długości łańcucha

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__
źródło
6

Twoje założenie jest błędne. Twój ciąg rzeczywiście kończy się na \0.

Zawiera 5 znaków h, e, l, l, oi 0 charakterze.

Wyjściowe print()wywołanie „wewnętrzne” to liczba wydrukowanych znaków, a to 5.

glglgl
źródło
6

W C wszystkie ciągi literalne są tak naprawdę tablicami znaków, które zawierają null-terminator.

Jednak terminator zerowy nie jest liczony jako długość łańcucha (dosłownie lub nie) i nie jest drukowany. Drukowanie zatrzymuje się po znalezieniu terminatora zerowego.

Jakiś koleś programisty
źródło
Czy istnieje sposób, aby to zweryfikować bez przechowywania łańcucha w tablicy?
Ajay Mishra
1
@AjayMishra Cóż, łańcuch już znajduje się w tablicy (jak wspomniano) ... Ale jeśli nie printfbyłoby zerowego terminatora, to wykraczałby poza granice łańcucha i drukowałby „losowe” lub „śmieciowe” znaki i zwracał liczba różna od długości ciągu. Jeśli znasz już długość łańcucha, możesz również sprawdzić, czy znak pod tym indeksem jest '\0', co będzie działać, ale będzie technicznie niezdefiniowanym zachowaniem, jeśli rozmiar tablicy nie zawiera terminatora (jak w char arr[5] = "hello";, który nie doda terminator do tablicy).
Jakiś programista koleś
@AjayMishra Tak E. g., Możesz zrobić char * p = "Hello"; int i = 0; while (p[i] != '\0') { printf("%d: %c", i, p[i]); i++; }i zobaczyć, jak to działa: pokazuje linie z indeksami i zawartość w tym wierszu. Po indeksie 4 znajduje znak 0 i przerywa pętlę while. Tam widzisz, że jest 0 znaków.
glglgl
6

Wszystkie odpowiedzi są naprawdę dobre, ale chciałbym dodać kolejny przykład, aby je wszystkie uzupełnić

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

Dla tych, którzy nie chcą wypróbować tego na gdb online, wynik jest następujący:

Witaj świecie

Piekło

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

Czy to pomaga zrozumieć, co robi terminator ucieczki? Nie jest to granica dla tablicy znaków ani łańcucha. To postać, która powie facetowi, który analizuje -STOP, (drukuj) parsuj aż do tego momentu.

PS: A jeśli parsujesz i wydrukujesz go jako tablicę znaków

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

dostajesz:

Piekielny świat

gdzie, spacja po podwójnym l, jest zakończeniem zerowym, jednak parsowanie tablicy char spowoduje tylko wartość char każdego bajtu. Jeśli wykonasz inną analizę i wydrukujesz wartość int każdego bajtu („% d%, char_array [i]), zobaczysz, że (otrzymasz reprezentację kodu ASCII-int) biała spacja ma wartość 0.

Tsakiroglou Fotis
źródło
4

W Cfunkcji printf()zwraca liczbę znaków drukowanego, \0jest to nullterminator, który jest używany do wskazania końca ciągu znaków w języku C i tam nie jest zbudowany w stringrodzaju jak z c++jednak twoje potrzeby rozmiar tablicy być przynajmniej większa niż liczba charciebie chce przechować.

Oto ref: cpp ref printf ()

jakiś użytkownik
źródło
3

Ale co, jeśli chciałbym wydrukować ciąg, powiedz printf („witaj”), chociaż stwierdziłem, że nie kończy się na \ 0 następującą instrukcją

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

Output: 5

Mylisz się. To stwierdzenie nie potwierdza, że ​​literał łańcucha "hello"nie kończy się znakiem kończącym zero '\0'. To stwierdzenie potwierdziło, że funkcja printfwyprowadza elementy łańcucha, dopóki nie zostanie napotkany znak kończący zero.

Gdy używasz literału ciągowego, jak w powyższej instrukcji, kompilator tworzy tablicę znaków o statycznym czasie przechowywania, który zawiera elementy literału ciągowego.

W rzeczywistości to wyrażenie

printf("hello")

jest przetwarzany przez kompilator w sposób podobny do następującego

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

Działanie funkcji printf w tym można sobie wyobrazić w następujący sposób

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

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

    return result;
}

Aby uzyskać liczbę znaków zapisanych w ciągu literowym „hello”, możesz uruchomić następujący program

#include <stdio.h>

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

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

    return 0;
}

Wyjście programu to

The size of the literal "hello" is 6
Vlad z Moskwy
źródło
0

Najpierw musisz wyczyścić swoją koncepcję. Ponieważ zostanie ona wyczyszczona, gdy zajmujesz się tablicą, użyte polecenie drukowania po prostu zlicza znaki umieszczone w nawiasie. Jest niezbędny w ciągu tablicowym, aby kończył się na \ 0

Muhammad Kashif
źródło
0

Łańcuch jest wektorem znaków. Zawiera sekwencję znaków tworzących ciąg, a następnie ciąg znaków specjalnych na końcu: „\ 0”

Przykład: char str [10] = {'H', 'e', ​​'l', 'l', 'o', '\ 0'};

Przykład: następujący wektor znaków nie jest jednym ciągiem, ponieważ nie kończy się na „\ 0”

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

Nicola Brogelli
źródło
Chyba nie ma wektorów w C.
Ajay Mishra