2010-10-13 15 views
5
struct DVDInfo *ReadStruct(void) { 
    struct DVDInfo *infoPtr; 
    int    num; 
    char   line[ kMaxLineLength ]; 
    char   *result; 

    infoPtr = malloc(sizeof(struct DVDInfo)); 

    if (NULL == infoPtr) { 
     printf("Out of memory!!! Goodbye!\n"); 
     exit(0); 
    } 

    printf("Enter DVD Title: "); 
    result = fgets(line, kMaxLineLength, stdin); 
    line[ strlen(line) - 1 ] = '\0'; 
    infoPtr->title = MallocAndCopy(line); 

    printf("Enter DVD comment: "); 
    result = fgets(line, kMaxLineLength, stdin); 
    line[ strlen(line) - 1 ] = '\0'; 
    infoPtr->comment = MallocAndCopy(line); 

    do { 
     printf("Enter DVD Rating (1-10): "); 
     scanf("%d", &num); 
     Flush(); 
    } 
    while ((num < 1) || (num > 10)); 

    infoPtr->rating = num; 

    printf("\n----------\n"); 

    return(infoPtr); 
} 

Tôi đã hỏi một câu hỏi khác về mã này trong một luồng khác trên stackoverflow nhưng không muốn nhân đôi trên đó - tại sao điểm cuối không được thêm vào cuối các tệp này được đọc bởi fgets? fgets cho biết thêm các chấm dứt zero anyway, không phải là overkill này?fgets tự nhiên đặt một số không kết thúc trong C?

+2

C đã đủ đệm tràn ngập cơ hội như nó được. Người ta nên cổ vũ khi chức năng dành thời gian để đảm bảo một chuỗi được chấm dứt một cách chính xác. (Một chuỗi trong C * phải * chấm dứt với một NUL là một chuỗi hợp lệ, theo định nghĩa trong C.) –

+2

Vâng, nó quá mức cần thiết. –

Trả lời

2

fgets viết một terminator nul vào bộ đệm bạn cung cấp (nếu bạn chỉ định kích thước bộ đệm lớn hơn 0). Nếu không, bạn không thể gọi strlen() trên nó, strlen() mong đợi một chuỗi, và nếu nó không phải là nul chấm dứt nó không phải là một chuỗi.

Bạn đang hỏi về

line[ strlen(line) - 1 ] = '\0'; 

này dải tắt ký tự cuối cùng trong line .Nếu bạn đã đọc một dòng, nó sẽ thay thế các ký tự cuối cùng, có lẽ là một \ n với một terminator nul.

Hãy xem xét rằng các fgets chỉ đọc một dòng, ví dụ: bộ đệm line của bạn hiện chứa chuỗi "Xin chào \ n" (\ n chỉ là chuỗi thoát ở đây, nó thực sự chỉ là 1 ký tự, không phải 2)

strlen ("Hello \ n") là 6 và 6 1 là 5, vì vậy 5. Chỉ số này được thay thế bằng 0

"Hello\n" 
    ^
     | 
     Add 0 terminator 

Kết quả: "Hello"

Chỉ cần cẩn thận:

  • bạn không muốn làm dòng [strlen (dòng) - 1] = '\ 0'; trên một chuỗi rỗng, trong trường hợp đó bạn sẽ kết thúc làm dòng [-1].
  • Bạn nên kiểm tra xem fgets có succeds hay không. Bạn không muốn poke xung quanh trong line nếu fgets thất bại, và không viết bất cứ điều gì để đệm của bạn.
  • Bạn có thể muốn kiểm tra xem toàn bộ dòng có thực sự được đọc hay không. NẾU dòng bạn đọc lớn hơn kMaxLineLength, hoặc ví dụ: nếu "dòng" cuối cùng trong tệp không có dấu \ n, strlen (dòng) -1 sẽ không là \ n (dòng mới).
+1

Điểm bullet cuối cùng của bạn cũng nên tính đến dòng cuối cùng trong một tệp không có dòng mới. – jamesdlin

3

Nói chung, bạn thay thế ký tự dòng mới mà fgets thêm vào chuỗi có ký tự NUL. Trong mọi trường hợp, fgets sẽ NUL chấm dứt.

Xem: http://www.opengroup.org/onlinepubs/009695399/functions/fgets.html

+0

fgets luôn chấm dứt bằng 0; –

+2

không phải lúc nào. Nếu bạn vượt qua chiều dài là 0 thì nó sẽ không. Trường hợp góc điên mặc dù. – JaredPar

1

result = fgets(line, kMaxLineLength, stdin); 

là Ok vì kích thước của dòng là kMaxLineLength của bạn.

fgets đọc trong ít nhất một ít hơn size ký tự từ stream và lưu trữ chúng vào bộ đệm ...

1

Các line[ strlen(line) - 1 ] = '\0'; là không cần thiết (và không an toàn — strlen() sẽ không hoạt động đúng nếu chuỗi isn' t đã được nul chấm dứt). fgets() sẽ hủy bỏ bộ đệm. Ngoài ra, bạn nên kiểm tra xem result != NULL trước khi thử sao chép line. fgets() trả về NULL ở cuối tệp hoặc nếu xảy ra lỗi.

+1

+1 để kiểm tra giá trị trả về của 'fgets'. – pmg

1

Có, nó quá mức cần thiết.

Một gợi ý để làm cho nó mạnh mẽ hơn chống lại các mã thối ... thay đổi

result = fgets(line, kMaxLineLength, stdin); 

để

result = fgets(line, sizeof(line), stdin); 
Các vấn đề liên quan