2009-02-01 32 views
30

Tôi là một lập trình viên C mới bắt đầu, và tôi cho rằng đây là trường hợp, nhưng muốn một số khẳng định nếu có thể.Là calloc (4, 6) giống như calloc (6, 4)?

Nếu chúng giống nhau, tại sao không chỉ lấy một đối số thay thế?

Trả lời

2

Cũng vậy. Lý do là vì hầu hết thời gian bạn muốn sử dụng toán tử sizeof làm một trong các đối số. Nếu vượt qua hai tham số làm phiền bạn, hãy gọi malloc() có một đối số duy nhất.

+12

Bạn nên lưu ý rằng malloc() sẽ không bằng không bộ nhớ như calloc(). –

+0

Vâng, đó là sự thật. Cảm ơn cho những người đứng đầu lên. –

+0

Có đúng là chúng giống nhau khi bạn cân nhắc không? Là calloc (16,3) giống như calloc (3,16) trên một máy mà cần bất kỳ phần tử dữ liệu nào để bắt đầu trên một ranh giới 4 byte? – ChrisW

20

Mọi người chủ yếu sử dụng các quy trình phân bổ để phân bổ không gian cho một số lượng mục đã đặt, do đó, calloc() cho phép chỉ định rõ ràng. Vì vậy, ví dụ, nếu bạn muốn không gian cho 100 số nguyên hoặc 20 của cấu trúc của riêng bạn:

int *pInt = calloc (100, sizeof(int)); 
tMyStruct *pMyStruct = calloc (20, sizeof(tMyStruct)); 

Mã này thực sự trông hơi "đẹp hơn" so với tương đương malloc() cuộc gọi:

int *pInt = malloc (100 * sizeof(int)); 
tMyStruct *pMyStruct = malloc (20 * sizeof(tMyStruct)); 

mặc dù, để C lập trình dày dạn, không có sự khác biệt thực sự (khác với khởi tạo zero của khóa học).

Tôi phải nói rằng tôi có không bao giờ sử dụng calloc trong tự nhiên, vì tôi hầu như luôn tạo một struct trong đó số không có ý nghĩa. Tôi muốn khởi tạo tất cả các trường theo cách thủ công để đảm bảo tôi nhận được các giá trị tôi muốn.

+0

Cảm ơn. Btw bạn cần phải thay đổi calloc thứ hai trong mã dưới cùng thành một malloc. – Ali

+1

Ta, Damnable cut'n'paste hoạt động! :-) – paxdiablo

+0

Tôi đồng ý - Tôi gần như không bao giờ sử dụng calloc(). Đôi khi tôi tạo một cấu trúc tĩnh được khởi tạo mặc định đúng, và sau đó gán cấu trúc đó cho các cấu trúc mới được phân bổ - và thực hiện bất kỳ sửa chữa dư nào. –

4

Có một sự khác biệt nhỏ: Calloc có thể quyết định không chỉ ra bộ nhớ khi cần thiết và sau đó có lợi thế là biết kích thước của các phần tử.

Tôi không thể đặt tên cho bất kỳ triển khai nào thực hiện việc này nhưng đã được suy nghĩ cho điều này.

Như một ví dụ:

Một callocates 4GB bộ nhớ, nhưng hệ thống chỉ có 2GB: nó sẽ không làm cho tinh thần để viết 2GBs của zero vào bộ nhớ ảo, do đó hệ thống có thể thiết lập một bẩn cờ trên bộ nhớ này để không nó ra khi nó được nạp vào bộ nhớ.

+0

Tôi sẽ ngạc nhiên nếu bất kỳ triển khai * real * nào thực sự thực thi 'calloc' như một' malloc' và 'memzero' cho tất cả các kích thước bộ nhớ nhỏ. Một tối ưu hóa rất nhỏ sẽ là ánh xạ (các) trang đến '/ dev/zero'. – sanjoyd

+0

@ theDigtialEngel: Tôi chưa bao giờ thấy một cuộc gọi thực hiện từ bên trong, vì vậy tôi không thể nói. –

12

calloc (4, 6) và calloc (6, 4) là KHÔNG giống nhau:

Trên một hệ thống 32bit/64bit điển hình, là người đầu tiên sẽ phân bổ 32 byte và lần thứ hai 24 byte.


Điểm mấu chốt là calloc mà phải trả lại bộ nhớ như thể nó đã được liên kết một cách chính xác như một mảng. Nó có nghĩa là để phân bổ một mảng và được sử dụng như sau:

A *p = (A*)calloc(count, sizeof(A)); 
for (int i = 0; i < count; ++i) 
{ 
    f(&(p[i])); 
    // f(p + i) is also valid 
} 

hoặc

A *p = (A*)calloc(count, sizeof(A)); 
for (A *q = p; q != p + count; ++q) 
{ 
    f(q); 
} 

calloc là nghĩa vụ phải bố trí các mảng có tính đến đệm tài khoản và yêu cầu điều hành khác của hệ thống đích. Vì vậy, trên hầu hết các máy 32bit, trong đó một cấu trúc 6 byte sẽ cần phải được đệm đến 8 byte, nó sẽ phân bổ 4 lô 8 byte.

calloc trong đó đối số đầu tiên là sizeof() rất có thể là lỗi và cần được điều tra.

calloc trong đó đối số thứ hai không phải là sizeof (atype) là không xác định. Nó reeks của các giả định ẩn và nguy hiểm cho cảng.

Làm rõ: Trên một hệ thống 32bit/64bit điển hình, một cấu trúc có thể được đệm và liên kết đến một bội số của 32bits. Như vậy trên các hệ thống này kích thước của sẽ không trả về 6 byte. Trong thực tế không có gì đảm bảo rằng trình biên dịch sẽ không pad và căn chỉnh với một số bội số của 16 byte nếu đó là những gì mà trình biên dịch/nền tảng yêu cầu.

Câu trả lời của tôi dựa trên thực tế bạn không nên đưa ra các giả định về kích thước cấu trúc. Chúng có thể thay đổi với các tùy chọn trình biên dịch, hoặc nền tảng đích. Chỉ cần đảm bảo rằng đối số thứ hai của bạn là biểu thức sizeof và không đưa ra giả định.


Từ standard:

Các calloc() chức năng có trách nhiệm phân bổ không gian không sử dụng trong một loạt các yếu tố nelem mỗi có kích thước tính bằng byte là elsize. Không gian sẽ được khởi tạo cho tất cả các bit 0.

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

+0

Hầu hết các lập trình viên tôi đã làm việc với giả định họ giống nhau. Một dự án đã viết một phân bổ tùy chỉnh với giả định đó. Khi nó đã được chứng minh sai, đội trưởng quyết định sửa chữa nó bằng cách thêm một bình luận cho biết đối số thứ hai phải là một sizeof hoặc nó sẽ gây ra một vụ tai nạn. –

+6

tôi tin rằng bạn đã sai. cả hai malloc và calloc phải trả về một con trỏ phù hợp cho bất kỳ kiểu đối tượng nào. như nó xảy ra, đó cũng chính xác là những gì bạn đã trích dẫn ở đó từ tiêu chuẩn. giả sử niềm tin của tôi là sai, bạn có thể cung cấp một liên kết để đọc thêm về điều này không? –

+0

anyway, nếu bạn thay đổi thứ tự của các đối số, bạn không bao giờ được đảm bảo cho bất kỳ chức năng nào chính xác như vậy sẽ xảy ra. việc triển khai có thể thực hiện tối ưu hóa thay mặt chúng - ngay sau khi các ràng buộc được đưa ra bởi tiêu chuẩn không bị vi phạm để sử dụng mã không hoạt động. –

5

Mặc dù câu trả lời được chấp nhận (mà tôi cho là đúng), dường như có sự nhầm lẫn về số lượng byte được phân bổ do căn chỉnh. Vì vậy, đây là một thử nghiệm nhỏ trên Linux 32-bit của tôi với gcc-4.3:

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

int main() 
{ 
    char* p1 = calloc(6, 4); 
    char* p2 = calloc(4, 6); 
    char* p3 = calloc(1,1); 
    printf("%p, %p, %p\n", p1, p2, p3); 
    return 0; 
} 

Kết quả là:

0x826b008, 0x826b028, 0x826b048 

trong đó cho thấy rằng cả hai calloc(6,4)calloc(4,6) phân bổ cùng một lượng bộ nhớ, đó là được làm tròn đến 32 byte trên hệ thống của tôi. Thay đổi các con số để calloc(3,4)calloc(4,3) sẽ cho kết quả sau:

0x95d2008, 0x95d2018, 0x95d2028 

đó cho thấy rằng 16 byte được dành riêng khi 12 được yêu cầu và phân bổ cho các chương trình. Trong cả hai trường hợp, cả hai cuộc gọi calloc(a,b)calloc(b,a) đều có cùng tác dụng đối với việc sử dụng bộ nhớ.


Được thêm bởi Jonathan Leffler vì 300 ký tự sẽ không bao giờ đủ.

xem xét chương trình này, trong đó rò rỉ bộ nhớ như cái rây thực, nhưng chứng tỏ một điểm:

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

int main() 
{ 
    int i, j, k; 

    for (i = 1; i < 17; i++) 
     for (j = 1; j < 9; j++) 
      for (k = 0; k < 4; k++) 
       printf("(%2d,%d)%d: %p\n", i, j, k, calloc(i, j)); 
    return(0); 
} 

Trên Windows, dưới Cygwin, điều này bắt đầu bằng cách phân bổ khối 16 byte ngoài (trên thực tế, khối thứ hai là 24 byte sau lần đầu tiên, nhưng sau đó, chúng cách nhau 16 byte).Khi phân bổ (2,7), các địa chỉ khối bắt đầu tăng thêm 24 byte; tương tự, (3,4) phân bổ các khối cách nhau 16 byte, nhưng (3,5) phân bổ các khối cách nhau 24 byte. Và, đối với bản ghi, cả hai (4,6) và (6,4) trở lại con trỏ cách nhau 32 byte.

Điều này chỉ đơn giản là chứng minh rằng có một số chi phí liên quan đến cuộc gọi phân bổ. Nếu bạn nhìn vào việc thực hiện nguyên mẫu của malloc() et al trong K & R, bạn sẽ thấy rằng kích thước của khối được lưu trữ trước bộ nhớ mà bạn có quyền sử dụng. Các triển khai khác nhau thực hiện những điều này một cách khác nhau; những người lo lắng về việc chà đạp bộ nhớ sẽ tránh lưu trữ dữ liệu kiểm soát gần nơi người dùng có thể tàn phá.

Khi bạn gọi (4.6), bạn chỉ có quyền truy cập đáng tin cậy vào 24 byte dữ liệu. Ngay cả khi việc triển khai của bạn cung cấp cho bạn các giá trị trả về cách nhau 32 byte, bạn có thể không sử dụng an toàn bất kỳ hơn 24 byte mà bạn đã yêu cầu. Và gỡ lỗi các phiên bản của malloc() sẽ quan sát nếu bạn viết ra khỏi giới hạn mà bạn yêu cầu.

+0

Điều đó chỉ cho thấy rằng có ít nhất 4 byte trên đầu và phân bổ được lượng tử hóa. Mỗi phân bổ bộ nhớ thường bao gồm một số chi phí (ví dụ, kích thước của khối được cấp phát được viết ngay trước con trỏ trả về - các chi tiết khác nhau theo cách thực hiện). –

+0

Điểm được thực hiện. Tôi đoán tôi đã không nói rõ ràng nhưng tôi muốn cho thấy rằng 'hiệu ứng' của calloc (a, b) và calloc (b, a) là hoàn toàn giống nhau. – PolyThinker

10

Để những câu trả lời xuất sắc được đăng, tôi muốn thêm một điểm nữa của sự khác biệt giữa việc sử dụng calloc(nelem, elsize) so malloc(nelem * elsize): triển khai chất lượng calloc sẽ đảm bảo rằng nếu nelem của bạn và elsize đã đủ lớn để gây ra một lỗi tràn số nguyên khi nhân với nhau, nó sẽ thất bại hơn là gây ra một phân bổ dưới mức, như là một lời kêu gọi malloc ngây thơ sẽ.

Chỉ tính năng này thôi cũng đủ để tôi thích calloc đến malloc. Background reading.

1

Có một cách khác để xem xét câu hỏi này.

Thư viện GNU C định nghĩa calloc như thế này:

void * __libc_calloc (size_t n, size_t elem_size) 
{ 
    // ... (declarations) 

    /* size_t is unsigned so the behavior on overflow is defined. */ 
    bytes = n * elem_size; 
#define HALF_INTERNAL_SIZE_T \ 
    (((INTERNAL_SIZE_T) 1) << (8 * sizeof (INTERNAL_SIZE_T)/2)) 
    if (__builtin_expect ((n | elem_size) >= HALF_INTERNAL_SIZE_T, 0)) 
    { 
     if (elem_size != 0 && bytes/elem_size != n) 
     { 
      __set_errno (ENOMEM); 
      return 0; 
     } 
    } 

    void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook); 
    if (__builtin_expect (hook != NULL, 0)) 
    { 
     sz = bytes; 
     mem = (*hook)(sz, RETURN_ADDRESS (0)); 
     if (mem == 0) 
     return 0; 

     return memset (mem, 0, sz); 
    } 

    sz = bytes; 

    // ...more stuff, but no mention of n & elem_size anymore 
} 

Vì vậy, ít nhất là trong glibc hai cuộc gọi này không có tác dụng giống hệt nhau.

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