2015-01-10 21 views
16

Tôi đã đăng bài này question trên programmers.stackexchange sớm hơn hôm nay. Tôi đã luôn luôn giả định rằng int (*)[] không không phân rã thành int ** trong thông số chức năng nhưng tôi nhận được nhiều câu trả lời cho câu hỏi của tôi cho rằng nó có.Liệu "int (*) []" phân rã thành "int **" trong tham số hàm?

Tôi đã sử dụng int (*)[] rất nhiều trong các thông số chức năng của mình nhưng bây giờ tôi đã trở nên thực sự bối rối.

Khi tôi biên dịch chức năng này sử dụng gcc -std=c99 -pedantic -Wall

void function(int (*a)[]) 
{ 
    sizeof(*a); 
} 

tôi nhận được thông báo lỗi này:

c99 -Wall -pedantic -c main.c -o main.o 
main.c: In function ‘function’: 
main.c:3:11: error: invalid application of ‘sizeof’ to incomplete type ‘int[]’ 
make: *** [main.o] Error 1 

Những gợi ý rằng *a có loại int [] và không int *.

Ai đó có thể giải thích nếu những thứ như int (*)[] phân rã thành int ** trong tham số chức năng và cho tôi một số tham chiếu (từ tài liệu chuẩn).

+0

Tôi không nghĩ vậy, nhưng tôi không chắc chắn. –

+9

Không. Chỉ mảng và chức năng phân rã. Con trỏ không phân rã. –

+0

(Thật thú vị, 'T (*) []' là một kiểu không đầy đủ mà không bao giờ có thể hoàn thành, không giống 'T []'.) –

Trả lời

18

Chỉ các loại mảng được chuyển đổi thành con trỏ thành phần tử đầu tiên khi được chuyển đến hàm. a là loại con trỏ đến một mảng int, tức là, là loại con trỏ và do đó không có chuyển đổi.

Đối với nguyên mẫu

void foo(int a[][10]); 

biên dịch giải thích nó như

void foo(int (*a)[10]); 

đó là bởi vì a[] là loại mảng. int a[][10] sẽ không bao giờ được chuyển đổi thành int **a. Điều đó nói rằng, đoạn thứ hai trong đó answer là sai và gây hiểu nhầm.

Là tham số chức năng, int *a[] tương đương với int ** điều này là do a là loại mảng.

+0

Có lẽ câu trả lời của bạn đã bị bỏ qua vì đó là mảng * mà không phân tách "* tên mảng *"? Tuy nhiên: Đó không phải là tôi! alk

+0

Lạm dụng: Không phải là "* loại *" phân rã, cũng không nhận được bất kỳ thứ gì "* đã chuyển đổi *" ... – alk

+0

@alk; * Decay * không được đề cập ở bất kỳ đâu trong tiêu chuẩn C. – haccks

7

int (*)[] là một con trỏ tới một mảng int.

Trong ví dụ của bạn, *acó thể phân rã thành int*. Nhưng sizeof(*a) không phân hủy; về cơ bản nó là sizeof(int[]) không hợp lệ.

a không thể phân rã nào cả (đó là con trỏ).

+0

Bạn có thể buộc nó phân rã: 'sizeof ((0, * a))' và nhận được kích thước của một con trỏ int. – 2501

7

N1256 §6.7.5.3/p7-8

7 Một tuyên bố của một tham số là '' mảng của loại '' sẽ điều chỉnh để '' con trỏ đủ điều kiện để loại '', nơi mà các vòng loại (nếu bất kỳ) là những thông số được chỉ định trong các số [] của dẫn xuất loại mảng. Nếu từ khóa static cũng xuất hiện trong các số [] 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 là . nhiều phần tử được chỉ định bởi biểu thức kích thước .

8 Một tuyên bố của một tham số là ‘‘chức năng trở loại’’ sẽ được điều chỉnh để ‘‘con trỏ đến hoạt động trở loại’’, như trong 6.3.2.1.

int (*)[] là "con trỏ tới mảng int". Có phải "mảng của loại" không? Không, đó là một con trỏ. Có phải "chức năng trả về loại" không? Không, đó là một con trỏ. Do đó nó không được điều chỉnh.

2

Trong trường hợp int (*a)[], sizeof *a không hoạt động vì một lý do: không có phần tử nào được tính cho mảng. Không có phần tử đếm, kích thước không thể được tính toán.

Kết quả là, bất kỳ số học con trỏ nào trên a sẽ không hoạt động vì nó được xác định theo kích thước của một đối tượng. Vì kích thước không xác định cho mảng, bạn không thể sử dụng số học con trỏ trên chính con trỏ. ký hiệu mảng được định nghĩa về con trỏ số học, vì vậy sizeof a[0][0] (hoặc bất kỳ biểu hiện liên quan đến a[n] sẽ không hoạt động trong khi sizeof (*a)[0] chí

này có hiệu quả có nghĩa là bạn có thể làm rất ít với con trỏ Những điều chỉ được phép là:..

  • dereferencing con trỏ bằng cách sử dụng unary * hành
  • đi qua các con trỏ đến một chức năng (kiểu của tham số chức năng phải là một mảng của mảng hoặc một con trỏ đến một mảng)
  • nhận được si ze của con trỏ (và liên kết hoặc loại, nếu trình biên dịch của bạn hỗ trợ một trong hai hoặc cả hai)
  • gán con trỏ đến một loại tương thích

Nếu trình biên dịch của bạn hỗ trợ mảng chiều dài thay đổi (Vlas), và bạn biết kích thước, bạn có thể làm việc xung quanh vấn đề này bằng cách thêm một dòng vào lúc bắt đầu của cơ quan chức năng như trong

void 
foo (int (*a0)[], size_t m, size_t n) 
{ 
    int (*a)[n] = a0; 
    ... 
} 

Without Vlas, bạn phải dùng đến một số biện pháp khác.

Điều đáng lưu ý là phân bổ động không phải là yếu tố với int (*)[].Một mảng các mảng phân rã thành một con trỏ tới một mảng (như chúng ta có ở đây), vì vậy chúng có thể hoán đổi cho nhau khi chuyển chúng đến một hàm (sizeof và bất kỳ từ khóa nào là toán tử, chứ không phải chức năng). Điều này có nghĩa là mảng được trỏ đến phải được phân bổ tĩnh: khi một mảng phân rã thành con trỏ, không có phân rã nào xảy ra, vì vậy bạn không thể nói con trỏ tới mảng (int (*)[]) giống như con trỏ trỏ tới một con trỏ (int **)). Nếu không trình biên dịch của bạn sẽ vui vẻ cho phép bạn vượt qua int [3][3] đến một hàm chấp nhận int ** thay vì muốn tham số của biểu mẫu int (*)[], int (*)[n], int [][n] hoặc int [m][n].

Do đó, ngay cả khi trình biên dịch của bạn không hỗ trợ Vlas, bạn có thể sử dụng thực tế là một mảng tĩnh được phân bổ có tất cả các yếu tố của nó nhóm lại với nhau:

void foo (int (*a0)[], size_t m, size_t n) 
{ 
    int *a = *a0; 
    size_t i, j; 

    for (i = 0; i < m; i++) 
    { 
     for (j = 0; j < n; j++) 
     { 
      // Do something with `a[i * n + j]`, which is `a0[i][j]`. 
     } 
    } 
    ... 
} 

Một cấp phát động một chiều mảng đó là được sử dụng như một mảng hai chiều có cùng thuộc tính, do đó, nó vẫn hoạt động. Chỉ khi thứ nguyên thứ hai được phân bổ động, nghĩa là một vòng lặp như for (i = 0; i < m; i++) a[i] = malloc (n * sizeof *a[i]); để phân bổ từng mảng phụ riêng lẻ, nguyên tắc này không hoạt động. Điều này là do bạn có một mảng con trỏ (int *[], hoặc int ** sau khi phân rã mảng), trỏ đến thành phần đầu tiên của mảng tại một vị trí khác trong bộ nhớ, chứ không phải mảng mảng, giữ tất cả các mục với nhau.

Vì vậy:

  • không, int (*p)[]int **q không thể được sử dụng trong cùng một cách. p là một con trỏ đến một mảng, có nghĩa là tất cả các mục được nhóm lại với nhau bắt đầu từ địa chỉ được lưu trữ trong p. q là con trỏ trỏ đến con trỏ, có nghĩa là các mục có thể được phân tán tại các địa chỉ khác nhau được lưu trữ trong q[0], q[1], ..., q[m - 1].

  • sizeof *p không hoạt động vì p trỏ đến một mảng có số phần tử không xác định. Trình biên dịch không thể tính toán kích thước của mỗi phần tử, do đó hoạt động trên p chính nó là rất hạn chế.

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