Tôi có bỏ kết quả của malloc không?


2408

Trong câu hỏi này , ai đó đã đề xuất trong một nhận xét rằng tôi không nên đưa ra kết quả malloc, tức là

int *sieve = malloc(sizeof(int) * length);

thay vì:

int *sieve = (int *) malloc(sizeof(int) * length);

Tại sao nó lại là vấn đề?


222
Ngoài ra, có thể duy trì nhiều hơn để viết sàng = malloc (sizeof * s sàng * length);
William Pursell


3
Các câu trả lời ở đây là một mỏ của một phía. Câu trả lơi con phụ thuộc vao nhiêu thư". Đó không phải là một điều cần thiết để làm cho chương trình hoạt động. Tuy nhiên, một số tiêu chuẩn mã hóa yêu cầu nó ... Ví dụ: xem Tiêu chuẩn mã hóa CERT
Tiến sĩ Person Person II

Kỳ lạ thay, mọi người đồng ý rằng bạn không bỏ vai NULL. (đó có thể là lý do tại sao C ++ được giới thiệu nullptr: C ++ không cho phép bất kỳ phôi con trỏ ẩn nào)
Sapphire_Brick

Bạn có thể, nhưng bạn không cần. Nhưng bạn cần phải có trong C ++.
dùng12211554

Câu trả lời:


2217

Không ; bạn không bỏ kết quả, vì:

  • Nó là không cần thiết, như void *được tự động và được quảng bá an toàn cho bất kỳ loại con trỏ nào khác trong trường hợp này.
  • Nó thêm lộn xộn vào mã, phôi không dễ đọc lắm (đặc biệt nếu loại con trỏ dài).
  • Nó làm cho bạn lặp lại chính mình, mà nói chung là xấu.
  • Nó có thể ẩn một lỗi nếu bạn quên bao gồm <stdlib.h>. Điều này có thể gây ra sự cố (hoặc tệ hơn là không gây ra sự cố cho đến khi về sau trong một số phần hoàn toàn khác của mã). Xem xét những gì xảy ra nếu con trỏ và số nguyên có kích thước khác nhau; sau đó bạn đang ẩn một cảnh báo bằng cách truyền và có thể mất bit của địa chỉ trả lại của bạn. Lưu ý: kể từ C99, các hàm ẩn đã biến mất khỏi C và điểm này không còn phù hợp nữa vì không có giả định tự động rằng các hàm không được khai báo sẽ trả về int.

Để làm rõ, lưu ý rằng tôi đã nói "bạn không được chọn", không phải "bạn không cần phải phân vai". Theo tôi, đó là một thất bại trong việc bao gồm các diễn viên, ngay cả khi bạn đã làm đúng. Đơn giản là không có lợi ích gì khi thực hiện nó, nhưng một loạt các rủi ro tiềm ẩn và bao gồm cả dàn diễn viên cho thấy bạn không biết về các rủi ro.

Cũng lưu ý, như các nhà bình luận chỉ ra, những điều trên nói về C thẳng chứ không phải C ++. Tôi rất tin tưởng vào C và C ++ như các ngôn ngữ riêng biệt.

Để thêm nữa, mã của bạn không cần lặp lại thông tin loại ( int) có thể gây ra lỗi. Tốt hơn là nên tham chiếu lại con trỏ đang được sử dụng để lưu trữ giá trị trả về, để "khóa" hai cái lại với nhau:

int *sieve = malloc(length * sizeof *sieve);

Điều này cũng di chuyển ra lengthphía trước để tăng khả năng hiển thị và giảm các dấu ngoặc thừa sizeof; chúng chỉ cần thiết khi đối số là tên loại. Nhiều người dường như không biết (hoặc bỏ qua) điều này, điều này làm cho mã của họ dài dòng hơn. Hãy nhớ rằng: sizeofkhông phải là một chức năng! :)


Mặc dù di chuyển lengthra phía trước có thể làm tăng khả năng hiển thị trong một số trường hợp hiếm gặp, người ta cũng nên chú ý rằng trong trường hợp chung, tốt hơn nên viết biểu thức như sau:

int *sieve = malloc(sizeof *sieve * length);

Kể từ khi giữ sizeofđầu tiên, trong trường hợp này, đảm bảo phép nhân được thực hiện với ít nhất là size_ttoán học.

So sánh: malloc(sizeof *sieve * length * width)so với malloc(length * width * sizeof *sieve)lần thứ hai có thể tràn length * widthkhi widthlengthlà loại nhỏ hơn size_t.


21
Hãy xem xét cập nhật câu trả lời. Dàn diễn viên không còn nguy hiểm nữa và việc lặp đi lặp lại bản thân không nhất thiết là điều xấu (dự phòng có thể giúp bắt lỗi).
n. 'đại từ' m.

11
Trình biên dịch đã thay đổi. Một trình biên dịch cập nhật sẽ cảnh báo bạn về một tuyên bố bị thiếu của malloc.
n. 'đại từ' m.

55
@nm Ok. Tôi nghĩ thật tệ khi cho rằng bất cứ ai đọc ở đây đều có một trình biên dịch cụ thể. Ngoài ra, vì C11, toàn bộ khái niệm "hàm ẩn" đã biến mất, tôi không biết điều đó. Tuy nhiên, tôi không thấy điểm nào trong việc thêm một dàn diễn viên vô nghĩa. Bạn cũng làm int x = (int) 12;chỉ để làm cho mọi thứ rõ ràng?
thư giãn

22
@nm nếu sử dụng một cách rõ ràng một con trỏ trống "đã giúp" giải quyết một lỗi, rất có thể bạn đã gặp phải hành vi không xác định, điều đó có nghĩa là chương trình đang được đề cập có khả năng là một lỗi tồi tệ hơn, chưa được phát hiện mà bạn chưa gặp phải. Và một ngày nọ, vào một buổi tối mùa đông lạnh lẽo, bạn sẽ đi làm về và thấy trang GitHub của bạn tràn ngập các báo cáo vấn đề phàn nàn về việc quỷ bay ra khỏi mũi của người dùng
Braden Best

12
@unwind Thậm chí tôi đồng ý với bạn, (int)12không thể so sánh được. 12 một int, các diễn viên không làm gì đơn giản. Retval của malloc()void *, không phải loại con trỏ đúc. (Nếu không phải void *vậy. Vì vậy, sự tương tự (int)12sẽ là (void*)malloc(…)điều không ai đang thảo luận.)
Amin Negm-Awad

376

Trong C, bạn không cần bỏ giá trị trả về malloc. Con trỏ đến void được trả về mallocđược tự động chuyển đổi thành đúng loại. Tuy nhiên, nếu bạn muốn mã của mình biên dịch bằng trình biên dịch C ++, thì cần phải sử dụng cast. Một lựa chọn ưu tiên trong cộng đồng là sử dụng như sau:

int *sieve = malloc(sizeof *sieve * length);

điều này cũng giải phóng bạn khỏi phải lo lắng về việc thay đổi phía bên phải của biểu thức nếu bạn thay đổi kiểu sieve.

Diễn viên là xấu, như mọi người đã chỉ ra. Đặc biệt là phôi con trỏ.


71
@MAKZ Tôi cho rằng malloc(length * sizeof *sieve)làm cho nó trông giống như sizeofmột biến - vì vậy tôi nghĩ malloc(length * sizeof(*sieve))là dễ đọc hơn.
Michael Anderson

21
malloc(length * (sizeof *sieve))nhiều hơn nữa vẫn có thể đọc được. IMHO.
Toby Speight

19
@Michael Anderson đặt ()vấn đề sang một bên, lưu ý rằng phong cách được đề xuất của bạn đã chuyển thứ tự., Hãy xem xét khi số lượng phần tử được tính như thế nào length*width, giữ nguyên sizeofđầu tiên trong trường hợp này đảm bảo phép nhân được thực hiện với ít nhất là size_ttoán học. So sánh malloc(sizeof( *ptr) * length * width)với malloc(length * width * sizeof (*ptr))- thứ 2 có thể tràn length*widthkhi width,lengthcác loại nhỏ hơn size_t.
chux - Phục hồi Monica

3
@chux không rõ ràng, nhưng câu trả lời đã được chỉnh sửa để nhận xét của tôi ít phù hợp hơn - đề xuất ban đầu làmalloc(sizeof *sieve * length)
Michael Anderson

12
C không phải là C ++. Giả vờ rằng họ cuối cùng sẽ dẫn đến sự nhầm lẫn và buồn bã. Nếu bạn đang sử dụng C ++, thì việc sử dụng kiểu C cũng rất tệ (trừ khi bạn đang sử dụng trình biên dịch C ++ rất cũ). Và static_cast>()(hoặc reinterpret_cast<>()) không tương thích với bất kỳ phương ngữ nào của C.
David C.

349

Bạn làm diễn viên, bởi vì:

  • Nó làm cho mã của bạn dễ di chuyển hơn giữa C và C ++, và như kinh nghiệm SO cho thấy, rất nhiều lập trình viên tuyên bố họ đang viết bằng C khi họ thực sự viết bằng C ++ (hoặc C cộng với các phần mở rộng trình biên dịch cục bộ).
  • Không làm như vậy có thể ẩn một lỗi : lưu ý tất cả các ví dụ SO gây nhầm lẫn khi viết type *so với type **.
  • Ý tưởng rằng nó khiến bạn không nhận ra bạn đã thất bại với #includemột tệp tiêu đề thích hợp bỏ lỡ rừng cho cây . Nó cũng giống như câu nói "đừng lo lắng về thực tế bạn đã thất bại khi yêu cầu trình biên dịch phàn nàn về việc không nhìn thấy các nguyên mẫu - rằng stdlib pesky đó là điều quan trọng THỰC SỰ cần nhớ!"
  • Nó buộc phải kiểm tra chéo nhận thức thêm . Nó đặt loại mong muốn (bị cáo buộc) ngay bên cạnh số học bạn đang làm cho kích thước thô của biến đó. Tôi cá là bạn có thể thực hiện một nghiên cứu SO chỉ ra rằng malloc()bọ bị bắt nhanh hơn nhiều khi có diễn viên. Như với các xác nhận, các chú thích tiết lộ ý định giảm lỗi.
  • Lặp đi lặp lại bản thân theo cách mà máy có thể kiểm tra thường là một ý tưởng tuyệt vời . Trên thực tế, đó là những gì một khẳng định là, và việc sử dụng diễn viên này là một khẳng định. Các xác nhận vẫn là kỹ thuật chung nhất mà chúng ta có để lấy mã chính xác, vì Turing đã đưa ra ý tưởng từ nhiều năm trước.

39
@ulidtko Trong trường hợp bạn không biết, có thể viết mã biên dịch cả C và C ++. Trong thực tế, hầu hết các tệp tiêu đề đều như thế này và chúng thường chứa mã (macro và hàm nội tuyến). Có một tệp .c/ .cppđể biên dịch vì cả hai không thường xuyên hữu ích, nhưng một trường hợp là thêm throwhỗ trợ C ++ khi được biên dịch bằng trình biên dịch C ++ (nhưng return -1;khi được biên dịch bằng trình biên dịch C, hoặc bất cứ điều gì).
hyde

37
Nếu ai đó có malloc gọi nội tuyến trong tiêu đề, tôi sẽ không ấn tượng, #ifdef __cplusplus và extern "C" {} dành cho công việc này, không thêm vào các diễn viên phụ.
paulm

15
Chà, điểm 1 không liên quan, vì C! = C ++, các điểm khác cũng không đáng kể, nếu bạn sử dụng biến trong malloccuộc gọi của mình : char **foo = malloc(3*sizeof(*foo));nếu khá đầy đủ: 3 con trỏ cho con trỏ char. sau đó lặp và làm foo[i] = calloc(101, sizeof(*(foo[i])));. Phân bổ mảng 101 ký tự, khởi tạo gọn gàng thành số không. Không cần diễn viên. thay đổi khai báo thành unsigned charhoặc bất kỳ loại nào khác, cho vấn đề đó, và bạn vẫn tốt
Elias Van Ootegem 23/12/13

34
Khi tôi nghĩ rằng tôi đã nhận nó, nó sẽ đến! Câu trả lời tuyệt vời. Đây là lần đầu tiên ở đây trong StackOverflow mà tôi +1 hai câu trả lời ngược nhau! +1 Không, bạn không chọn và +1 Có, bạn thực hiện! CƯỜI LỚN. Các bạn thật tuyệt vời. Và đối với tôi và các sinh viên của mình, tôi đã quyết định: tôi tham gia diễn xuất. Các loại lỗi sinh viên mắc phải dễ dàng phát hiện hơn khi đúc.
Bác sĩ Beco

15
@Leushenko: Lặp đi lặp lại bản thân theo cách không thể xác thực bằng máy cũng như kiểm tra tại địa phương là không tốt. Lặp đi lặp lại bản thân theo những cách có thể được xác nhận bằng các phương tiện như vậy sẽ ít tệ hơn. Do đó struct Zebra *p; ... p=malloc(sizeof struct Zebra);, malloc không thể tránh việc sao chép thông tin về loại p, nhưng cả trình biên dịch lẫn kiểm tra mã cục bộ sẽ không phát hiện bất kỳ vấn đề nào nếu một loại thay đổi nhưng loại kia thì không. Thay đổi mã thành p=(struct Zebra*)malloc(sizeof struct Zebra);và trình biên dịch sẽ squawk nếu loại cast không khớp pvà kiểm tra cục bộ sẽ tiết lộ ...
supercat

170

Như những người khác đã nêu, nó không cần thiết cho C, nhưng cần thiết cho C ++. Nếu bạn nghĩ rằng bạn sẽ biên dịch mã C của mình bằng trình biên dịch C ++, vì bất kỳ lý do gì, bạn có thể sử dụng macro thay thế, như:

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

Bằng cách đó bạn vẫn có thể viết nó một cách rất nhỏ gọn:

int *sieve = NEW(int, 1);

và nó sẽ biên dịch cho C và C ++.


17
Vì dù sao bạn cũng đang sử dụng macro, tại sao bạn không sử dụng newđịnh nghĩa về C ++?
Hosam Aly

63
Bởi vì không có lý do để làm như vậy. Nó chủ yếu dành cho các chương trình C được biên dịch bằng trình biên dịch C ++. Nếu bạn định sử dụng 'mới', điều duy nhất bạn nhận được là các vấn đề. Bạn cần một macro miễn phí. Và bạn cần một macro để giải phóng một mảng, một sự khác biệt không tồn tại trong C.
quinmars

8
Chưa kể nếu không phải bạn là người giải phóng bộ nhớ mà có thể là thư viện C bạn đang sử dụng, v.v. Nhiều vấn đề có thể xảy ra mà không có lợi ích gì.
quinmars

86
@Hosam: Vâng, chắc chắn là như vậy. Nếu bạn sử dụng, newbạn phải sử dụng deletevà nếu bạn sử dụng, malloc()bạn phải bạn free(). Không bao giờ trộn chúng.
Graeme Perrow

17
Nếu ai đó sẽ thực hiện phương pháp này, gọi macro NEWcó lẽ là một ý tưởng tồi vì tài nguyên không bao giờ được trả lại bằng cách sử dụng delete(hoặc DELETE) vì vậy bạn đang trộn từ vựng của mình. Thay vào đó, đặt tên cho nó MALLOC, hay đúng hơn là CALLOCtrong trường hợp này, sẽ có ý nghĩa hơn.
mah

139

Từ Wikipedia :

Ưu điểm để đúc

  • Bao gồm các diễn viên có thể cho phép một chương trình hoặc chức năng C biên dịch thành C ++.

  • Dàn diễn viên cho phép các phiên bản trước của 1989 của malloc ban đầu trả về char *.

  • Việc truyền có thể giúp nhà phát triển xác định sự không nhất quán trong việc định cỡ kiểu khi loại con trỏ đích thay đổi, đặc biệt nếu con trỏ được khai báo cách xa lệnh gọi malloc () (mặc dù trình biên dịch hiện đại và máy phân tích tĩnh có thể cảnh báo hành vi đó mà không yêu cầu ép kiểu).

Nhược điểm để đúc

  • Theo tiêu chuẩn ANSI C, dàn diễn viên là dự phòng.

  • Thêm diễn viên có thể che dấu thất bại để bao gồm tiêu đề stdlib.h, trong đó nguyên mẫu cho malloc được tìm thấy. Trong trường hợp không có nguyên mẫu cho malloc, tiêu chuẩn yêu cầu trình biên dịch C giả sử malloc trả về một int. Nếu không có cast, một cảnh báo được đưa ra khi số nguyên này được gán cho con trỏ; tuy nhiên, với các diễn viên, cảnh báo này không được tạo ra, ẩn một lỗi. Trên một số kiến ​​trúc và mô hình dữ liệu nhất định (chẳng hạn như LP64 trên các hệ thống 64 bit, trong đó các con trỏ dài và 64 bit và int là 32 bit), lỗi này thực sự có thể dẫn đến hành vi không xác định, vì malloc được khai báo ngầm trả về 32- giá trị bit trong khi hàm thực sự được xác định trả về giá trị 64 bit. Tùy thuộc vào các quy ước gọi và bố trí bộ nhớ, điều này có thể dẫn đến phá vỡ ngăn xếp. Vấn đề này ít có khả năng không được chú ý trong các trình biên dịch hiện đại, khi chúng thống nhất đưa ra các cảnh báo rằng một chức năng chưa được khai báo đã được sử dụng, do đó, một cảnh báo sẽ vẫn xuất hiện. Ví dụ, hành vi mặc định của GCC là hiển thị cảnh báo có nội dung "khai báo ngầm định không tương thích của chức năng tích hợp" bất kể diễn viên có mặt hay không.

  • Nếu loại con trỏ bị thay đổi khi khai báo, người ta cũng có thể, cần thay đổi tất cả các dòng nơi malloc được gọi và truyền.

Mặc dù malloc mà không cần đúc là phương pháp ưa thích và hầu hết các lập trình viên có kinh nghiệm đều chọn nó , bạn nên sử dụng bất cứ điều gì bạn muốn khi biết về các vấn đề.

tức là: Nếu bạn cần biên dịch chương trình C thành C ++ (Mặc dù đó là một ngôn ngữ riêng), bạn phải bỏ kết quả sử dụng malloc.


1
" Đúc có thể giúp nhà phát triển xác định sự không nhất quán trong việc định cỡ kiểu khi loại con trỏ đích thay đổi, đặc biệt nếu con trỏ được khai báo cách xa malloc()cuộc gọi " nghĩa là gì? Bạn có thể cho một ví dụ?
Spikatrix

3
@CoolGuy: Xem bình luận trước đó về câu trả lời khác . Nhưng lưu ý rằng p = malloc(sizeof(*p) * count)thành ngữ chọn tự động thay đổi loại, vì vậy bạn không phải nhận cảnh báo và thay đổi bất cứ điều gì. Vì vậy, đây không phải là một lợi thế thực sự so với giải pháp thay thế tốt nhất cho việc không đúc.
Peter Cordes

7
Đây là câu trả lời thích hợp: Có những ưu và nhược điểm, và nó tập trung vào vấn đề sở thích (trừ khi mã phải được biên dịch thành C ++ - thì việc chọn diễn viên là bắt buộc).
Peter - Tái lập lại

3
Điểm 3 là moot, vì Nếu loại con trỏ bị thay đổi khi khai báo, người ta nên kiểm tra mọi trường hợp của malloc, realloc và tự do xâm nhập vào loại đó. Đúc sẽ buộc bạn phải làm điều đó.
Michaël Roy

104

Trong C, bạn có thể chuyển đổi một voidcon trỏ thành bất kỳ loại con trỏ nào khác, do đó không cần thiết phải sử dụng. Sử dụng một người có thể gợi ý cho người quan sát thông thường rằng có một số lý do tại sao một người cần thiết, có thể gây hiểu nhầm.


100

Bạn không bỏ kết quả của malloc, vì làm như vậy sẽ thêm sự lộn xộn vô nghĩa vào mã của bạn.

Lý do phổ biến nhất khiến mọi người bỏ kết quả của malloc là vì họ không chắc chắn về cách thức hoạt động của ngôn ngữ C. Đó là một dấu hiệu cảnh báo: nếu bạn không biết cơ chế ngôn ngữ cụ thể hoạt động như thế nào, thì đừng đoán. Nhìn lên hoặc hỏi về Stack Overflow.

Một vài bình luận:

  • Một con trỏ void có thể được chuyển đổi thành / từ bất kỳ loại con trỏ nào khác mà không cần truyền rõ ràng (C11 6.3.2.3 và 6.5.16.1).

  • Tuy nhiên, C ++ sẽ không cho phép một kiểu ẩn giữa void*và một loại con trỏ khác. Vì vậy, trong C ++, dàn diễn viên đã đúng. Nhưng nếu bạn lập trình trong C ++, bạn nên sử dụng newchứ không phải malloc (). Và bạn không bao giờ nên biên dịch mã C bằng trình biên dịch C ++.

    Nếu bạn cần hỗ trợ cả C và C ++ với cùng một mã nguồn, hãy sử dụng các trình chuyển đổi trình biên dịch để đánh dấu sự khác biệt. Không tìm cách sắp xếp cả hai tiêu chuẩn ngôn ngữ với cùng một mã, vì chúng không tương thích.

  • Nếu trình biên dịch C không thể tìm thấy hàm vì bạn quên bao gồm tiêu đề, bạn sẽ gặp lỗi trình biên dịch / trình liên kết về điều đó. Vì vậy, nếu bạn quên bao gồm <stdlib.h>điều đó không có gì to tát, bạn sẽ không thể xây dựng chương trình của mình.

  • Trên các trình biên dịch cổ đại tuân theo một phiên bản của tiêu chuẩn hơn 25 năm tuổi, việc quên đưa vào <stdlib.h>sẽ dẫn đến hành vi nguy hiểm. Bởi vì trong tiêu chuẩn cổ xưa đó, các hàm không có nguyên mẫu có thể nhìn thấy đã chuyển đổi kiểu trả về thành int. Đúc kết quả từ malloc rõ ràng sau đó sẽ che giấu lỗi này.

    Nhưng đó thực sự là một vấn đề không. Bạn không sử dụng máy tính 25 tuổi, vậy tại sao bạn lại sử dụng trình biên dịch 25 tuổi?


9
"Sự lộn xộn vô nghĩa" là sự cường điệu hóa có xu hướng làm hỏng mọi khả năng thuyết phục bất cứ ai chưa đồng ý với bạn. Một diễn viên chắc chắn không phải là vô nghĩa; Câu trả lời của Ron Burk và Kaz đưa ra những tranh luận ủng hộ việc tuyển chọn mà tôi rất đồng ý. Cho dù những mối quan tâm đó nặng hơn những mối quan tâm bạn đề cập là một câu hỏi hợp lý để hỏi. Đối với tôi, mối quan tâm của bạn trông tương đối nhỏ so với họ.
Don nở

"Một con trỏ void có thể được chuyển đổi thành / từ bất kỳ loại con trỏ nào khác mà không cần truyền rõ ràng" không được hỗ trợ bởi 6.3.2.3. Có lẽ bạn đang nghĩ về "con trỏ tới bất kỳ loại đối tượng"? "Con trỏ trống" và "con trỏ tới một hàm" không dễ chuyển đổi.
chux - Tái lập Monica

Quả thực tài liệu tham khảo không đầy đủ. Phần có liên quan cho "nhân chứng" là quy tắc chuyển nhượng đơn giản 6.5.16.1. "Một toán hạng là một con trỏ tới một loại đối tượng và một toán tử khác là một con trỏ tới một phiên bản void đủ điều kiện hoặc không đủ tiêu chuẩn". Tôi đã thêm tài liệu tham khảo này vào câu trả lời cho đầy đủ.
Lundin

91

Trong C, bạn nhận được một chuyển đổi ngầm định từ void *bất kỳ con trỏ (dữ liệu) nào khác.


6
@Jens: OK, có thể từ ngữ thích hợp hơn là "chuyển đổi ngầm". Giống như sử dụng biến tích phân trong biểu thức dấu phẩy động.
EFraim

@EFraim Điều đó thực sự sẽ dẫn đến một dàn diễn viên, và một điều tiềm ẩn ở đó.
Nhà vật lý điên

71

malloc()Hiện tại, việc truyền giá trị được trả về là không cần thiết, nhưng tôi muốn thêm một điểm mà dường như không ai chỉ ra:

Vào thời cổ đại, nghĩa là, trước khi ANSI C cung cấp void *loại con trỏ chung, char *là loại cho việc sử dụng đó. Trong trường hợp đó, các diễn viên có thể tắt các cảnh báo của trình biên dịch.

Tham khảo: C FAQ


2
Tắt cảnh báo trình biên dịch là một ý tưởng tồi.
Albert van der Horst

8
@AlbertvanderHorst Không phải nếu bạn làm như vậy bằng cách giải quyết vấn đề chính xác thì cảnh báo là có để cảnh báo bạn.
Dan Bechard

@Dan. Nếu bằng cách giải quyết vấn đề chính xác có nghĩa là viết lại chương trình con để trả về các loại ANSI C hiện đại thay vì char *, tôi đồng ý. Tôi sẽ không gọi đó là tắt trình biên dịch. Đừng nhượng bộ những người quản lý khăng khăng rằng không có cảnh báo về trình biên dịch, thay vì sử dụng chúng cho mỗi lần biên dịch lại để tìm các vấn đề có thể xảy ra. Groetjes Albert
Albert van der Horst

53

Chỉ cần thêm kinh nghiệm của tôi, nghiên cứu về kỹ thuật máy tính, tôi thấy rằng hai hoặc ba giáo sư mà tôi đã thấy viết bằng C luôn đúc malloc, tuy nhiên người tôi đã hỏi (với một CV lớn và hiểu biết về C) nói với tôi rằng nó hoàn toàn không cần thiết nhưng chỉ được sử dụng để hoàn toàn cụ thể, và để có được các sinh viên vào tâm lý là hoàn toàn cụ thể. Về cơ bản, việc truyền sẽ không thay đổi bất cứ điều gì trong cách hoạt động của nó, nó thực hiện chính xác những gì nó nói, phân bổ bộ nhớ và truyền không ảnh hưởng đến nó, bạn có cùng bộ nhớ và ngay cả khi bạn nhầm lẫn với một thứ khác (và bằng cách nào đó trốn tránh trình biên dịch lỗi) C sẽ truy cập nó theo cùng một cách.

Chỉnh sửa: Đúc có một điểm nhất định. Khi bạn sử dụng ký hiệu mảng, mã được tạo phải biết có bao nhiêu bộ nhớ phải tăng để đạt đến điểm bắt đầu của phần tử tiếp theo, điều này đạt được thông qua việc truyền. Bằng cách này, bạn biết rằng đối với một nhân đôi, bạn đi trước 8 byte trong khi đối với một int bạn đi 4, v.v. Do đó, nó không có tác dụng nếu bạn sử dụng ký hiệu con trỏ, trong ký hiệu mảng, nó trở nên cần thiết.


3
Ngoại trừ như đã đề cập, các diễn viên có thể ẩn các lỗi và làm cho mã khó phân tích hơn cho trình biên dịch hoặc trình phân tích tĩnh.
Lundin

2
"Về cơ bản, việc đúc sẽ không thay đổi bất cứ điều gì trong cách hoạt động". Truyền tới loại kết hợp không nên thay đổi bất cứ điều gì, nhưng nếu loại var thay đổi và diễn viên không còn phù hợp, vấn đề có thể xảy ra? Các IWO, kiểu cast và var nên được giữ đồng bộ - gấp đôi công việc bảo trì.
chux - Tái lập lại

Tôi có thể thấy lý do tại sao giáo sư thích đúc. Việc truyền có thể hữu ích từ quan điểm giáo dục nơi truyền đạt thông tin của người hướng dẫn và mã sinh viên không cần phải duy trì - mã vứt bỏ của nó. Tuy nhiên, từ góc độ mã hóa, đánh giá ngang hàng và bảo trì , p = malloc(sizeof *p * n);rất đơn giản và tốt hơn.
chux - Tái lập Monica

53

Không bắt buộc phải truyền kết quả malloc, vì nó trả về void*void*có thể được trỏ đến bất kỳ kiểu dữ liệu nào.


34

Một con trỏ void là một con trỏ đối tượng chung và C hỗ trợ chuyển đổi ngầm định từ một loại con trỏ void sang các loại khác, do đó không cần phải đánh máy một cách rõ ràng.

Tuy nhiên, nếu bạn muốn cùng một mã hoạt động hoàn toàn tương thích trên nền tảng C ++, không hỗ trợ chuyển đổi ngầm định, bạn cần thực hiện kiểu chữ, vì vậy tất cả phụ thuộc vào khả năng sử dụng.


2
Đây không phải là trường hợp sử dụng thông thường để biên dịch một nguồn duy nhất là cả C và C ++ (trái ngược với việc sử dụng tệp tiêu đề chứa khai báo để liên kết mã C và C ++ với nhau). Sử dụng mallocvà bạn bè trong C ++ là một dấu hiệu cảnh báo tốt cho thấy nó đáng được chú ý đặc biệt (hoặc viết lại bằng C).
Toby Speight

1
"Con trỏ void là con trỏ chung" -> "Con trỏ void là con trỏ đối tượng chung ". Kích thước con trỏ hàm có thể vượt quá void *, do đó void *không đủ để lưu trữ con trỏ hàm tốt.
chux - Phục hồi Monica

Ý định của tôi về dòng đó là như vậy nhưng dù sao cũng cảm ơn @chux đã gợi ý.
Endeavour

33

Đây là những gì Hướng dẫn tham khảo thư viện GNU C nói:

Bạn có thể lưu trữ kết quả mallocvào bất kỳ biến con trỏ nào mà không cần truyền, vì ISO C tự động chuyển đổi loại void *thành loại con trỏ khác khi cần thiết. Nhưng dàn diễn viên là cần thiết trong các ngữ cảnh khác với toán tử gán hoặc nếu bạn có thể muốn mã của mình chạy trong C. truyền thống.

Và thực sự, tiêu chuẩn ISO C11 (p347) đã nói như vậy:

Con trỏ được trả về nếu phân bổ thành công được căn chỉnh phù hợp để nó có thể được gán cho một con trỏ tới bất kỳ loại đối tượng nào có yêu cầu căn chỉnh cơ bản và sau đó được sử dụng để truy cập vào một đối tượng hoặc một mảng các đối tượng đó trong không gian được phân bổ (cho đến khi không gian được giải quyết rõ ràng)


31

Kiểu được trả về là void *, có thể được truyền tới loại con trỏ dữ liệu mong muốn để có thể điều chỉnh được.


1
void* có thể được chuyển sang loại mong muốn, nhưng không cần phải làm như vậy vì nó sẽ được tự động chuyển đổi. Vì vậy, dàn diễn viên là không cần thiết, và trên thực tế là không mong muốn vì những lý do được đề cập trong các câu trả lời có điểm cao.
Toby Speight

nhưng chỉ khi bạn cần phải hủy đăng ký "một cách nhanh chóng", nếu bạn tạo một biến thay vào đó, nó sẽ được chuyển đổi một cách an toàn và tự động thành loại hiệu quả của biến, mà không cần truyền (trong C).
Ferrarezi

28

Trong ngôn ngữ C, một con trỏ void có thể được gán cho bất kỳ con trỏ nào, đó là lý do tại sao bạn không nên sử dụng kiểu đúc. Nếu bạn muốn phân bổ "loại an toàn", tôi có thể đề xuất các hàm macro sau, mà tôi luôn sử dụng trong các dự án C của mình:

#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

Với những thứ này, bạn có thể nói một cách đơn giản

NEW_ARRAY(sieve, length);

Đối với mảng không động, macro hàm phải có thứ ba là

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

mà làm cho các vòng lặp mảng an toàn hơn và thuận tiện hơn:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}

"một con trỏ void có thể được gán cho bất kỳ con trỏ đối tượng nào " Các con trỏ hàm là một vấn đề khác, mặc dù không phải là malloc()một.
chux - Tái lập Monica

Việc gán một void*đến / từ một con trỏ hàm có thể làm mất thông tin vì vậy "một con trỏ trống có thể được gán cho bất kỳ con trỏ nào", là một vấn đề trong những trường hợp đó. Việc chỉ định a void*, từ malloc() bất kỳ con trỏ đối tượng nào không phải là một vấn đề.
chux - Tái lập Monica

Các dovòng lặp bình luận liên quan đến macro liên quan đến một vòng lặp chưa được tự hỏi đi từ câu hỏi tiêu đề. Xóa bình luận đó. Sẽ đưa cái này xuống sau.
chux - Phục hồi Monica

27

Nó phụ thuộc vào ngôn ngữ lập trình và trình biên dịch. Nếu bạn sử dụng malloctrong C, không cần phải nhập cast, vì nó sẽ tự động gõ cast. Tuy nhiên, nếu bạn đang sử dụng C ++, thì bạn nên gõ cast vì mallocsẽ trả về một void*kiểu.


1
Hàm malloc cũng trả về một con trỏ void trong C nhưng các quy tắc của ngôn ngữ khác với C ++.
Tháng Tám Karlstrom

16

Mọi người đã từng sử dụng GCC và Clang đều hư hỏng. Đó không phải là tất cả những gì tốt đẹp ngoài đó.

Tôi đã khá kinh hoàng trong những năm qua bởi các trình biên dịch có độ tuổi đáng kinh ngạc mà tôi được yêu cầu sử dụng. Thông thường các công ty và nhà quản lý áp dụng cách tiếp cận cực kỳ bảo thủ để thay đổi trình biên dịch và thậm chí sẽ không kiểm tra xem trình biên dịch mới (có tuân thủ tiêu chuẩn và tối ưu hóa mã tốt hơn) sẽ hoạt động trong hệ thống của họ hay không. Thực tế thực tế cho các nhà phát triển đang làm việc là khi bạn viết mã, bạn cần phải bao gồm các cơ sở của mình và thật không may, việc chọn mallocs là một thói quen tốt nếu bạn không thể kiểm soát trình biên dịch nào có thể được áp dụng cho mã của mình.

Tôi cũng sẽ đề nghị rằng nhiều tổ chức áp dụng một tiêu chuẩn mã hóa của riêng họ và đó phải là phương pháp mà mọi người tuân theo nếu nó được xác định. Trong trường hợp không có hướng dẫn rõ ràng, tôi có xu hướng tìm cách biên dịch ở khắp mọi nơi, thay vì tuân thủ một tiêu chuẩn.

Lập luận rằng nó không cần thiết theo các tiêu chuẩn hiện tại là khá hợp lệ. Nhưng lập luận đó bỏ qua thực tiễn của thế giới thực. Chúng tôi không viết mã trong một thế giới được cai trị độc quyền theo tiêu chuẩn trong ngày, mà bởi tính thực tiễn của cái mà tôi muốn gọi là "lĩnh vực thực tế của quản lý địa phương". Và đó là uốn cong và xoắn hơn thời gian không gian từng có. :-)

YMMV.

Tôi có xu hướng nghĩ về việc đúc malloc như một hoạt động phòng thủ. Không xinh, không hoàn hảo, nhưng nói chung là an toàn. (Thành thật mà nói, nếu bạn đã không bao gồm stdlib.h sau đó bạn đã cách nhiều vấn đề hơn so với đúc malloc!).


15

Tôi đưa vào dàn diễn viên chỉ đơn giản là thể hiện sự không tán thành lỗ hổng xấu xí trong hệ thống loại, cho phép mã như đoạn mã sau để biên dịch mà không cần chẩn đoán, mặc dù không có phôi nào được sử dụng để mang lại chuyển đổi xấu:

double d;
void *p = &d;
int *q = p;

Tôi ước điều đó không tồn tại (và nó không có trong C ++) và vì vậy tôi đã chọn. Nó đại diện cho sở thích của tôi, và chính trị lập trình của tôi. Tôi không chỉ đúc một con trỏ, mà còn hiệu quả, bỏ phiếu, và đuổi quỷ ngu ngốc . Nếu tôi thực sự không thể thể hiện sự ngu ngốc , thì ít nhất hãy để tôi bày tỏ mong muốn làm điều đó bằng một cử chỉ phản kháng.

Trong thực tế, một cách thực hành tốt là bọc malloc(và bạn bè) với các hàm trả về unsigned char *và về cơ bản không bao giờ sử dụng void *trong mã của bạn. Nếu bạn cần một con trỏ chung cho bất kỳ đối tượng nào, hãy sử dụng một char *hoặc unsigned char *và có các phôi theo cả hai hướng. Một thư giãn có thể được thưởng thức, có lẽ, là sử dụng các chức năng như memsetmemcpykhông có phôi.

Về chủ đề truyền và tương thích C ++, nếu bạn viết mã của mình để nó biên dịch thành cả C và C ++ (trong trường hợp đó bạn phải truyền giá trị trả về mallockhi gán nó cho một thứ khác void *), bạn có thể làm rất hữu ích điều cho bản thân bạn: bạn có thể sử dụng các macro để truyền mà dịch sang các kiểu kiểu C ++ khi biên dịch thành C ++, nhưng giảm xuống thành một biểu tượng C khi biên dịch thành C:

/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif

Nếu bạn tuân thủ các macro này, thì một greptìm kiếm đơn giản về cơ sở mã của bạn cho các mã định danh này sẽ cho bạn biết vị trí của tất cả các phôi của bạn, vì vậy bạn có thể xem lại bất kỳ trong số chúng là không chính xác.

Sau đó, về phía trước, nếu bạn thường xuyên biên dịch mã bằng C ++, nó sẽ thực thi việc sử dụng một dàn diễn viên phù hợp. Chẳng hạn, nếu bạn strip_qualchỉ sử dụng để loại bỏ một consthoặc volatile, nhưng chương trình thay đổi theo cách liên quan đến chuyển đổi loại, bạn sẽ nhận được chẩn đoán và bạn sẽ phải sử dụng kết hợp các phôi để có được chuyển đổi mong muốn.

Để giúp bạn tuân thủ các macro này, trình biên dịch GNU C ++ (không phải C!) Có một tính năng tuyệt vời: một chẩn đoán tùy chọn được tạo cho tất cả các lần xuất hiện của kiểu phôi C.

     -Wold-style-cast (chỉ C ++ và Objective-C ++)
         Cảnh báo nếu sử dụng kiểu cũ (kiểu C) sang loại không trống
         trong một chương trình C ++. Các phôi kiểu mới (Dynamic_cast,
         static_cast, reinterpret_cast và const_cast) ít bị tổn thương hơn
         để các hiệu ứng ngoài ý muốn và dễ dàng hơn để tìm kiếm.

Nếu mã C của bạn biên dịch thành C ++, bạn có thể sử dụng -Wold-style-casttùy chọn này để tìm hiểu tất cả các lần xuất hiện của (type)cú pháp truyền có thể len ​​vào mã và theo dõi các chẩn đoán này bằng cách thay thế nó bằng một lựa chọn thích hợp trong số các macro trên (hoặc một kết hợp, nếu cần thiết).

Cách xử lý chuyển đổi này là biện minh kỹ thuật độc lập lớn nhất để làm việc trong "Clean C": phương ngữ C và C ++ kết hợp, về mặt kỹ thuật biện minh cho việc đúc giá trị trả về malloc.


Như những người khác đã chỉ ra, tôi thường khuyên bạn không nên trộn mã C và C ++. Tuy nhiên, nếu bạn có lý do chính đáng để làm điều đó, thì macro có thể hữu ích.
Phil1970

@ Phil1970 Tất cả được viết bằng một phương ngữ gắn kết, có thể di chuyển đến trình biên dịch C và C ++ và tận dụng một số khả năng của C ++. Tất cả phải được biên dịch thành C ++ hoặc nếu không thì tất cả được biên dịch thành C.
Kaz

Tức là những gì tôi đã cố gắng nói trong bình luận trước đó là không có sự pha trộn giữa C và C ++. Mục đích là mã được biên dịch thành C hoặc tất cả được biên dịch dưới dạng C ++.
Kaz

15

Điều tốt nhất để làm khi lập trình trong C bất cứ khi nào có thể:

  1. Làm cho chương trình của bạn được biên dịch thông qua trình biên dịch C với tất cả các cảnh báo được bật -Wallvà sửa tất cả các lỗi và cảnh báo
  2. Đảm bảo không có biến nào được khai báo là auto
  3. Sau đó biên dịch nó bằng trình biên dịch C ++ với -Wall-std=c++11. Sửa tất cả các lỗi và cảnh báo.
  4. Bây giờ biên dịch lại bằng trình biên dịch C. Chương trình của bạn bây giờ sẽ biên dịch mà không có bất kỳ cảnh báo nào và chứa ít lỗi hơn.

Quy trình này cho phép bạn tận dụng kiểm tra loại nghiêm ngặt C ++, do đó giảm số lượng lỗi. Cụ thể, thủ tục này buộc bạn phải bao gồm stdlib.hhoặc bạn sẽ nhận được

malloc đã không được tuyên bố trong phạm vi này

và cũng buộc bạn bỏ kết quả mallochoặc bạn sẽ nhận được

chuyển đổi không hợp lệ từ void*sangT*

hoặc loại mục tiêu của bạn là gì.

Những lợi ích duy nhất từ ​​việc viết bằng C thay vì C ++ tôi có thể tìm thấy là

  1. C có ABI được chỉ định rõ
  2. C ++ có thể tạo thêm mã [ngoại lệ, RTTI, mẫu, đa hình thời gian chạy ]

Lưu ý rằng nhược điểm thứ hai trong trường hợp lý tưởng sẽ biến mất khi sử dụng tập hợp con chung cho C cùng với tính năng đa hình tĩnh .

Đối với những người tìm thấy quy tắc nghiêm ngặt C ++ bất tiện, chúng ta có thể sử dụng tính năng C ++ 11 với loại suy ra

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

18
Sử dụng trình biên dịch C cho mã C. Sử dụng trình biên dịch C ++ cho mã C ++. Không có ifs, không buts. Viết lại mã C của bạn trong C ++ là một điều hoàn toàn khác, và có thể - hoặc có thể không - đáng giá thời gian và rủi ro.
Toby Speight

2
Tôi muốn thêm vào lời khuyên @TobySpeight: Nếu bạn cần sử dụng mã C trong dự án C ++, bạn thường có thể biên dịch mã C thành C (ví dụ gcc -c c_code.c), mã C ++ là C ++ (ví dụ g++ -c cpp_code.cpp), sau đó liên kết chúng lại với nhau (ví dụ gcc c_code.o cpp_code.ohoặc ngược lại tùy thuộc vào các phụ thuộc của dự án). Bây giờ không có lý do gì để tước đi bất kỳ tính năng hay nào của ngôn ngữ này ...
tự kỷ

1
@ user877329 Đây là một giải pháp thay thế hợp lý hơn cho việc thêm các phôi vào mã làm giảm mức độ dễ đọc của mã, chỉ vì mục đích "tương thích với C ++".
tự kỷ

1
Có lẽ ưu điểm chính trong ngữ cảnh này là C cho phép bạn viết p = malloc(sizeof(*p));, điều này không cần thay đổi ngay từ đầu nếu pthay đổi thành một loại tên khác. "Ưu điểm" được đề xuất của việc truyền là bạn gặp lỗi biên dịch nếu plà loại sai, nhưng thậm chí còn tốt hơn nếu nó chỉ hoạt động.
Peter Cordes

1
Tôi muốn đề cập rằng viết bằng C có thể cần thiết khi nhắm mục tiêu các nền tảng thiếu trình biên dịch C ++ thích hợp. Các ngoại lệ và mẫu là các tính năng thường giúp c ++ tạo mã nhỏ hơn và / hoặc hiệu quả hơn trong khi tính đa hình thời gian chạy trong C ++ hầu hết tương đương với C.
user7860670

15

Không, bạn không bỏ kết quả malloc().

Nói chung, bạn không chọn đến hoặc từvoid * .

Một lý do điển hình được đưa ra cho việc không làm như vậy là thất bại #include <stdlib.h>có thể không được chú ý. Điều này không còn là vấn đề nữa trong một thời gian dài vì C99 đã khai báo hàm ngầm là bất hợp pháp, vì vậy nếu trình biên dịch của bạn tuân thủ ít nhất C99, bạn sẽ nhận được thông báo chẩn đoán.

Nhưng có một lý do mạnh mẽ hơn nhiều để không giới thiệu các phôi con trỏ không cần thiết:

Trong C, một con trỏ cast hầu như luôn luôn là một lỗi . Điều này là do quy tắc sau ( §6,5 p7 trong N1570, dự thảo mới nhất cho C11):

Một đối tượng sẽ có giá trị được lưu trữ chỉ được truy cập bởi một biểu thức giá trị có một trong các loại sau:
- một loại tương thích với loại hiệu quả của đối tượng,
- một phiên bản đủ điều kiện của loại tương thích với loại đối tượng hiệu quả,
- một loại là loại đã ký hoặc không dấu tương ứng với loại có hiệu lực của đối tượng,
- một loại là loại đã ký hoặc không dấu tương ứng với một phiên bản đủ điều kiện của loại đối tượng có hiệu lực,
- loại tổng hợp hoặc liên kết bao gồm một loại trong số các loại đã nói ở trên giữa các thành viên của nó (bao gồm, đệ quy, một thành viên của một tập hợp phụ hoặc có chứa), hoặc
- một loại ký tự.

Điều này còn được gọi là quy tắc răng cưa nghiêm ngặt . Vì vậy, đoạn mã sau đây là hành vi không xác định :

long x = 5;
double *p = (double *)&x;
double y = *p;

Và, đôi khi đáng ngạc nhiên, sau đây là tốt:

struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;

Đôi khi, bạn làm cần phải gợi ý dàn diễn viên, nhưng với những quy tắc nghiêm ngặt răng cưa , bạn phải rất cẩn thận với nó. Vì vậy, bất kỳ sự xuất hiện của một con trỏ trong mã của bạn là nơi bạn phải kiểm tra lại tính hợp lệ của nó . Do đó, bạn không bao giờ viết một con trỏ không cần thiết.

tl; dr

Tóm lại: Bởi vì trong C, bất kỳ sự xuất hiện nào của một con trỏ đúc sẽ giơ cờ đỏ cho mã yêu cầu sự chú ý đặc biệt, bạn không bao giờ nên viết các phôi con trỏ không cần thiết .


Ghi chú bên:

  • Có những trường hợp bạn thực sự cần một diễn viên void *, ví dụ nếu bạn muốn in một con trỏ:

    int x = 5;
    printf("%p\n", (void *)&x);

    Diễn viên là cần thiết ở đây, vì printf()là một hàm biến đổi, vì vậy các chuyển đổi ngầm không hoạt động.

  • Trong C ++, tình hình là khác nhau. Đúc các kiểu con trỏ là hơi phổ biến (và chính xác) khi xử lý các đối tượng của các lớp dẫn xuất. Do đó, nó làm cho cảm giác rằng trong C ++, việc chuyển đổi đến và đi từ void *không tiềm ẩn. C ++ có một bộ các hương vị khác nhau của đúc.


1
Trong ví dụ của bạn, bạn tránh void *. có sự khác biệt giữa các cast từ double * đến int * và ngược lại. malloc trả về điểm được căn chỉnh theo loại tiêu chuẩn lớn nhất để không có quy tắc răng cưa nào bị phá vỡ ngay cả khi ai đó tạo thành con trỏ căn chỉnh này sang loại khác.
P__J__

Bí danh không có gì để làm với sự liên kết và trong phần còn lại của nhận xét của bạn - rõ ràng bạn không nhận được điểm.

@PeterJ: chỉ trong trường hợp, vấn đề là tránh một con trỏ không cần thiết, vì vậy nó không giống như một đoạn mã bạn phải đặc biệt chú ý.

Vấn đề răng cưa nghiêm ngặt không thực sự có liên quan đến con trỏ void. Để có được các lỗi gây ra bởi các vi phạm bí danh nghiêm ngặt, bạn phải hủy tham chiếu dữ liệu được chỉ ra. Và vì bạn không thể tham chiếu con trỏ void, các lỗi như vậy theo định nghĩa không liên quan đến con trỏ void mà liên quan đến thứ khác.
Lundin

Thay vào đó, bạn sẽ phải thực hiện một quy tắc để cấm tất cả các phôi con trỏ. Nhưng sau đó, làm thế nào bạn sẽ viết những thứ như thói quen tuần tự hóa và lập trình liên quan đến phần cứng? Những thứ đó là sức mạnh của C. Diễn viên như vậy là tốt nếu bạn biết những gì bạn đang làm.
Lundin

15

Tôi thích làm các diễn viên, nhưng không phải bằng tay. Yêu thích của tôi là sử dụng g_newg_new0macro từ glib. Nếu glib không được sử dụng, tôi sẽ thêm các macro tương tự. Các macro này làm giảm sự sao chép mã mà không ảnh hưởng đến an toàn loại. Nếu bạn nhận được loại sai, bạn sẽ nhận được một diễn viên ngầm giữa các con trỏ không trống, điều này sẽ gây ra một cảnh báo (lỗi trong C ++). Nếu bạn quên bao gồm tiêu đề xác định g_newg_new0, bạn sẽ gặp lỗi. g_newg_new0cả hai đều có cùng một đối số, không giống như mallocmất ít đối số hơn calloc. Chỉ cần thêm 0để có được bộ nhớ khởi tạo bằng không. Mã có thể được biên dịch bằng trình biên dịch C ++ mà không thay đổi.


12

Casting chỉ dành cho C ++ chứ không phải C. Trong trường hợp bạn đang sử dụng trình biên dịch C ++, tốt nhất bạn nên thay đổi nó thành trình biên dịch C.


9

Khái niệm đằng sau con trỏ void là nó có thể được truyền tới bất kỳ loại dữ liệu nào, đó là lý do tại sao malloc trả về void. Ngoài ra, bạn phải nhận thức về typecasting tự động. Vì vậy, không bắt buộc phải bỏ con trỏ mặc dù bạn phải làm điều đó. Nó giúp giữ cho mã sạch và giúp gỡ lỗi


11
" Điều đó không bắt buộc - mặc dù bạn phải làm điều đó " - Tôi nghĩ rằng có một mâu thuẫn ở đó!
Toby Speight

5
Tôi nghĩ bạn nên đọc bài đăng này cho ai đó, và xem họ có hiểu bạn đang nói gì không. Sau đó viết lại nó, làm cho nó rõ ràng những gì bạn muốn nói. Tôi thực sự không thể hiểu câu trả lời của bạn là gì.
Bill Woodger

9

Một con trỏ void là một con trỏ chung và C hỗ trợ chuyển đổi ngầm định từ một loại con trỏ void sang các loại khác, do đó không cần phải đánh máy một cách rõ ràng.

Tuy nhiên, nếu bạn muốn cùng một mã hoạt động hoàn toàn tương thích trên nền tảng C ++, không hỗ trợ chuyển đổi ngầm định, bạn cần thực hiện kiểu chữ, vì vậy tất cả phụ thuộc vào khả năng sử dụng.


9
  1. Như đã nêu, nó không cần thiết cho C, nhưng đối với C ++.

  2. Bao gồm các diễn viên có thể cho phép một chương trình hoặc chức năng C biên dịch thành C ++.

  3. Trong C thì không cần thiết, vì void * được tự động và được thăng cấp an toàn cho bất kỳ loại con trỏ nào khác.

  4. Nhưng nếu bạn sử dụng thì nó có thể ẩn một lỗi nếu bạn quên bao gồm stdlib.h . Điều này có thể gây ra sự cố (hoặc tệ hơn là không gây ra sự cố cho đến khi về sau trong một số phần hoàn toàn khác của mã).

    Bởi vì stdlib.h chứa nguyên mẫu cho malloc được tìm thấy. Trong trường hợp không có nguyên mẫu cho malloc, tiêu chuẩn yêu cầu trình biên dịch C giả định malloc trả về một int. Nếu không có cast, một cảnh báo được đưa ra khi số nguyên này được gán cho con trỏ; tuy nhiên, với các diễn viên, cảnh báo này không được tạo ra, ẩn một lỗi.


7

Việc đúc malloc là không cần thiết trong C nhưng bắt buộc trong C ++.

Đúc không cần thiết trong C vì:

  • void * được tự động và an toàn được thăng cấp lên bất kỳ loại con trỏ nào khác trong trường hợp C.
  • Nó có thể ẩn một lỗi nếu bạn quên bao gồm <stdlib.h>. Điều này có thể gây ra sự cố.
  • Nếu con trỏ và số nguyên có kích thước khác nhau, thì bạn đang ẩn cảnh báo bằng cách truyền và có thể mất bit của địa chỉ trả về của bạn.
  • Nếu loại con trỏ bị thay đổi khi khai báo, người ta cũng có thể cần thay đổi tất cả các dòng mallocđược gọi và truyền.

Mặt khác, việc truyền có thể làm tăng tính di động của chương trình của bạn. tức là, nó cho phép một chương trình hoặc hàm C biên dịch thành C ++.


0

Đối với tôi, việc mang về nhà và kết luận ở đây là việc truyền mallocbằng C là hoàn toàn KHÔNG cần thiết nhưng nếu bạn thực hiện, nó sẽ không ảnh hưởng mallocmallocvẫn sẽ phân bổ cho bạn không gian bộ nhớ may mắn được yêu cầu của bạn. Một lý do khác về nhà là lý do hoặc một trong những lý do khiến mọi người thực hiện casting và điều này là để cho phép họ biên dịch cùng một chương trình trong C hoặc C ++.

Có thể có những lý do khác nhưng những lý do khác, gần như chắc chắn, sẽ khiến bạn gặp rắc rối nghiêm trọng sớm hay muộn.


0

Bạn có thể, nhưng không cần truyền vào C. Bạn phải truyền nếu mã đó được biên dịch thành C ++.

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.