2014-08-29 13 views
11

Đối với mã bên dưới: (1) "chính" gọi hàm "f1". (2) chức năng "f1" thực hiện một số thao tác số; tạo một mảng của "char" với malloc và sau đó, trả về con trỏ của mảng tới chính (không có de-allocating -freeing- array).Bộ nhớ được cấp phát trong một hàm vẫn được cấp phát sau khi hàm trả về?

Tôi có 3 câu hỏi liên quan đến vụ án: (1) Tôi giả định, mặc dù hàm "f1" đã chấm dứt, mảng char được phân bổ vẫn được cấp phát cho đến khi chương trình chính kết thúc hoàn toàn. Đó là, bộ nhớ phân bổ vẫn thuộc về chính và không có quá trình nào khác có thể truy cập (ý tôi là, can thiệp vào) nó từ bên ngoài. Tôi có đúng không? (2) Tôi có phải giải phóng mảng (được phân bổ trong "f1") trước khi chương trình chấm dứt (hoặc nó được giải phóng ngay sau khi chương trình chính chấm dứt)? (3) Nếu câu trả lời cho câu hỏi thứ hai là "có" thì làm thế nào để bạn giải phóng một mảng được phân bổ trong một chức năng khác?

lưu ý: Tôi muốn ở trong ranh giới của thuần túy c và không được chuyển sang C++.

char *f1 (...) { 
    ... 
    ... 
    char *fTmp = malloc (length1 * sizeof (char)); 
    char *fData = malloc (length2 * sizeof (char)); 
    ... 
    ... 
    free (fTmp); 
    return (fData); 
} 

int main() { 
    char *fData = f1 (...); 
    ... 
    return (0); 
} 

Trả lời

14

Tôi giả định, mặc dù hàm "f1" đã chấm dứt, mảng char được phân bổ vẫn được cấp phát cho đến khi chương trình chính chấm dứt hoàn toàn.

True. Bộ nhớ được cấp phát động không có gì liên quan đến các hàm, nó thuộc về quá trình.

Tức là, bộ nhớ được phân bổ vẫn thuộc về chính và không có quy trình nào khác có thể truy cập nó từ bên ngoài. Tôi có đúng không?

Memory không thuộc về main() (dự định như là chức năng) nhưng để xử lý riêng của mình (trong đó main() chỉ là điểm khởi đầu). Trong một hệ thống có bảo vệ bộ nhớ (trong đó mỗi quá trình được phân lập từ các bộ lọc khác), nó không thể truy cập từ bên ngoài. Tuy nhiên, bạn có thể phân bổ nó theo cách cụ thể của hệ thống để chia sẻ bộ nhớ qua các quy trình.

Tôi có phải giải phóng mảng (được phân bổ trong "f1") trước khi chương trình chấm dứt (hoặc nó được giải phóng ngay khi chương trình chính chấm dứt)?

Có. Bộ nhớ chưa phân bổ - trong hầu hết các hệ thống - được tự động phân phối bởi Hệ điều hành khi quá trình chấm dứt nhưng điều này phụ thuộc vào hệ thống. IMO ngay cả khi hệ điều hành hiện nó, bạn nên luôn luôn deallocate, bằng cách sử dụng deallocation tự động như một lá cờ đỏ (tôi quên rằng để deallocate, nó là một lỗi? Một cái gì đó tôi bị mất?). Hơn nữa nếu f1 được gọi 1000 lần nó sẽ rò rỉ bộ nhớ mỗi lần nhanh chóng ăn tất cả bộ nhớ có sẵn. Hãy suy nghĩ về một quá trình trong một máy chủ, nó có thể (và nên) được và chạy trong nhiều năm.

Nếu câu trả lời cho câu hỏi thứ hai là "có" thì bạn làm cách nào để giải phóng mảng được phân bổ trong một chức năng khác?

Thật tuyệt khi những người phân bổ bộ nhớ cũng giải phóng bộ nhớ. Nếu không thể, người gọi sẽ chịu trách nhiệm về bộ nhớ đó. Đó là, ví dụ, những gì strdup() nào. Trong trường hợp này, hàm được gọi phải trả về (bằng cách nào đó) một con trỏ tới bộ nhớ được cấp phát (hoặc một thẻ điều khiển/mã thông báo có thể được sử dụng bởi một hàm chuyên biệt khác). Ví dụ:

char* pBuffer = f1(); 
// Use it 
free(pBuffer); 

Lưu ý rằng có rất nhiều nhiều kỹ thuật nếu bạn muốn ẩn như nội con trỏ.Bạn có thể sử dụng mã thông báo (ví dụ: số nguyên, khóa trong từ điển), typedef hoặc loại mờ.

+1

Tôi đã làm điều tương tự; tức là, tôi giải phóng mảng trong một quá trình đã được tạo trong một tiến trình khác ** bằng cách sử dụng con trỏ được truyền qua **. Tôi không chắc chắn những gì tôi đã làm là đúng cách. Vì vậy, cách tiếp cận của bạn có vẻ là câu trả lời gần nhất: "Thật tốt khi ai phân bổ bộ nhớ cũng giải phóng nó. Nếu không thể thì người gọi sẽ chịu trách nhiệm về trí nhớ như vậy". – ssd

+1

@ merkez3110 nhưng bạn không thể chia sẻ con trỏ giữa các quy trình (trừ khi bạn đang chạy trong một môi trường không có bộ nhớ ảo được bảo vệ). –

+1

Xin lỗi vì đã sử dụng thuật ngữ sai: "quy trình". Trong bình luận của tôi ở trên, tôi thực sự có nghĩa là "chức năng" khác nhau trong cùng một chương trình, không phải các quá trình khác nhau. Cảm ơn mọi người. – ssd

1

Sử dụng malloc sẽ cấp phát bộ nhớ trên heap cho đến khi bạn free.

Điều này có nghĩa là bạn cần đảm bảo mọi malloc đều có miễn phí tương ứng, cũng không ngụ ý rằng không có quy trình nào khác không thể truy cập dữ liệu của bạn. Nó chỉ là một giá trị tại một địa chỉ.

Trong chính bạn, bạn phải free(fData) để tránh rò rỉ bộ nhớ.

Tóm lại thì:

1) Giả định đầu tiên của bạn là chính xác, thứ hai và thứ ba thì không. Nó sẽ vẫn được phân bổ, nhưng nó không phải là địa phương cho chính, và không bị ràng buộc với quá trình khi nó chấm dứt .

2) Có, bạn phải giải phóng nó

3) Sử dụng con trỏ bạn nhận được từ hàm. Nếu bạn không trả lại một con trỏ đến dữ liệu được cấp phát của bạn từ một hàm, hãy đảm bảo rằng hàm đó là của hàm free.

1
  1. Có, nó vẫn còn trong heap. Tuy nhiên, bạn đang bối rối về khái niệm về quy trình. Trừ khi bạn tạo một quy trình khác (sử dụng fork trên * nix), nó vẫn là quá trình tương tự.

  2. Đó là thói quen tốt để giải phóng bộ nhớ khi không sử dụng. Nhưng nếu chương trình kết thúc bình thường, bộ nhớ được cấp phát sẽ được giải phóng bởi hệ thống.

  3. Như thế này:

    int main() { 
        char *fData = f1 (...); 
        //... 
        free(fData); 
        //... 
    } 
    
4
  1. Có, cấp phát bộ nhớ với malloc() ở lại cho đến khi nó được giải phóng. Làm cách nào khác một hàm có thể trả về dữ liệu có kích thước biến cho người gọi của nó?

  2. Khi một chương trình thoát, tất cả bộ nhớ được phân bổ với malloc() được giải phóng. Tuy nhiên, nói chung không phải là một ý tưởng tốt để giữ nhiều bộ nhớ không cần thiết xung quanh cho đến khi chương trình chấm dứt, vì nó có thể tác động đến hiệu suất, hoặc hệ thống có thể hết bộ nhớ ảo. Điều này có thể là mối quan tâm đặc biệt đối với các chương trình chạy dài, việc sử dụng bộ nhớ của chúng đôi khi tiếp tục phát triển cho đến khi chúng sử dụng tất cả bộ nhớ ảo có sẵn.

  3. Bạn gọi free() trên con trỏ được hàm trả về. Vì vậy, trong trường hợp của bạn, main() có thể thực hiện free(fData) sau khi hoàn thành việc sử dụng mảng.

Điều này tất cả nên được đề cập trong bất kỳ lớp học lập trình C hoặc sách giáo khoa nào.

2

malloc cấp phát bộ nhớ trên heap và do đó bộ nhớ này vẫn được cấp phát cho đến khi nó được giải phóng bởi hàm free hoặc chương trình chấm dứt thành công.
Trong trường hợp của bạn, bạn giải phóng ftemp trong f1 để nó không còn tồn tại sau khi chức năng chấm dứt. fdata vẫn còn trên heap và bạn có thể truy cập main khi bạn đang quay trở lại con trỏ tới vị trí được phân bổ đó.

Sau khi main chấm dứt thành công, bộ nhớ được chỉ định bởi fdata được giải thoát.

Vì vậy, nó được coi là tốt để giải phóng bộ nhớ ngay sau khi bạn không cần nó nữa. Không có điểm trong các khối giải phóng ở cuối chương trình, bởi vì tất cả không gian của chương trình được đưa trở lại hệ thống khi quá trình chấm dứt (xem xét các hệ điều hành hiện đại).

1

Có hai loại bộ nhớ cơ bản mà bạn có thể làm việc trong C. Hai loại là ngăn xếp và đống. Nói chung, các biến bạn tạo trong một hàm sẽ được cấp phát trên ngăn xếp và sẽ được giải phóng khi hàm trả về. Bộ nhớ được cấp phát trong heap sẽ vẫn tồn tại và bạn có nghĩa vụ quản lý phân bổ đó trong chương trình của bạn. Bộ nhớ trong heap sẽ vẫn được cấp phát cho đến khi bạn tự do sử dụng con trỏ (địa chỉ bộ nhớ) đề cập đến khối dữ liệu.

Đọc một chút về cả hai sẽ giúp bạn hiểu. Tôi chỉ ra rằng bạn có hai trường hợp fData, mỗi trường hợp có phạm vi riêng của chúng. Cả hai con trỏ đều trỏ đến bộ nhớ bạn phân bổ với:

char *fData = malloc (length2 * sizeof (char)); 

.. mặc dù chúng đi vào và ra khỏi phạm vi khi mã của bạn thực thi.

1

Nếu bạn không giải phóng bộ nhớ bạn không sử dụng, cuối cùng điều này sẽ tích lũy - nếu bạn làm điều này với nhiều con trỏ khác- và chương trình của bạn có thể hết bộ nhớ. Sau khi giải phóng khối bộ nhớ bằng cách sử dụng chức năng free, tôi cũng đề nghị gán NULL cho con trỏ vì điều này bảo vệ chống lại con trỏ lơ lửng ngay cả khi bạn đã giải phóng con trỏ, nếu bạn thử truy cập nó, bạn có thể nhận được hành vi không xác định, trong khi truy cập và hoạt động trên NULL con trỏ dẫn đến sự cố, vì vậy bạn có thể dễ dàng theo dõi sự cố

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