2013-06-23 37 views
39

VớiTại sao nó an toàn hơn để sử dụng sizeof (* con trỏ) trong malloc

struct node 
{ 
    int a; 
    struct node * next; 
}; 

Để malloc một cấu trúc mới,

struct node *p = malloc(sizeof(*p)); 

là an toàn hơn

struct node *p = malloc(sizeof(struct node)); 

Tại sao? Tôi nghĩ chúng giống nhau.

+8

FYI, khi được sử dụng với một biến, 'sizeof' không cần dấu ngoặc:' p = malloc (sizeof * p); ' –

+52

Đó là chi tiết có thể đọc được với parens –

+5

@TomerArazy: Tôi cầu xin sự khác biệt; nó là _less_ có thể đọc được với parens. Không có parens, nó _cannot_ là một typename; sự thiếu hụt parens giúp người đọc của họ phân biệt. (nhỏ vấn đề: cũng có, các parens có thể cho rằng sizeof là một chức năng, mà nó không phải là) – wildplasser

Trả lời

58

Đó là an toàn hơn vì bạn không phải đề cập đến tên loại hai lần và không phải xây dựng chính tả thích hợp cho phiên bản "không tham chiếu" của loại. Ví dụ, bạn không cần phải "đếm những vì sao" trong

int *****p = malloc(100 * sizeof *p); 

so sánh với loại dựa trên sizeof trong

int *****p = malloc(100 * sizeof(int ****)); 

nơi bạn đã quá chắc chắn rằng bạn sử dụng đúng số * dưới sizeof.

Để chuyển sang loại khác, bạn chỉ phải thay đổi một địa điểm (khai báo p) thay vì hai. Và những người có thói quen đúc kết quả của malloc phải thay đổi ba địa điểm.

Nói chung, điều này có ý nghĩa rất nhiều khi tuân thủ nguyên tắc sau: nhập tên thuộc về khai báo và không có nơi nào khác. Các câu lệnh thực tế phải độc lập với loại. Họ nên tránh đề cập đến bất kỳ tên loại hoặc sử dụng bất kỳ tính năng loại cụ thể khác càng nhiều càng tốt.

Phương tiện sau có nghĩa là: Tránh phôi không cần thiết. Tránh cú pháp không đổi loại cụ thể không cần thiết (như 0.0 hoặc 0L trong đó một đồng bằng 0 sẽ đủ). Tránh đề cập đến tên loại dưới sizeof. Và cứ thế.

+12

Không có gì là thực tế rằng, khi loại được thay đổi, thay đổi nó trong khai báo của p là đủ (không cần phải thay đổi nó trong 'malloc') không chỉ có nghĩa là có ít công việc hơn nhưng có ít cơ hội lỗi hơn. Do đó, điều này giúp giảm lỗi. –

+0

AndreyT, tôi thấy quan điểm của bạn, trừu tượng nói ... nhưng tôi sẽ đặt cược bạn một bia không bao giờ là một nền tảng trong lịch sử của máy tính mà sizeof (int ****)! = Sizeof (int ***):) – Josh

+0

@Josh: Về cơ bản bạn nói rằng mỗi khi bạn cần biết kích thước con trỏ, bạn chỉ có thể sử dụng 'sizeof (double *)'. Có, nó sẽ hoạt động, nhưng những thứ như thế sẽ tạo ra cùng một loại căng thẳng chưa được giải quyết như một dấu ngoặc đơn chưa được so khớp bên trái (http://xkcd.com/859/) – AnT

19

Bởi vì nếu tại một số điểm sau này trong thời gian p được thực hiện để trỏ đến một kiểu cấu trúc sau đó phân bổ tuyên bố bộ nhớ của bạn sử dụng malloc không nhất thiết phải thay đổi, nó vẫn phân bổ đủ bộ nhớ cần thiết cho việc mới loại. Nó đảm bảo:

  • Bạn không phải sửa đổi tuyên bố phân bổ bộ nhớ mỗi khi bạn thay đổi loại nó phân bổ bộ nhớ.
  • Mã của bạn mạnh mẽ hơn và ít bị lỗi thủ công hơn.

Nói chung, luôn luôn là một thực hành tốt để không dựa vào các loại cụ thể và biểu mẫu đầu tiên chỉ làm điều đó, nó không khó mã hóa một loại.

3

Đó là thuận lợi, bởi vì bạn có thể chuyển đổi này:

struct node *p= malloc(sizeof(*p)); 

Into này:

#define MALLOC(ptr) (ptr) = malloc(sizeof(*(ptr))) 

struct node *p; 
MALLOC(p); 

Hoặc, đối với một mảng:

#define MALLOC_ARR(ptr, arrsize) \ 
     (ptr) = malloc(sizeof(*(ptr)) * arrsize) 

struct node *p; 
MALLOC_ARR(p, 20); 

Và tại sao điều này là an toàn? Bởi vì người dùng sử dụng các macro này sẽ ít có khả năng phạm sai lầm đã được vạch ra bởi AndreyT, giống như trong trường hợp của DIM() để nhận được kích thước của một mảng tĩnh.

#define DIM(arr) ((sizeof(arr))/(sizeof(arr[0]))) 

Điều này cũng an toàn hơn, vì người dùng không cần phải làm cho kích thước mảng tĩnh phù hợp ở một vài nơi. Đặt kích thước mảng ở một nơi và sau đó chỉ cần sử dụng DIM() và bạn đã hoàn tất! Trình biên dịch sẽ xử lý nó cho bạn.

0

Nó không ảnh hưởng trực tiếp đến an toàn, nhưng người ta có thể khẳng định nó có thể ngăn chặn một lỗi trong bản sửa đổi trong tương lai. Mặt khác, rất dễ quên rằng *. Làm thế nào về làm cho nó một loại, nó chắc chắn là sạch hơn.

typedef struct node 
{ 
    int a; 
    struct node * next; 
} node_t; 

Sau đó, bạn có thể làm điều này:

node_t *p = malloc(sizeof(node_t));