2015-08-13 14 views
6

tôi đã xác định mảng trong các định dạng sau, nhưng dường như các chương trình hoạt động tốt chỉ trong trường hợp B.mơ hồ trong việc kê khai mảng 2ngày trong C

TRƯỜNG HỢP A:

int **M1; 
M1 = (int **)malloc(m * sizeof(int *)); 
for (int i=0; i<m; i++){ 
    M1[i] = (int *)malloc(d * sizeof(int)); 
} 

TRƯỜNG HỢP B:

int (*M1)[m] = malloc(sizeof(int[m][d])); 

Tôi nhận được lỗi phân đoạn trong TRƯỜNG HỢP A. Điều gì có thể là lý do?

+2

Mã A hoạt động hoàn hảo cho tôi. Môi trường của bạn là gì? – blue112

+1

Trường hợp bạn nhận được lỗi phân đoạn chính xác ở đâu? –

+0

[Xin đừng bỏ kết quả của 'malloc()'] (http://stackoverflow.com/a/605858/3233393). – Quentin

Trả lời

3

Các mã sau biên dịch mà không có lỗi hoặc cảnh báo trong gcc trình biên dịch,

int **M1,i; 
    M1 = (int **)malloc(5 * sizeof(int *)); 
    for (i=0;i<5;i++){ 
     M1[i] = (int *)malloc(2 * sizeof(int)); 
    } 
    M1[0][0]=111; 
    M1[0][1]=222; 
    printf("\n%d %d",M1[0][0],M1[0][1]); 

Cho 111 222 như đầu ra.

Sự cố có thể là một số nơi khác trong mã của bạn.

1

TRƯỜNG HỢP A;

M1 là con trỏ trỏ đến con trỏ. Bạn đang cấp phát bộ nhớ cho một mảng 2D loại int và bạn có thể lưu trữ các giá trị m*d trong mảng.

M1[0][0] to M[m-1][d-1] là truy cập hợp lệ cho mảng này để nhận giá trị loại int và bất kỳ điều gì khác ngoài điều này sẽ dẫn đến hành vi không xác định.

Trường hợp B:

M1 là một con trỏ đến một mảng kiểu int trong đó có m yếu tố.

Trong trường hợp A, lỗi phân đoạn có nhiều khả năng nhất là vì mảng ngoài truy cập bị ràng buộc

+0

Không, ông phân bổ bộ nhớ cho 'm' số' int * ', đó là hoàn toàn tốt đẹp. Nó cũng hoàn toàn tốt để truy cập 'M1 [0] ', bạn chỉ nhận được một' int * ', không phải là' int'. – Jite

+0

@ Jite Tôi nghĩ bạn đã hiểu lầm câu trả lời của tôi. Yeah 'int *' là tốt nhưng tôi có nghĩa là giá trị int hợp lệ có thể truy cập là 'M1 [0] [0]' tới 'M1 [4] [4]' – Gopi

+1

Bạn lấy '[4] [4]' từ đâu? Chúng ta không biết giá trị của 'm' hay' d'? – Jite

1

Một con trỏ và một mảng là những thứ khác nhau. Một con trỏ tới một kiểu chứa địa chỉ nơi một biến kiểu khai báo được cấp phát.

Mảng thuộc loại là không gian bộ nhớ trong đó các biến của loại được khai báo là được lưu trữ liên tục.

Bây giờ bạn đang khai báo. Không có sự mơ hồ (ngay cả khi không hiển nhiên) trong các khai báo C. Trong trường hợp Một bạn đã tuyên bố:

int **M1; 

Đây không phải là một mảng 2d, thậm chí không phải là một monodimensional. Bạn đang khai báo một biến, M!, như một con trỏ trỏ đến một con trỏ khác đến một int. Trong từ phẳng M1 sẽ giữ địa chỉ của một biến khác mà lần lượt giữ địa chỉ của nơi trong bộ nhớ được lưu trữ một số nguyên. Bây giờ thực hiện:

M1 = (int **)malloc(m * sizeof(int *)); 

Bạn gán cho M1 địa chỉ của một khu vực bộ nhớ có thể lưu trữ lên đến m con trỏ số nguyên tiếp giáp, quyền truy cập vào bộ nhớ được trỏ bởi M1, và liên tiếp các địa điểm hoạt động như một truy cập mảng (nhưng không phải).Đây là nhiều hay ít tương đương với việc khai báo tĩnh:

int *[m]; //an array of pointers to int 

Sau đó gán mỗi yếu tố này pseudo-mảng một bộ nhớ lưu trữ cho d số nguyên tiếp giáp:

for (int i=0; i<m; i++) 
{ 
    M1[i] = (int *)malloc(d * sizeof(int)); 
} 

bây giờ bạn có chỗ để lưu trữ d liên tiếp số nguyên bắt đầu tại địa chỉ được lưu trong M1[i] cho i = 0-> d-1.

Điều gì sẽ xảy ra khi bạn cố truy cập một giá trị sử dụng các bảng con: M1[a][b]? Trình biên dịch truy lục địa chỉ được chỉ bởi M1 và sử dụng chỉ số đầu tiên (a), truy xuất nội dung được chỉ bởi địa chỉ trong vị trí atht của mảng con trỏ. Điều này trỏ đến số nguyên đầu tiên của không gian con mà chúng tôi đã phân bổ để giữ d int liên tục. Đây thực sự là một mảng intodimensional của int. Áp dụng các subscript thứ hai cho nó trình biên dịch cuối cùng lấy số nguyên cần thiết. Cose để một mảng địa chỉ bidimensional, nhưng không có chuối! Nó không phải là một mảng giá trị của int. :-)

Trong trường hợp B bạn đang tuyên bố một con trỏ đến một mảng monodimensional của int, mà bạn đang gán đủ không gian để giữ mảng hai chiều của bạn. Bởi vì trong C không tồn tại khái niệm về mảng đa chiều, nhưng một nguyên tắc cơ bản của mảng của mảng của mảng .... vv (ad-libitum), tuyên bố:

int (*M1)[m] = malloc(sizeof(int[m][d])); 

như một con trỏ đến modimensiional mảng có không gian được phân bổ cho một mảng gồm các phần tử [m][d] đã thực hiện thủ thuật.

Nhưng tất nhiên cả hai giải pháp đều sai!

Bạn đang sử dụng các hiệu ứng phụ để có được những gì bạn muốn, nhưng sử dụng thay thế những gì bạn đã tuyên bố cần: một mảng giá trị của int.

Giải pháp chính xác là để xác định một con trỏ đến một mảng hai chiều các số nguyên nơi chỉ subscript đầu tiên được yêu cầu:

int (*M1)[][m]; //declare a pointer to a real array 

M1 = malloc(m * d * sizeof(int)); //Allocate room for bidimensional array 

for (int i=0; i<m; i++) 
    for (int j=0; j<d; j++) 
    { 
     (*M1)[i][j] = (i*100)+j; //Fill elements with something using bidimensional subscripting 
    } 
for (int i=0; i<m; i++) 
    for (int j=0; j<d; j++) 
    { 
     printf("[%d][%d]=%d\n", i, j, (*M1)[i][j]); //Check back data... 
    } 

Bây giờ có một cái nhìn vào những gì sẽ xảy ra nếu bạn đi ra ngoài giới hạn trong 3 các trường hợp. Trong trường hợp Một nếu bạn nhận được ngoài giới hạn với subscript đầu tiên bạn sẽ thu thập một con trỏ sai rằng sẽ gây ra ngay lập tức một bộ nhớ lỗi, trong trường hợp B bạn sẽ có lỗi bộ nhớ chỉ khi bạn đi ra khỏi quá trình bộ nhớ địa chỉ, giống nhau cho giải pháp đúng. Điều này sẽ trả lời cho câu hỏi của bạn.

cuối, bởi vì chúng ta đang nói về misunderstandigs về mảng và con trỏ, đừng hiểu sai tiêu chuẩn ISO 9899: 2011§6.7.6.3/7 nói rằng:

Một tuyên bố của một tham số như '' mảng loại '' sẽ được điều chỉnh thành '' con trỏ đủ điều kiện để nhập '', trong đó các loại vòng loại (nếu có) là các giá trị được chỉ định trong [và] của dẫn xuất loại mảng.Nếu từ khóa tĩnh cũng xuất hiện trong [và] của loại mảng dẫn xuất, sau đó cho mỗi cuộc gọi đến hàm, giá trị của đối số thực tế tương ứng sẽ cung cấp quyền truy cập vào thành phần đầu tiên của một mảng có ít nhất là nhiều phần tử được chỉ định bởi biểu thức kích thước .

Nó chỉ đơn giản nói rằng mảng sẽ chỉ được thông qua bằng cách tham khảo (tự động điều chỉnh bởi trình biên dịch), không bao giờ theo giá trị (có nghĩa là pháp luật cho struct tức). Bạn không được yêu cầu cung cấp bất kỳ con trỏ nào đủ điều kiện thay vì một mảng trong cuộc gọi hàm hoặc chương trình sẽ sụp đổ (Tuyên bố tham số là '' mảng kiểu '' sẽ được điều chỉnh thành '' con trỏ đủ điều kiện để nhập '' - có nghĩa là sẽ được điều chỉnh bởi trình biên dịch, không phải bởi bạn). Các trường hợp như char *[]char ** hoạt động vì lý do được báo cáo ở trên, không phải vì việc trao đổi chúng là hợp pháp!