2012-11-18 23 views
6

Trong kinh nghiệm của tôi với C mã hóa, tôi đã nhìn thấy 2 cách các đối số qua cho các chức năng:Thực tiễn tốt nhất để cấp phát bộ nhớ để sử dụng bởi hàm - malloc bên trong hay bên ngoài?

  1. malloc trước khi chức năng gọi

  2. malloc chức năng bên trong (biến không được khởi tạo trước khi chức năng gọi)

Tôi đặc biệt thích biểu mẫu thứ hai. Nhưng trong khi tôi là người duy nhất để mã chương trình của tôi, tôi biết điều đó, nhưng một số người khác không thể biết, và có thể dẫn đến 2 malloc, và rò rỉ bộ nhớ.

Vì vậy, câu hỏi của tôi là: Thực tiễn tốt nhất cho việc này là gì?

+0

malloc bên trong hoặc bên ngoài không quan trọng, chỉ cần đảm bảo giải phóng bộ nhớ được cấp phát đó là – Omkant

+0

tùy thuộc vào tình huống! –

Trả lời

9

Phân bổ bộ nhớ trong trình gọi linh hoạt hơn, vì nó cho phép người gọi sử dụng bộ nhớ tĩnh hoặc tự động thay vì phân bổ động và loại bỏ sự cần thiết phải xử lý trường hợp lỗi phân bổ trong callee. Mặt khác, khi người gọi cung cấp bộ nhớ yêu cầu người gọi phải biết kích thước trước. Nếu kích thước được biên dịch vào người gọi như là một hằng số và callee là trong một thư viện mà sau này được cập nhật để sử dụng một cấu trúc lớn hơn, mọi thứ sẽ phá vỡ khủng khiếp. Bạn có thể tránh điều này, tất nhiên, bằng cách cung cấp một hàm thứ hai (hoặc biến bên ngoài trong thư viện) để lấy kích thước cần thiết.

Khi nghi ngờ, bạn luôn có thể thực hiện hai chức năng:

  1. Chức năng chính mà sử dụng bộ nhớ gọi cung cấp.
  2. Một hàm bao hàm phân bổ dung lượng lưu trữ phù hợp, gọi hàm trong # 1 bằng cách sử dụng nó và trả về con trỏ cho người gọi.

Sau đó, người gọi được tự do lựa chọn phương pháp nào phù hợp hơn cho trường hợp sử dụng cụ thể.

8

Cá nhân tôi ủng hộ mạnh mẽ đề xuất đầu tiên của bạn (bất cứ khi nào có thể) cho trực giao. Lấy ví dụ sau:

extern void bar(int *p, int n); 

void foo(int n) 
{ 

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

    // fill array object 
    bar(p, n); 

    // work with array elements 

    /* ... */ 

    // array no longer needed, free object 
    free(p); 
} 

Đây là trực giao. mallocfree được gọi trong phạm vi từ vựng tương tự, sạch sẽ và dễ đọc. Một ưu điểm khác là bạn có thể chuyển tới hàm bar một mảng có thời lượng lưu trữ khác nhau, ví dụ: một mảng có thời lượng lưu trữ tự động hoặc tĩnh. Bạn cho phép bar chỉ tập trung vào công việc mà nó đã làm và để cho một hàm khác quản lý việc phân bổ mảng.

Lưu ý rằng đây cũng là cách tất cả các chức năng của Standard C hoạt động: chúng không bao giờ xuất hiện để gọi malloc.

3

Các tiêu chí tôi muốn sử dụng để quyết định là:

  • Nếu mã bên ngoài chức năng gọi có thể biết có bao nhiêu bộ nhớ để phân bổ, sau đó nó là tốt hơn để có mã gọi điện thoại cấp phát bộ nhớ.

  • Nếu mã bên ngoài hàm được gọi không thể biết số lượng bộ nhớ cần phân bổ, thì hàm được gọi phải thực hiện cấp phát bộ nhớ. Có khả năng sau đó sẽ có một hàm thứ hai có sẵn để giải phóng bộ nhớ được trả về bởi hàm đầu tiên (hàm 'được gọi là'), trừ khi nó chỉ là một đơn free() cần thiết. Tài liệu chức năng nên làm rõ điều này.

Ví dụ, nếu hàm được gọi là đọc cấu trúc cây hoàn chỉnh từ tệp, chức năng sẽ phải cấp phát bộ nhớ. Tuy nhiên, cũng sẽ có một hàm đồng hành để giải phóng bộ nhớ (vì mã được gọi biết làm thế nào để thực hiện nó và mã gọi không cần phải biết). Mặt khác, nếu hàm được gọi là đọc một danh sách đơn giản các giá trị số nguyên và dấu phẩy động vào một cấu trúc kích thước cố định, thì tốt hơn hết là làm cho hàm gọi điện thoại cấp phát bộ nhớ. Lưu ý rằng tôi đã bỏ qua 'chuỗi'! Nếu các chuỗi có kích thước cố định trong cấu trúc, thì hàm gọi có thể thực hiện việc phân bổ, nhưng nếu các chuỗi có kích thước biến, thì có lẽ hàm được gọi thực hiện phân bổ.

Thư viện chuẩn C có các chức năng như fgets(), mong đợi mã gọi để phân bổ bộ nhớ được sử dụng. Trình tự gọi cho biết số fgets() có bao nhiêu không gian. Bạn gặp vấn đề nếu bạn không cung cấp đủ bộ nhớ. (Sự cố với fgets() là bạn chỉ có thể bắt đầu dòng văn bản chứ không phải toàn bộ dòng văn bản.)

Thư viện POSIX 2008 cung cấp đủ không gian cho dòng.

asprintf() và các chức năng liên quan (xem TR24731-2) cấp phát bộ nhớ theo yêu cầu. Các chức năng snprintf() không - nó được cho biết bao nhiêu không gian có sẵn, nó sử dụng không nhiều hơn thế, và nói bao nhiêu nó thực sự cần thiết, và nó là vào bạn để lưu ý nếu bạn không cung cấp đủ không gian và làm một cái gì đó về nó (phân bổ nhiều không gian hơn và thử lại, hoặc vô tình bỏ qua giá trị cắt ngắn và tiếp tục như thể không có gì sai).

1

Nguyên tắc ẩn thông tin cho thấy rằng việc phân bổ bộ nhớ được thực hiện tốt nhất trong một hàm.

Nếu bạn nhìn vào cách stdio.h công trình:

FILE *myFile; 

myFile = fopen("input.txt", "r"); 

if (!myFile) { 
    fprintf(stderr, "Error opening input.txt for reading.\n"); 
    // other exit handling close 
} 
else { 
    // code to read from file 
    fclose(myFile); 
} 

cuộc gọi thư viện cấp phát bộ nhớ chứa thông tin về các tập tin bạn đang làm việc với, và nó trả về một con trỏ đến cấu trúc đó. Người gọi chịu trách nhiệm về sau khi giải phóng bộ nhớ đó (với một cuộc gọi đến fclose).

Mẫu này được lặp lại trong toàn bộ thư viện Chuẩn C.

Có ít nhất hai nhược điểm để yêu cầu người gọi để phân bổ và giải phóng bộ nhớ:

  1. thêm mã sẽ được yêu cầu ở phía bên gọi.
  2. Mã gọi sẽ cần phải được biên dịch lại (tối thiểu) hoặc thay đổi nếu kích thước của cấu trúc được phân bổ đã từng thay đổi.
Các vấn đề liên quan