Sự khác biệt giữa printf () và put () trong C là gì?


176

Tôi biết bạn có thể in với printf()puts(). Tôi cũng có thể thấy rằng printf()cho phép bạn nội suy các biến và định dạng.

Chỉ puts()đơn thuần là một phiên bản nguyên thủy của printf(). Nó có nên được sử dụng cho mọi khả năng printf()mà không cần nội suy chuỗi không?



47
Chỉ cần một lưu ý về việc sử dụng printf thay vì đặt: không bao giờ, bao giờ thực hiện printf(variable)để in một chuỗi. Sử dụng puts(variable)hoặc printf("%s', variable). Có một rủi ro bảo mật khi sử dụng chuỗi định dạng biến: nếu biến có thể được viết bởi kẻ tấn công, chúng có thể tấn công chương trình bằng cách sử dụng chuỗi định dạng.
Zan Lynx

Câu trả lời:


141

putsđơn giản hơn printfnhưng hãy lưu ý rằng trước đây sẽ tự động nối thêm một dòng mới. Nếu đó không phải là những gì bạn muốn, bạn có thể fputsxâu chuỗi hoặc sử dụng printf.


8
Tôi nghĩ điều quan trọng là phải đề cập đến các đối số bổ sung printf dùng để thêm các biến bổ sung vào chuỗi xuất ra.
Erutan409

99

(Điều này được chỉ ra trong một nhận xét của Zan Lynx, nhưng tôi nghĩ rằng nó xứng đáng với một câu hỏi - cho rằng câu trả lời được chấp nhận không đề cập đến nó).

Sự khác biệt cơ bản giữa puts(mystr);printf(mystr);là ở phần sau, đối số được hiểu là một chuỗi định dạng . Kết quả sẽ giống nhau (ngoại trừ dòng mới được thêm vào) nếu chuỗi không chứa bất kỳ ký tự điều khiển nào ( %) nhưng nếu bạn không thể dựa vào đó (nếu mystrlà biến thay vì bằng chữ) thì bạn không nên sử dụng nó.

Vì vậy, nói chung là nguy hiểm - và sai về mặt khái niệm - để truyền một chuỗi động dưới dạng đối số duy nhất của 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

Điều tương tự áp dụng cho fputsvs fprintf(nhưng fputskhông thêm dòng mới).


Bằng cách nào sử dụng printf()sẽ kém hiệu quả? Trong thời gian chạy? Tại thời điểm biên dịch?
franklin

10
@franklin trong thời gian chạy, vì printfcần phân tích chuỗi định dạng. Tuy nhiên, điều này thường không liên quan. Hơn nữa, một trình biên dịch thông minh có thể tối ưu hóa điều này và thay thế printfbằng cuộc gọi đếnputs
leonbloy

33

Bên cạnh định dạng, putstrả về một số nguyên không âm nếu thành công hoặc EOFnếu không thành công; trong khi printftrả về số lượng ký tự được in (không bao gồm null null).


16

Trong các trường hợp đơn giản, trình biên dịch chuyển đổi các cuộc gọi thành printf() thành các cuộc gọi thànhputs() .

Ví dụ, đoạn mã sau sẽ được biên dịch thành mã lắp ráp mà tôi hiển thị tiếp theo.

#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

Trong ví dụ này, tôi đã sử dụng GCC phiên bản 4.7.2 và biên dịch nguồn với gcc -o hello hello.c.


9
Và những gì về dòng mới đặt vị trí trong thiết bị xuất chuẩn?
zubergu

1
Nó nên được printf("Hello world!\n");gcc thực sự dịch mà đặt. Vì đó là một tin nhắn cũ, tôi sẽ tự chỉnh sửa nó.
Rafael Almeida

2
Làm thế nào bạn đọc mã lắp ráp sau khi biên dịch mã C?
Koray Tugay

3
@KorayTugay: -save-tempstùy chọn cho gcc thực hiện điều đó
schaiba

Bạn cũng có thể sử dụng một công cụ như gdb để phân tách nhị phân.
Ivan Kaloyanov

10

Phải, printfcó thể được coi là một phiên bản mạnh mẽ hơn puts. printfcung cấp khả năng định dạng biến cho đầu ra sử dụng định dạng specifiers như %s, %d, %lf, vv ...


10

Theo kinh nghiệm của tôi, printf()hauls trong nhiều mã hơn puts()bất kể chuỗi định dạng.

Nếu tôi không cần định dạng, tôi không sử dụng printf. Tuy nhiên, fwriteđể stdoutlàm việc nhanh hơn nhiều puts.

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

Lưu ý: trên mỗi nhận xét, '\ 0' là hằng số nguyên. Các biểu thức chính xác nên được sizeof(char)chỉ định bởi các ý kiến.


2
"fwrite để stdout hoạt động nhanh hơn rất nhiều so với đặt." - Điều gì có thể là lý do?
Antony hatchkins 15/03/13

6
@AntonyHatchkins Nó thường không "nhanh" hơn nhiều. tuy nhiên, đặt (), phải thực hiện lệnh gọi strlen () mỗi lần trên chuỗi của bạn trong khi nếu kích thước được biết với fwrite () thì có thể tránh được. Đó là khá nhiều người đóng góp thực sự duy nhất cho một sự khác biệt hiệu suất.
Wiz

8
Câu trả lời này không chính xác. '\0'có loại int, vì vậy trên hầu hết các hệ thống này sẽ in Using fwrit. Nếu bạn muốn in ít hơn 1 byte, chỉ cần sử dụng 1. sizeof (char), đó có thể là những gì bạn dự định ở đây, được đảm bảo là 1.
Bradley Garagan

8
int puts(const char *s);

đặt () viết chuỗi s và một dòng mới vào stdout.

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

Hàm printf () ghi đầu ra vào thiết bị xuất chuẩn, dưới sự kiểm soát của chuỗi định dạng xác định cách các đối số tiếp theo được chuyển đổi cho đầu ra.

Tôi sẽ sử dụng cơ hội này để yêu cầu bạn đọc tài liệu.


5

hàm printf () được sử dụng để in cả chuỗi và biến lên màn hình trong khi hàm put () chỉ cho phép bạn in một chuỗi chỉ lên màn hình.


2

puts là sự lựa chọn đơn giản và thêm một dòng mới vào cuối và printf ghi đầu ra từ một chuỗi được định dạng.

Xem tài liệu cho puts và choprintf .

Tôi khuyên bạn chỉ nên sử dụng printfvì điều này phù hợp hơn phương thức chuyển đổi, tức là nếu bạn đang gỡ lỗi, việc tìm kiếm tất cả các bản in sẽ ít đau đớn hơn putsprintf. Hầu hết các lần bạn cũng muốn xuất một biến trong bản in của mình, do đó, putsphần lớn được sử dụng trong mã ví dụ.


1

Khi so sánh puts()printf(), mặc dù mức tiêu thụ bộ nhớ của chúng gần như nhau, nhưng puts()mất nhiều thời gian hơn so với printf().


Vui lòng thêm một số giải thích cho câu trả lời của bạn để người khác có thể học hỏi từ nó - bạn có nguồn đáng tin cậy cho khiếu nại đó không? Hoặc một số lý do để giải thích sự khác biệt này?
Nico Haase
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.