Jaka jest różnica między printf () i puts () w C?

176

Wiem, że możesz drukować za pomocą printf()i puts(). Widzę również, że printf()pozwala to na interpolację zmiennych i formatowanie.

Jest puts()tylko prymitywną wersją printf(). Czy powinien być używany na wszystkie możliwe sposoby printf()bez interpolacji ciągów?

Alex
źródło
47
Tylko uwaga o używaniu printf zamiast puts: never, ever rób a, printf(variable)aby wydrukować łańcuch. Użyj puts(variable)lub printf("%s', variable). Stosowanie łańcucha formatu zmiennej wiąże się z ryzykiem dla bezpieczeństwa: jeśli zmienna może zostać zapisana przez osobę atakującą, może zaatakować program przy użyciu ciągów formatu.
Zan Lynx

Odpowiedzi:

141

putsjest prostsze niż, printfale pamiętaj, że ta pierwsza automatycznie dołącza nową linię. Jeśli tego nie chcesz, możesz fputsustawić ciąg na standardowe wyjście lub użyć printf.

Michael Kristofik
źródło
8
Myślę, że ważne jest również, aby wspomnieć o dodatkowych argumentach, które printf pobiera w celu dodania dodatkowych zmiennych do wyjściowego ciągu.
Erutan409
99

(Wskazuje na to komentarz Zana Lynxa, ale myślę, że zasługuje na odpowiedź - biorąc pod uwagę, że przyjęta odpowiedź o tym nie wspomina).

Zasadnicza różnica między puts(mystr);i printf(mystr);polega na tym, że w drugim przypadku argument jest interpretowany jako łańcuch formatujący . Wynik będzie często taki sam (z wyjątkiem dodanego nowego wiersza), jeśli łańcuch nie zawiera żadnych znaków sterujących ( %), ale jeśli nie możesz na tym polegać (jeśli mystrjest to zmienna zamiast literału), nie powinieneś jej używać.

Zatem generalnie niebezpieczne - i koncepcyjnie błędne - jest przekazywanie dynamicznego ciągu jako pojedynczego argumentu printf:

  char * myMessage;
  // ... myMessage gets assigned at runtime, unpredictable content
  printf(myMessage);  // <--- WRONG! (what if myMessage contains a '%' char?) 
  puts(myMessage);    // ok
  printf("%s\n",myMessage); // ok, equivalent to the previous, perhaps less efficient

To samo dotyczy fputsvs fprintf(ale fputsnie dodaje nowej linii).

leonbloy
źródło
W jaki sposób używanie printf()byłoby mniej wydajne? W czasie wykonywania? W czasie kompilacji?
franklin
10
@franklin w czasie wykonywania, ponieważ printfmusi przeanalizować ciąg formatu. Jednak zwykle nie powinno to mieć znaczenia. Co więcej, sprytny kompilator mógłby to zoptymalizować i zastąpić printfwywołanieputs
leonbloy
33

Oprócz formatowania putszwraca nieujemną liczbę całkowitą, jeśli zakończy się powodzeniem lub EOFniepowodzeniem; while printfzwraca liczbę wydrukowanych znaków (bez końcowego null).

echristopherson
źródło
16

W prostych przypadkach kompilator konwertuje wywołania do printf()na wywołania puts().

Na przykład następujący kod zostanie skompilowany do kodu asemblera, który pokażę dalej.

#include <stdio.h>
main() {
    printf("Hello world!");
    return 0;
}
push rbp
mov rbp,rsp
mov edi,str.Helloworld!
call dword imp.puts
mov eax,0x0
pop rbp
ret

W tym przykładzie użyłem GCC w wersji 4.7.2 i skompilowałem źródła z gcc -o hello hello.c.

Hannu Balk
źródło
9
A co z nową linią, która umieszcza miejsca w stdout?
zubergu
1
Powinien był printf("Hello world!\n");rzeczywiście zostać przetłumaczony przez gcc na puts. Ponieważ jest to stara wiadomość, samodzielnie ją edytuję.
Rafael Almeida
2
Jak przeczytałeś kod asemblera po skompilowaniu kodu C?
Koray Tugay
3
@KorayTugay: -save-tempsopcja dla gcc to robi
schaiba
Możesz także użyć narzędzia takiego jak gdb, aby zdemontować plik binarny.
Ivan Kaloyanov
10

Racja, printfmożna to uznać za mocniejszą wersję puts. printfzapewnia możliwość formatowania zmiennych wyjściowych w formacie dla specyfikatorami takich jak %s, %d, %lfitp ...

Justin Ethier
źródło
10

Z mojego doświadczenia printf()wynika , że zaciąga więcej kodu niż puts()niezależnie od ciągu formatu.

Jeśli nie potrzebuję formatowania, nie używam printf. Jednak, fwriteaby stdoutdziała dużo szybciej niż puts.

static const char my_text[] = "Using fwrite.\n";
fwrite(my_text, 1, sizeof(my_text) - sizeof('\0'), stdout);

Uwaga: na komentarz „\ 0” jest stałą liczbą całkowitą. Prawidłowe wyrażenie powinno być sizeof(char)zgodne z komentarzami.

Thomas Matthews
źródło
2
„Wypisz na stdout działa dużo szybciej niż puts”. - Jaki może być powód?
Antony Hatchkins
6
@AntonyHatchkins Zazwyczaj nie jest to dużo szybsze. Jednak puts () musi wykonać wywołanie strlen () za każdym razem na twoim napisie, podczas gdy jeśli rozmiar jest znany za pomocą fwrite (), można tego uniknąć. To właściwie jedyny rzeczywisty wpływ na różnicę w wydajności.
Wiz
8
Ta odpowiedź jest nieprawidłowa. '\0'ma typ int, więc w większości systemów będzie to drukowane Using fwrit. Jeśli chcesz wydrukować 1 bajt mniej, po prostu użyj 1. sizeof (char), co jest prawdopodobnie tym, co zamierzałeś tutaj, na pewno będzie 1.
Bradley Garagan
8
int puts(const char *s);

puts () zapisuje łańcuch si końcowy znak nowej linii na standardowe wyjście.

int printf(const char *format, ...);

Funkcja printf () zapisuje dane wyjściowe na standardowe wyjście, pod kontrolą łańcucha formatu, który określa, w jaki sposób kolejne argumenty są konwertowane na wyjście.

Skorzystam z okazji, aby poprosić o przeczytanie dokumentacji.

Koray Tugay
źródło
5

funkcja printf () jest używana do drukowania zarówno łańcuchów, jak i zmiennych na ekranie, podczas gdy funkcja puts () pozwala tylko na wydrukowanie ciągu tylko na ekranie.

Wesley Nyandika
źródło
2

putsjest prostym wyborem i dodaje nowy wiersz na końcu i printfzapisuje wynik ze sformatowanego ciągu.

Zobacz dokumentację dla puts i dla printf.

printfZalecałbym używanie tylko, ponieważ jest to bardziej spójne niż metoda przełączania, tj. Jeśli debbugujesz, przeszukiwanie wszystkich printfs jest mniej bolesne niż putsi printf. W większości przypadków chcesz wypisać zmienną również na swoich wydrukach, więc putsjest ona najczęściej używana w przykładowym kodzie.

Johan Engblom
źródło
1

Podczas porównywania puts()i printf(), mimo że ich zużycie pamięci jest prawie takie samo, puts()zajmuje więcej czasu w porównaniu z printf().

thil
źródło
Dodaj wyjaśnienie do swojej odpowiedzi, tak aby inni mogli się z niej nauczyć - czy masz wiarygodne źródła tego twierdzenia? Albo jakieś powody, by wyjaśnić tę różnicę?
Nico Haase