2011-09-01 23 views
6

Có cách nào tốt đẹp để kết hợp các trình khởi tạo được chỉ định từ C99, với kết quả là malloc không?Kết hợp các trình khởi tạo được chỉ định và malloc trong C99 +?

Sau đây dường như có sự trùng lặp không cần thiết:

typedef struct { 
    int a, b, c; 
} Type; 

Type *t = malloc(sizeof *t); 
*t = (Type) { 
    .a = 2, 
    .b = 3, 
    .c = 5, 
}; 

việc sử dụng Type, và *t có thể được gỡ bỏ từ các mã trên?

+3

Nếu có thể, điều gì sẽ xảy ra nếu 'malloc' trả về' NULL'? – detly

+0

Có vẻ như bạn thực sự muốn có C++. –

+0

Câu hỏi thú vị, nhưng tôi không nghĩ có câu trả lời hay. Cá nhân tôi sẽ chỉ sử dụng 'calloc' theo sau bởi' t-> a = 2; t-> b = 3; '... (' calloc' là chỉ có trong trường hợp bạn muốn để lại ra bất kỳ thành viên, vì vậy nếu bạn biết bạn sẽ rõ ràng đặt tất cả, bạn có thể sử dụng 'malloc' chỉ là tốt) –

Trả lời

5

Vì bạn đã hỏi;) có một công cụ trong C để tránh trùng lặp rõ ràng mã, macro. Điều đó nói rằng tôi không thấy một cách nào để không lặp lại ít nhất là tên của loại. Nhưng trong C++ họ không thể một trong hai, do đó, C là ít nhất là tốt :)

Các dễ nhất tôi thấy là

#define DESIGNATE_NEW(T)   \ 
    memcpy(malloc(sizeof(T)),   \ 
     &(T const){ __VA_ARGS__ }, \ 
     sizeof(T)) 

mà sẽ cung cấp cho

Type *t = DESIGNATE_NEW(Type, 
    .a = 2, 
    .b = 3, 
    .c = 5, 
); 

này có một số lợi thế.

  • Nó khởi tạo tất cả các thành viên một cách chính xác, ngay cả trên kiến ​​trúc với không đại diện chuẩn của 0 với nhiều loại phao hoặc con trỏ.
  • Khác với phiên bản của Keith, nó là "kiểu mã hóa" có thể chấp nhận được vì nó chỉ là một biểu thức giống như khởi tạo và bất kỳ ai nên chụp ngay lập tức những gì mã snipset thứ hai được cho là phải làm.

NB: Quan sát const trong macro, điều này cho phép một số trường hợp của hợp chất chữ được gấp lại, nếu trình biên dịch quyết định điều này là có liên quan. Ngoài ra còn có phương tiện để có một biến thể trong đó danh sách các nhà thiết kế là tùy chọn, xem P99 bên dưới.

Bất lợi là memcpy và tôi sẽ hạnh phúc hơn với bài tập. Thứ hai không có kiểm tra cho sự thất bại của malloc trước khi sử dụng kết quả, nhưng người ta có thể có thể đi qua với một số weirdness để có mã thoát độc đáo.

Trong P99 Tôi đi một cách hơi khác. Ở đó chúng ta luôn luôn có một chức năng khởi tạo cho một loại, một cái gì đó giống như

inline 
Type* Type_init(Type* t, int a, int b, int c) { 
    if (t) { 
    *t = (Type const){ .a = a, .b = b, .c = c }; 
    } 
    return t; 
} 

mà theo kỳ diệu vĩ mô có thể được thực hiện để cung cấp luận cứ mặc định cho a, bc nếu họ bị bỏ qua.Sau đó, bạn có thể chỉ cần sử dụng một cái gì đó như

Type *t = P99_NEW(Type, 1, 2, 3); 

trong mã ứng dụng của bạn. Điều này là tốt hơn, vì nó tránh dereferrencing con trỏ khi cuộc gọi đến malloc không thành công. Mặt khác, việc này giới thiệu lại một đơn đặt hàng cho người khởi tạo, vì vậy không hoàn hảo.

+0

Macro 'DESIGNATE_NEW' của bạn gần nhất là giải pháp thành ngữ. –

1

Không, đó là cách duy nhất để sử dụng trình khởi tạo được chỉ định. Không có (Type) {}, trình biên dịch không biết cách xác nhận hợp lệ nội dung.

2

Bạn có thể sử dụng macro Variad. Tôi sẽ không để cho rằng đây là một ý tưởng tốt, nhưng nó hoạt động:

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

#define CREATE(type, ptr, ...) \ 
    type *ptr = malloc(sizeof *ptr); \ 
    if (ptr) *ptr = (type){__VA_ARGS__} 

int main(void) 
{ 
    typedef struct { 
     int a, b, c; 
    } Type; 
    CREATE(Type, t, .a = 2, .b = 3, .c = 5); 
    printf("t->a = %d, t->b = %d, t->c = %d\n", t->a, t->b, t->c); 
    return 0; 
} 

Lưu ý rằng tôi đã không thể sử dụng thông thường do { ... } while (0) lừa định nghĩa vĩ mô (nó sẽ tạo ra một phạm vi mới, và t sẽ không được hiển thị), vì vậy bạn phải cẩn thận về ngữ cảnh mà bạn sử dụng nó.

Cá nhân, tôi nghĩ tôi hạnh phúc hơn với sự sao chép không cần thiết.

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