2010-07-17 21 views
5

sự khác biệt giữa điều này là gì:C++: định nghĩa mảng địa phương so với một cuộc gọi malloc

somefunction() { 
    ... 
    char *output; 
    output = (char *) malloc((len * 2) + 1); 
    ... 
} 

và điều này:

somefunction() { 
    ... 
    char output[(len * 2) + 1]; 
    ... 
} 

Khi là một trong hơn thích hợp hơn người kia?

cảm ơn tất cả các câu trả lời của bạn. dưới đây là tóm tắt:

  1. ví dụ: 1 là phân bổ heap
  2. ví dụ: 2 là đống phân bổ
  3. có một giới hạn kích thước trên stack, sử dụng nó để phân bổ nhỏ
  4. bạn phải phân bổ đống miễn phí, hoặc nó sẽ bị rò rỉ
  5. việc phân bổ chồng là không thể truy cập một khi chức năng thoát
  6. việc phân bổ đống có thể truy cập cho đến khi bạn giải phóng nó (hoặc ứng dụng kết thúc)
  7. VLA của không nằm trong tiêu chuẩn C++

sửa chào đón.

đây là một số giải thích về sự khác biệt giữa đống vs stack:
What and where are the stack and heap?

+0

Bạn nên tham khảo sách C++ của mình. Nếu bạn không có một cuốn sách C++, tôi khuyên bạn nên nhận được một trong những văn bản mới bắt đầu được liệt kê trong [Danh sách và Danh sách Sách Hoàn chỉnh C++] (http://stackoverflow.com/questions/388242/the-definitive-c-book -guide-and-list). –

+4

Ngoài ra, câu hỏi phụ thuộc hoàn toàn vào cách 'len' được định nghĩa; nếu nó không phải là một hằng số, thì ví dụ mã thứ hai là C++ hình thành không đúng. –

+0

len là một int và được gán cho kết quả của strlen bên trong hàm. – Gush

Trả lời

5

Sử dụng người dân địa phương khi bạn chỉ có một lượng nhỏ dữ liệu và bạn sẽ không sử dụng dữ liệu bên ngoài phạm vi chức năng bạn đã khai báo. Nếu bạn định chuyển dữ liệu xung quanh, hãy sử dụng malloc.

Biến cục bộ được giữ trên ngăn xếp, có nhiều kích thước hơn giới hạn so với vùng heap, nơi mảng được phân bổ bằng malloc go. Tôi thường đi cho bất cứ điều gì> 16 byte được đặt trên đống, nhưng bạn có một chút linh hoạt hơn thế. Chỉ cần không được phân bổ người dân địa phương trong phạm vi kích thước kb/mb - chúng thuộc về đống.

+2

+1 để đề cập đến giới hạn kích thước của ngăn xếp, nhưng tôi nghĩ rằng 16 byte là một chút thấp. Không phải là kích thước ngăn xếp mặc định trên hầu hết các nền tảng trong megabyte (vì vậy các biến đa KB nên được sử dụng tốt trong hầu hết các trường hợp)? –

+0

Có, nhưng khi bạn phân bổ trên ngăn xếp, bạn cũng nên xem xét thời gian tồn tại của dữ liệu cũng như kích thước của nó. Tôi có nghĩa là 16 byte cho các giá trị mà thời gian sống có thể không được biết đến. Bạn có thể phân bổ vài kb trên ngăn xếp, sau đó phân nhánh thành một hàm khác, cũng phân bổ nhiều kb, v.v. - nó có thể nhanh chóng lấp đầy. (Thảo luận về điều này nhiều hơn trong phản hồi của StackedCrooked.) –

+3

Vì câu hỏi là về C++, tôi phản đối mạnh mẽ đề nghị sử dụng malloc. Trong C, sử dụng malloc. Trong C++, sử dụng mới. –

6

Bộ nhớ giao đất đầu tiên trên heap. Bạn phải nhớ để giải phóng bộ nhớ, hoặc nó sẽ bị rò rỉ. Điều này là thích hợp nếu bộ nhớ cần phải được sử dụng bên ngoài chức năng, hoặc nếu bạn cần phải phân bổ một lượng lớn bộ nhớ.

Thứ hai cấp phát bộ nhớ trên ngăn xếp. Nó sẽ được tự động lấy lại khi hàm trả về. Đây là cách thuận tiện nhất nếu bạn không cần phải trả lại bộ nhớ cho người gọi.

+1

Mặt khác, bạn không muốn đặt quá nhiều vào ngăn xếp, bởi vì nó không phải là lớn như đống. Vì vậy, 'int data [10]' là tốt, nhưng tôi sẽ không tính vào 'int data [10000]' làm việc ở tất cả. – zvone

5

Ví dụ đầu tiên phân bổ một khối lưu trữ từ heap. Cái thứ hai cấp phát bộ nhớ từ ngăn xếp. Sự khác biệt sẽ hiển thị khi bạn trả về kết quả đầu ra từ hàm somefunction(). Bộ nhớ được cấp phát động vẫn có sẵn để bạn sử dụng, nhưng bộ nhớ dựa trên ngăn xếp trong ví dụ thứ hai là, um, không nơi nào. Bạn vẫn có thể ghi vào bộ lưu trữ này và đọc nó trong một thời gian, cho đến khi bạn gọi hàm tiếp theo, lúc đó bộ nhớ sẽ bị ghi đè ngẫu nhiên với địa chỉ trả về, đối số và như vậy.

Có rất nhiều thứ kỳ lạ khác đang diễn ra với mã trong câu hỏi này. Trước hết, đây là chương trình C++, bạn muốn sử dụng mới thay vì malloc(), vì vậy bạn muốn nói

output = new char[len+1]; 

Và điều gì với len * 2 + 1?Có lẽ đây là một cái gì đó đặc biệt trong mã của bạn, nhưng tôi đoán bạn muốn phân bổ các ký tự unicode hoặc các ký tự nhiều byte. Nếu nó unicode, null chấm dứt mất hai byte cũng như mỗi ký tự không, và char là loại sai, là 8 bit byte trong hầu hết các trình biên dịch. Nếu nó là multibyte, thì hey, tất cả mọi phiên cược đều bị tắt.

+1

+1 cho mới so với malloc – bstpierre

+0

Tôi không biết nếu im đang làm là đúng - nhưng nó bắt nguồn từ tài liệu cho PQescapeStringConn - "'đến' sẽ trỏ đến bộ đệm có thể chứa ít nhất một byte nữa gấp đôi giá trị độ dài " từ đây: http://www.postgresql.org/docs/7.3/static/libpq-exec.html – Gush

3

Đầu tiên một số thuật ngữ:

  • Mẫu đầu tiên được gọi đống phân bổ.
  • Mẫu thứ hai được gọi là phân bổ ngăn xếp.

Nguyên tắc chung là: phân bổ trên stack, trừ khi:

  1. Kích thước yêu cầu của mảng này là không rõ tại thời gian biên dịch.
  2. Kích thước yêu cầu vượt quá 10% tổng kích thước ngăn xếp. Kích thước ngăn xếp mặc định trên Windows và Linux thường là 1 hoặc 2 MB. Vì vậy, mảng địa phương của bạn không được vượt quá 100.000 byte.
+1

Tôi nghĩ rằng các quy tắc chung đó hơi yếu. Ví dụ, tôi có thể phân bổ 10% stack của tôi trong main(), và sau đó gọi một số chức năng khác (mà lần lượt sẽ gọi nhiều hơn và nhiều hơn nữa, mỗi điền vào ngăn xếp). Tôi sẽ không thể giải phóng không gian đó cho đến khi tất cả các chức năng sâu hơn của tôi được hoàn thành (về cơ bản là kết thúc của chương trình trong trường hợp này). Chương trình của tôi đã tự động nhận được không gian ngăn xếp ít hơn 10%. Tôi nghĩ rằng quy tắc 10% chỉ nên hạn chế ở ** các hàm ** thuần túy **. –

+1

@Mark H: Bạn nói đúng rằng quy tắc 10% không nên được cấp phát chính và chắc chắn không có trong mã reentrant. Tuy nhiên, tôi không thấy các hàm thuần túy sẽ an toàn hơn thế nào so với tràn ngăn xếp hơn các hàm không thuần túy. – StackedCrooked

+1

Một hàm thuần túy sẽ không gọi các hàm khác (trừ khi chúng là thuần túy) - nhưng chúng thường sẽ thực hiện một số thuật toán và trả về ngay lập tức. Bất kỳ biến cục bộ nào bạn phân bổ trong chúng sẽ không tồn tại, đó là cách ngăn xếp. Các biến có thời gian tồn tại lâu hơn (ví dụ trước) nên đi trên heap. –

1

Bạn đã gắn thẻ câu hỏi của mình với cả C++ và C, nhưng giải pháp thứ hai không được phép trong C++. Các mảng chiều dài thay đổi chỉ được phép trong C (99).

Nếu bạn giả sử 'len' là hằng số, cả hai đều sẽ hoạt động.

malloc() (và C++ 'mới') cấp phát bộ nhớ trên heap, có nghĩa là bạn phải giải phóng() (hoặc nếu bạn đã cấp phát 'mới', 'xóa') bộ đệm hoặc bộ nhớ sẽ không bao giờ được khai hoang (rò rỉ).

Sau đó phân bổ mảng trên ngăn xếp và sẽ biến mất khi nó nằm ngoài phạm vi. Điều này có nghĩa là bạn không thể trả lại con trỏ tới vùng đệm nằm ngoài phạm vi được phân bổ.

Trước đây là hữu ích khi bạn muốn chuyển khối bộ nhớ xung quanh (nhưng trong C++, nó được quản lý tốt nhất với lớp RAII, không theo cách thủ công), trong khi thứ hai là tốt nhất cho các mảng nhỏ, kích thước cố định chỉ cần tồn tại trong một phạm vi.

Cuối cùng, bạn có thể đánh dấu các mảng khác ngăn xếp phân bổ với 'tĩnh' để mang nó ra khỏi stack và thành phần dữ liệu toàn cầu:

static char output[(len * 2) + 1]; 

này cho phép bạn quay trở lại con trỏ đến bộ đệm bên ngoài phạm vi của nó, tuy nhiên, tất cả các cuộc gọi đến một chức năng như vậy sẽ đề cập đến cùng một mảnh dữ liệu toàn cầu, do đó, không sử dụng nó nếu bạn cần một khối duy nhất của bộ nhớ mỗi lần.

Cuối cùng, không sử dụng malloc trong C++ trừ khi bạn có lý do thực sự tốt (ví dụ: realloc). Thay vào đó, hãy sử dụng 'mới' và 'xóa' đi kèm.

+0

từ http: //gcc.gnu.org/onlinedocs/gcc/Variable-Length.html Các mảng tự động có độ dài thay đổi được cho phép trong ISO C99 và dưới dạng phần mở rộng GCC chấp nhận chúng ở chế độ C90 và trong C++. Tôi đoán đó là lý do tại sao nó biên dịch và hoạt động cho tôi bằng gcc. Tôi có thể tin rằng nó không phải là C++ theo tiêu chuẩn hoặc định nghĩa đúng của ngôn ngữ mà bạn có thể đề cập đến. Tôi có nên xóa thẻ C++ không? – Gush

+0

Tôi chỉ đơn giản là tự hỏi bạn đang sử dụng, nhưng đối với tôi để giả sử C + +, tôi nghĩ rằng tôi muốn cảnh báo bạn rằng VLA không phải là một phần của tiêu chuẩn C + +. –

+0

ok. tôi không biết điều đó và nó có liên quan – Gush

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