2011-12-31 33 views
6

Đây là một chương trình thử nghiệm mà tôi đã viết cho một dự án lớn hơn mà tôi đang làm việc. Nó có liên quan đến việc viết dữ liệu struct vào đĩa với fwrite() và sau đó đọc dữ liệu đó trở lại với fread(). Một thành viên của cấu trúc được phân bổ động.C fread() đọc một cách kỳ diệu các thành phần cấu trúc được phân bổ động, như thế nào?

Thứ nhất, đây là mã của tôi

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define STRING_LEN 128 

struct Person { 
    int age; 
    char *name; 
}; 

int main(int argc, const char *argv[]) 
{ 
    struct Person *person = calloc(1, sizeof(struct Person)); 
    person->age = 22; 
    person->name = calloc(STRING_LEN, sizeof(char)); 

    char *name = "Name that is really, really, really, really, really, really, long."; 
    strncpy(person->name, name, STRING_LEN); 

    FILE *out_file = fopen("rw.out", "w"); 
    fwrite(person, sizeof(struct Person), 1, out_file); 
    fclose(out_file); 

    FILE *in_file = fopen("rw.out", "r"); 
    struct Person *person_read = calloc(1, sizeof(struct Person)); 
    fread(person_read, sizeof(struct Person), 1, in_file); 
    fclose(in_file); 

    printf("%d %s\n", person_read->age, person_read->name); 

    free(person->name); 
    free(person); 
    free(person_read); 

    return 0; 
} 

Và outpout

22 Name that is really, really, really, really, really, really, long. 

Câu hỏi của tôi là, tại sao làm việc này? Không nên viết fwrite() chỉ viết địa chỉ mà 'tên' chứa (tức là địa chỉ bắt đầu của chuỗi)? Đó là, tôi đang đi qua trong sizeof (struct Person) để fwrite() và nó được viết chuỗi các 'tên' được trỏ đến.

Thậm chí còn khó hiểu hơn với tôi là hành vi của fread(). Một lần nữa, nếu tôi đi qua sizeof (struct Person), giá trị thực của 'name' được đọc như thế nào? Làm thế nào là bộ nhớ cho nó được phân bổ?

Sự hiểu biết trước đây của tôi về cách sử dụng fwrite() + fread() là tôi sẽ phải "viết thủ công" dữ liệu mà 'tên' trỏ tới, "theo cách thủ công" đọc dữ liệu đó rồi sao chép chuỗi đó sau khi cấp phát bộ nhớ cho cả cấu trúc và thành viên 'tên'. Nói cách khác, tôi sẽ phải tự đi qua bất kỳ con trỏ nào, ghi dữ liệu, và sau đó đọc dữ liệu đó theo thứ tự giống nhau.

EDIT: Dan và các mục khác là chính xác. Tôi đã xem xét các tập tin đầu ra với xxd:

0000000: 1600 0000 0000 0000 30a0 d900 0000 0000 ........0....... 

Nếu tôi in ra địa chỉ đó 'tên' chứa trước khi viết và sau khi đọc nó là như nhau (0xd9a030), mà phù hợp với sản lượng từ xxd.

Trả lời

9

Bạn đang ghi dữ liệu trong cấu trúc, là một int theo sau là một con trỏ tới một chuỗi. Nó chỉ là dữ liệu giống như bất cứ điều gì khác, và bạn biết nó dài bao lâu bởi vì cấu trúc là chiều dài cố định - một int cộng với một con trỏ. Bạn đọc cùng một con trỏ đến cùng một chuỗi tên như bản gốc. Tên chính nó không được viết và cũng không đọc.

+0

+1 Bạn có thể kiểm tra điều này bằng kích thước in ấn (struct Person) và/hoặc bằng cách xem bên trong tệp tin văn bản. fread() chỉ đọc một con trỏ (một địa chỉ bộ nhớ), nó xảy ra trùng với tên 'người-> hiện tại'. – leonbloy

+0

Bảo đảm duy nhất là cấu trúc có cùng độ dài trong cùng một cấu hình môi trường. Nó có thể thay đổi thời điểm bạn sử dụng -m32, -m64, thay đổi thông số căn chỉnh chung, v.v. –

1

Con trỏ tên * vẫn hợp lệ trong suốt các lệnh gọi hàm fwrite và fread, điều này dường như là một lời cảm ơn đối với bạn. Nếu bạn miễn phí (person-> name) trước printf bạn sẽ nhận được kết quả hoặc lỗi mà bạn mong đợi.

5

Cả hai person->nameperson_read->name gió chỉ tới cùng một vị trí bộ nhớ. Vì bạn không deallocate person->name trước khi đọc tệp trở lại, giá trị con trỏ trong person_read->name vẫn hợp lệ.

Nếu bạn đã deallocated person->name hoặc đọc tệp từ một chương trình khác, giá trị con trỏ sẽ không còn hợp lệ nữa, và cố gắng tham chiếu nó sẽ gọi hành vi không xác định - bạn sẽ in ra vô nghĩa hoặc nhận được một segfault.

+0

+1 Cảm ơn, điều này có ý nghĩa rõ ràng sau khi đọc câu trả lời của bạn. – tsliwkan

Các vấn đề liên quan