2010-11-21 44 views
21

Tôi đã luôn luôn được lập trình trong Java, mà có lẽ là lý do tại sao tôi rất bối rối về điều này:Tôi rất bối rối về malloc() và calloc() trên C

Trong Java Tôi tuyên bố một con trỏ:

int[] array 

và khởi tạo hoặc chỉ định một số bộ nhớ:

int[] array = {0,1,0} 
int[] array = new int[3] 

Bây giờ, trong C, đó là tất cả rất khó hiểu. Lúc đầu, tôi nghĩ rằng nó là dễ dàng như việc tuyên bố nó:

int array[] 

và khởi tạo nó hay gán cho nó một số bộ nhớ:

int array[] = {0,1,0} 
int array[] = malloc(3*sizeof(int)) 
int array[] = calloc(3,sizeof(int)) 

Trừ khi tôi là sai, tất cả những điều trên là tương đương Java-C , đúng?

Sau đó, hôm nay tôi gặp một mã trong đó tôi thấy như sau:

pthread_t tid[MAX_OPS]; 

và một số dòng dưới đây, mà không cần bất kỳ loại khởi ...

pthread_create(&tid[0],NULL,mou_usuari,(void *) 0); 

Đáng ngạc nhiên (ít nhất là tới tôi), mã hoạt động! Ít nhất trong Java, điều đó sẽ trả về một "NullPointerException" tốt đẹp!

Vì vậy, theo thứ tự:

  1. Am I correct với tất cả các "bản dịch" Java-C?

  2. Tại sao mã đó hoạt động?

  3. Có sự khác biệt nào giữa việc sử dụng malloc(n*sizeof(int))calloc(n,sizeof(int)) không?

Cảm ơn trước

Trả lời

32

Bạn không thể chỉ định bộ nhớ cho một mảng. Một mảng có kích thước cố định, cho toàn bộ tuổi thọ của nó. Một mảng không bao giờ có thể rỗng. Một mảng không phải là một con trỏ.

malloc trả về địa chỉ cho khối bộ nhớ được dành riêng cho chương trình. Bạn không thể "gán" (là khối bộ nhớ) cho một mảng, nhưng bạn có thể lưu trữ địa chỉ của khối bộ nhớ này trong một con trỏ: may mắn, đăng ký mảng được định nghĩa thông qua con trỏ - vì vậy bạn có thể "sử dụng con trỏ như mảng" , ví dụ

int *ptr = malloc(5 * sizeof *ptr); 
ptr[2] = 5; // access the third element "of ptr" 
free(ptr); // always free at the end 

Khi bạn khai báo một mảng mà không có một kích thước (ví dụ: array[]), nó chỉ đơn giản có nghĩa là kích thước của mảng được xác định trong danh sách khởi tạo. Đó là

int array[] = {1, 2, 3, 4, 5}; // is equal to 
int array[5] = {1, 2, 3, 4, 5}; 

Đang cố gắng để khai báo một mảng mà không có một kích thước và không có một khởi tạo là một lỗi.


pthread_t tid[MAX_OPS]; tuyên bố một mảng tên tid loại pthread_t và kích thước MAX_OPS. Nếu mảng có lưu trữ tự động (tức là khai báo nằm bên trong một hàm chứ không phải tĩnh, không phải toàn cục), thì mỗi phần tử mảng có giá trị không xác định (và nó sẽ khiến hành vi không xác định cố đọc giá trị đó). May mắn thay, tất cả những gì mà hàm gọi là nó lấy địa chỉ của phần tử đầu tiên của mảng làm tham số đầu tiên, và có thể khởi tạo nó (phần tử) bên trong hàm.


Sự khác biệt của callocmalloc là các khối bộ nhớ mà calloc lợi nhuận được khởi tạo bằng không.Đó là;

int *ptr = calloc(5, sizeof *ptr); 
// is somewhat equal to 
int *ptr = malloc(5 * sizeof *ptr); 
memset(ptr, 0, 5 * sizeof *ptr); 

Sự khác biệt giữa

int *ptr = malloc(5 * sizeof *ptr); 
// and 
int array[5]; 

array có lưu trữ tự động, (được lưu trữ trên stack), và được "phát hành" sau khi nó đi ra khỏi phạm vi. Tuy nhiên, ptr (được lưu trữ trên heap), được phân bổ động và phải được lập trình viên free d.

+1

Đoạn 1 có một số xác nhận không rõ ràng nguy hiểm. OP không cố gán bộ nhớ cho một mảng, anh ta đang cố gán một (void *), trả về từ malloc() cho một mảng, và nếu mảng đó là int * Array [i], có lẽ trong một cho vòng lặp {}, nó sẽ hoạt động tốt và là cơ sở cho các mảng đa chiều động, được cấp phát ra khỏi vùng heap như thế nào. Ngoài ra, C99 hỗ trợ các mảng có kích thước biến được cấp phát khỏi ngăn xếp, một vài tính năng C lập trình viên sử dụng, hầu hết thích alloca(), bao gồm cả tôi. http://stackoverflow.com/q/1018853/2548100 – user2548100

+1

calloc() là khá nhiều chỉ là memset (malloc (n * mysize), 0, (n * mysize)). Đặc biệt là vì C sử dụng các chuỗi được kết thúc bằng null, calloc() rất hữu ích, đặc biệt khi xem các chuỗi trong một trình gỡ lỗi, thường chỉ hiển thị chuỗi chỉ tới trình kết thúc null. Nếu bạn chỉ cần nói ra với C, hãy sử dụng calloc thay vì malloc, nó sẽ giúp bạn tiết kiệm được rất nhiều lỗi chuỗi C không kết thúc mà có thể và có thể sẽ làm hỏng chương trình của bạn. Đối với mã sản xuất/phát hành, chỉ sử dụng calloc() khi bạn thực sự cần khởi tạo bộ đệm/mảng/vectơ tới (_int8) 0. – user2548100

+7

Chỉ để bọc mọi thứ lên, và để hoàn thành, một mảng là một con trỏ. Trong thực tế, bất kỳ tên mảng nào trong C là chính xác, chính xác là một con trỏ đến cơ sở của byte đầu tiên của đối tượng thứ nhất trong mảng và không có gì hơn. Đối với những người đến từ Java,.Net, vv, nó rất hữu ích để biết rằng C giữ loại đối tượng/biến hoàn toàn tách biệt với lưu trữ được phân bổ để giữ chúng. Đây là lý do tại sao bạn có thể đúc một con trỏ như là một int, tạo UNIONs, vv Rất, rất linh hoạt, nhưng nguy hiểm cho noobies. Khi bạn cấp phát một mảng int, nó chỉ lưu trữ tại một vị trí. Bạn có thể đặt bất cứ thứ gì bạn thích vào bộ nhớ đó. – user2548100

-2

tôi thấy hữu ích khi bạn đang lập trình trong C (như trái ngược với C++) để chỉ * mảng rõ ràng, phải nhớ rằng có một con trỏ có thể được di chuyển xung quanh. Vì vậy, tôi muốn bắt đầu bằng cách rephrasing ví dụ của bạn như:

int array[] = {0,1,2}; 
int *array = malloc(3*sizeof(int)); 
int *array = calloc(3,sizeof(int)); 

Đầu tiên làm cho nó rõ ràng rằng có cái gì đó gọi là mảng mà được trỏ đến một khối bộ nhớ có chứa một 0, 1 và 2. mảng có thể' t được di chuyển elesewhere.

Mã tiếp theo của bạn: pthread_t tid [MAX_OPS];

Thực tế có gây ra mảng có sizeof (pthread_t) * MAX_OPS được cấp phát hay không. Nhưng nó không phân bổ một con trỏ gọi là * tid. Có một địa chỉ của cơ sở của mảng, nhưng bạn không thể di chuyển nó ở nơi khác.

Loại ptherad_t thực ra là trang bìa cho con trỏ. Vì vậy, tid ở trên thực sự là một mảng con trỏ. Và tất cả chúng đều được phân bổ tĩnh nhưng chúng không được khởi tạo.

pthread_create lấy vị trí ở đầu mảng (&tid[0]), là một con trỏ và phân bổ khối bộ nhớ để giữ cấu trúc dữ liệu pthread. Con trỏ được đặt để trỏ đến cấu trúc dữ liệu mới và cấu trúc dữ liệu được cấp phát.

Câu hỏi cuối cùng của bạn --- sự khác biệt giữa malloc(n*sizeof(int))calloc(n,sizeof(int)) là sau đó khởi tạo mỗi byte thành 0, trong khi lần đầu tiên không.

+0

Vì vậy, nếu tôi tuyên bố: int array [] có bộ nhớ đã được cấp phát chưa? Sau đó nó giống với khai báo con trỏ và sau đó sử dụng malloc? cảm ơn một lần nữa – bluehallu

+0

@ Hallucynogenyc: Không, nó không giống nhau. int array [size] được cấp phát khỏi stack. int array [] = malloc() nằm trên heap. – Puppy

+2

Trong C, đầu tiên của 3 dòng này đơn giản là * không hợp lệ *. Nó sẽ không biên dịch. –

0

2 - Tờ khai mảng này là tĩnh:

pthread_t tid[MAX_OPS]; 

Chúng tôi không cần phải phân bổ khối bộ nhớ, thay vì phân bổ động:

pthread_t *tid = (pthread_t *)malloc(MAX_OPS * sizeof(pthread_t)); 

Đừng quên để giải phóng bộ nhớ:

free(tid); 

3 - Sự khác biệt giữa malloc và calloc là calloc phân bổ khối bộ nhớ cho mảng và khởi tạo tất cả các bit của nó tại 0.

+0

Vì vậy, sự khác biệt giữa thứ nhất và thứ hai là gì? Và tại sao bạn lại đúc một con trỏ dòng thứ hai? Xin lỗi nếu tôi có vẻ ngu ngốc, nhưng điều này hoàn toàn mới đối với tôi ... – bluehallu

+0

Ok, tôi vừa thấy tại sao bạn lại chọn. Tuy nhiên, có bất kỳ sự khác biệt thực tế giữa dòng đầu tiên và thứ hai một phần từ đó bạn có thể "di chuyển" con trỏ đến bất cứ điều gì bạn muốn? – bluehallu

+0

Một khai báo tĩnh an toàn hơn một khai báo động nhưng bạn không thể tái phân bổ khối bộ nhớ của mình để thay đổi kích thước của nó. –

1

C cung cấp phân bổ bộ nhớ tĩnh cũng như động, bạn có thể phân bổ mảng khỏi ngăn xếp hoặc trong bộ nhớ thực thi (được trình biên dịch quản lý). Điều này cũng giống như trong Java, bạn có thể cấp phát một int trên stack hoặc một Integer trên heap. Mảng trong C giống như bất kỳ biến ngăn xếp nào khác, chúng nằm ngoài phạm vi, vv Trong C99, chúng cũng có thể có kích thước biến, mặc dù chúng không thể thay đổi kích cỡ.

Sự khác biệt chính giữa {} và malloc/calloc là {} mảng được phân bổ tĩnh (không cần giải phóng) và tự động được khởi tạo cho bạn, trong khi mảng malloc/calloc phải được giải phóng một cách rõ ràng và bạn phải khởi tạo chúng một cách rõ ràng. Nhưng tất nhiên, các mảng malloc/calloc không nằm ngoài phạm vi và bạn có thể (đôi khi) realloc() chúng.

+1

Các mảng chỉ tĩnh nếu nằm ngoài bất kỳ hàm nào hoặc được đánh dấu rõ ràng là 'tĩnh'; nếu không chúng tự động là –

3

Bạn đang thiếu ba rất cơ bản và thắt chặt C chủ đề (và gây hiểu nhầm!):

  • sự khác biệt giữa mảng và con trỏ
  • sự khác biệt giữa tĩnh và động phân bổ
  • sự khác biệt từ các biến khai báo trên ngăn xếp hoặc trên heap

Nếu bạn viết int array[] = malloc(3*sizeof(int)); bạn sẽ gặp lỗi biên dịch (ví dụ: 'định danh': khởi tạo mảng cần dấu ngoặc nhọn).

này có nghĩa là khai báo một mảng cho phép khởi tạo chỉ tĩnh:

  • int array[] = {1,2,3}; rằng bảo lưu 3 số nguyên tiếp giáp trên stack;
  • int array[3] = {1,2,3}; giống với trang trước;
  • int array[3]; mà vẫn bảo lưu 3 số nguyên tiếp giáp trên stack, nhưng không khởi tạo chúng (nội dung sẽ được rác ngẫu nhiên)
  • int array[4] = {1,2,3}; khi danh sách initializer không khởi tạo tất cả các yếu tố, phần còn lại được đặt thành 0 (C99 §6.7.8/19): trong trường hợp này bạn sẽ nhận được 1,2,3,0

Lưu ý rằng trong tất cả những trường hợp này bạn không phải phân bổ bộ nhớ mới, bạn chỉ cần sử dụng bộ nhớ đã cam kết với chồng. Bạn sẽ chạy trong một vấn đề chỉ khi ngăn xếp đầy (đoán nó, nó sẽ là một ngăn xếp ngăn xếp ). Vì lý do này tuyên bố int array[]; sẽ là sai và vô nghĩa.

Để sử dụng malloc, bạn phải khai báo con trỏ: int* array.

Khi bạn viết int* array = malloc(3*sizeof(int)); bạn đang thực sự làm ba hoạt động:

  1. int* array nói với trình biên dịch để đặt con trỏ trên stack (một biến số nguyên có chứa một địa chỉ bộ nhớ)
  2. malloc(3*sizeof(int)) phân bổ trên heap 3 số nguyên tiếp giáp và trả về địa chỉ của số đầu tiên
  3. = gán các bản sao trả về giá trị (địa chỉ của số nguyên đầu tiên bạn đã cấp) cho biến con trỏ

Vì vậy, để trở về với câu hỏi của bạn:

pthread_t tid[MAX_OPS]; 

là một mảng trên stack, vì vậy nó không cần phải được phân bổ (nếu MAX_OPS là, nói, 16 sau đó vào ngăn xếp sẽ được đặt trước số byte liền kề cần thiết để vừa với 16 pthread_t). Nội dung của bộ nhớ này sẽ là rác (các biến ngăn xếp không được khởi tạo bằng 0), nhưng pthread_create trả về một giá trị trong tham số đầu tiên của nó (một con trỏ đến biến số pthread_t) và bỏ qua bất kỳ nội dung nào trước đó, vì vậy mã chỉ là tốt.

+4

đối với 'int array [4]', tất cả chúng đều được khởi tạo. Khi danh sách bộ khởi tạo không khởi tạo tất cả các phần tử, phần còn lại được đặt thành 0/NULL (C99 §6.7.8/19). –

+0

Điều này gây nhầm lẫn; "heap" và "phân bổ động" tham chiếu đến cùng một điều. "khởi tạo tĩnh" có nghĩa là khởi tạo các biến tĩnh, mà không phải là trường hợp khi nói về cái gọi là các biến "ngăn xếp". Loại phân bổ trong 'int array [3];' bên trong một hàm, là "phân bổ tự động" (hoặc "ngăn xếp" không chính thức, một số hệ thống không có ngăn xếp), không phải là "tĩnh". –

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