2015-10-22 19 views
15

Dlaczego printf drukuje spację zamiast zatrzymywać się, gdy używam znaku NULL z tabeli ASCII? To, co mam na myśli:Przepuszczanie pustego bajtu za pomocą specyfikatora formatu w `printf`

printf("Hello%c, world", 0); //Hello , world 
printf("Hello%c, world", '\0'); //Hello , world 

Dopiero kiedy mogę umieścić znak ucieczki w samym ciąg printf zatrzymuje łańcuch:

printf("Hello\0, world"); //Hello 

Próbowałem to na Windows 8, Windows 10 (przy użyciu Cygwin MinGW , Netbeans, Code :: Blocks), XUbuntu, to wszystko to samo.

Gdzie jest problem? Poprosiłem jednego z moich przyjaciół, ale powiedział, że nie ma takiego problemu, że wszystkie trzy przykłady zostały wykonane w ten sam sposób.

+0

Dlaczego uważasz, że pierwsze 2 powinny wydrukować tylko Hello? [C++ example] (http://coliru.stacked-crooked.com/a/1d7e28cf27e49f71) – rozina

+2

Ponieważ 0 jest liczbą terminatorów NULL/zero w tabeli ASCII, a jej odpowiednikiem jest "\ 0". –

+2

@rozina, ponieważ ''\ 0'' jest znakiem kończącym zero? –

Odpowiedz

22

printf("Hello\0, world"); wykorzystuje parametr jako C łańcucha więc dekoduje aż stwierdzi char NUL, tak że zatrzymuje się po \0, pomijając co następuje.

printf("Hello%c, world", 0); dekoduje jego parametr (dopóki nie znajdzie się wewnątrz niej NUL char - czyli po d), w międzyczasie znajdzie %c, więc zastąpi go char podanej jako parametr (którego kod ASCII jest NUL), a następnie wysłać do terminalu znak NUL, a następnie kontynuować.

printf instrukcja mówi:

Te funkcje napisać wyjścia pod kontrolą ciąg formatu który określa w jaki sposób kolejne argumenty [...] są konwertowane na wyjściu.

+1

Masz jakieś odniesienie do Standardu na ten temat? – emlai

+2

Z definicji jest to ciąg znaków C i format printf. –

+1

Pytanie wciąż pozostaje - dlaczego w niektórych kompilatorach (jako jeden z moich przyjaciół) te trzy przykłady wykonują to samo. –

6

Podejmujesz zależność od szczegółów implementacji printf(). Funkcja wyjściowego wyjścia niskopoziomowego wymaga długości ciągu jako argumentu. Istnieją dwa sposoby, aby to zrobić printf().

Nieco oczywistym sposobem jest najpierw sformatować ciąg, następnie użyć strlen(). Tego właśnie oczekiwałeś.

Ale to nieefektywne, ponieważ wymaga podwójnego przejścia przez bufor ciągów i dołączanie 0. Innym sposobem jest śledzenie sformatowanej długości ciągu podczas zastępowania pól, po prostu zwiększając go dla każdego dołączonego znaku. Ponieważ kontynuuje on poza% c, otrzymasz teraz większą długość obejmującą wszystko za% c. To, co robi funkcja terminalowa z osadzonym 0, jest również szczegółem implementacji, biorąc pod uwagę, że nie jest to znak możliwy do wydrukowania. Zastąpienie go spacją nie jest rzadkością.

Sposobem na to jest nie poleganie na szczegółach implementacji.

1
printf("Hello%c, world", 0); //Hello , world 
printf("Hello%c, world", '\0'); //Hello , world 

W obu tych przypadkach, starasz się wydrukować wartości znak odpowiadający kod znaku 0, który nie jest do druku znaków. Nie znalazłem na nim rozdziału i wersetu, ale podejrzewam, że zachowanie próby drukowania wartości nul jest nieokreślone, a może nawet nieokreślone. Tak czy inaczej, nie spodziewałbym się, że w tym przypadku będzie traktowany jako terminator łańcuchowy.

printf("Hello\0, world"); //Hello 

W tym przypadku znak nul jest częścią stałej łańcuchowej i interpretowany przez kompilator jako terminator ciągu znaków.

+0

NUL char jest zdefiniowany jako rodzaj NOP dla wyniku. –

+1

@ Jean-BaptisteYunès: Rozdział i werset? Nie mogłem znaleźć żadnego ostatecznego języka w standardzie, ale przeszukiwanie pliku PDF na iPadzie to rodzaj bólu w dupie. –

+0

Standard ASCII. ** 7,24 ** ** NUL ** (** NULL **). Znak kontrolny używany do wypełnienia nośnika lub wypełnienia. Znaki NUL mogą być wstawiane lub usuwane ze strumienia danych bez wpływu na zawartość informacyjną tego strumienia, ale takie działanie może wpływać na układ informacji i/lub kontrolę sprzętu. –

0

W skrócie: %c oznacza wydrukować znak, tak printf Drukuj NUL postać, której wartość wynosi 0. NUL jest to znaki niedrukowalne. Więc widzimy tam tylko przestrzeń.

"Witaj \ 0, świat" to literał łańcuchowy, wynikiem strlen("Hello\0, world") jest 5. Tak więc wyświetli wynik "Cześć".

można zobaczyć więcej na stronie cppreference: string literal

Łańcuch znaków dosłowny to sekwencja zero lub więcej znaków wielobajtowych zamkniętych w podwójnych cudzysłowach, jak w „xyz”. Znak null ("\ 0") jest zawsze dołączany do literału łańcuchowego, tak więc literał łańcuchowy "Hello" to const char [6] zawierający znaki "H", "e", "l", "l" , "0" i "\ 0". Jeśli literał łańcuchowy zawiera osadzone znaki null, reprezentuje tablicę zawierającą więcej niż jeden ciąg znaków.

Powiązane problemy