2010-05-23 39 views
14

Tôi là một trợ lý giảng dạy của một khóa học lập trình giới thiệu, và một số sinh viên làm này là loại lỗi:"Địa chỉ của" (&) một mảng/địa chỉ bị bỏ qua là gcc?

char name[20]; 
scanf("%s",&name); 

mà không có gì ngạc nhiên khi họ đang học ... Điều gì là đáng ngạc nhiên là, bên cạnh cảnh báo gcc, mã hoạt động (ít nhất là phần này). Tôi đã cố gắng để hiểu và tôi đã viết đoạn mã sau:

void foo(int *v1, int *v2) { 
    if (v1 == v2) 
    printf("Both pointers are the same\n"); 
    else 
    printf("They are not the same\n"); 
} 

int main() { 
    int test[50]; 
    foo(&test, test); 
    if (&test == test) 
    printf("Both pointers are the same\n"); 
    else 
    printf("They are not the same\n"); 
} 

Biên soạn và thực hiện:

$ gcc test.c -g 
test.c: In function ‘main’: 
test.c:12: warning: passing argument 1 of ‘foo’ from incompatible pointer type 
test.c:13: warning: comparison of distinct pointer types lacks a cast 
$ ./a.out 
Both pointers are the same 
Both pointers are the same 

bất cứ ai có thể giải thích lý do tại sao họ không khác nhau?

Tôi nghi ngờ đó là vì tôi không thể lấy địa chỉ của một mảng (vì tôi không thể có & &x), nhưng trong trường hợp này mã không được biên dịch.

Chỉnh sửa: Tôi biết rằng một mảng tự nó giống với địa chỉ của phần tử đầu tiên, nhưng điều này không liên quan đến vấn đề này, tôi nghĩ vậy. Ví dụ:

int main() { 
    int a[50]; 
    int * p = a; 
    printf("%d %d %d\n", p == a, p == &a[0], &p[0] == a); 
    printf("%d %d %d\n", p == &a, &p == a, &p == &a); 
} 

in:

$ ./a.out 
1 1 1 
1 0 0 

Tôi không hiểu tại sao dòng thứ hai bắt đầu với 1.

+1

Chỉnh sửa của bạn cho biết rằng bạn cho rằng có một số con trỏ được lưu trữ cùng với mảng. Nhưng đó không phải là trường hợp. Một mảng bao gồm * chỉ * của các phần tử của nó. –

+0

Tôi biết rằng một mảng chỉ là các phần tử, đó là lý do tại sao tôi nghĩ rằng nó không nên biên dịch. – dbarbosa

Trả lời

22

Trong ví dụ của bạn, mảng test là một khối 50 ints. Vì vậy, nó trông như thế này:

| int | int | ... | int | 

Khi bạn áp dụng các & hành unary đến một mảng, bạn sẽ có được địa chỉ của mảng. Cũng giống như khi bạn áp dụng nó cho bất cứ điều gì khác, thực sự. Vì vậy &test là một con trỏ trỏ đến đó khối 50 ints:

(&test) -----------> | int | int | ... | int | 

Một con trỏ trỏ đến một mảng của 50 ints đã gõ int (*)[50] - đó là loại &test.

Khi bạn chỉ sử dụng tên test ở bất kỳ vị trí nào không phải là toán hạng của các toán tử sizeof hoặc unary- &, nó được đánh giá là con trỏ đến phần tử đầu tiên. Vì vậy, các test mà bạn vượt qua để foo() để đánh giá một con trỏ đến các test[0] phần tử:

(test) -----------------\ 
         v 
(&test) -----------> | int | int | ... | int | 

Bạn có thể thấy rằng những cả hai đều trỏ đến cùng một địa chỉ - mặc dù &test được trỏ đến toàn bộ mảng, và test trỏ thành phần tử đầu tiên của mảng (chỉ hiển thị trong các loại khác nhau mà các giá trị đó có).

+0

+1; đôi khi một bức tranh có giá trị 1.000 từ :-) –

+6

Tôi thích nghệ thuật ascii :) Bây giờ với pic đó, thật dễ dàng để giải thích tại sao thêm '1' vào' & test' di chuyển ra ngoài mảng, và thêm '1' vào' test' chỉ di chuyển ra ngoài phần tử đầu tiên. –

+0

Sau đó tôi phải thay đổi "câu trả lời được chấp nhận"! – dbarbosa

9

Thực ra, chúng khác nhau, chúng không có cùng loại ít nhất.

Nhưng trong C, địa chỉ của mảng giống với địa chỉ của thành phần đầu tiên trong mảng là lý do tại sao "chúng không khác", về cơ bản, chúng trỏ đến cùng một thứ.

+0

"địa chỉ của mảng giống với địa chỉ của phần tử đầu tiên trong mảng" Tôi luôn biết rằng mảng của chính nó (và * không * địa chỉ của mảng) giống như địa chỉ của phần tử đầu tiên, tức là 'test' giống hệt' & test [0] '. Tôi vừa mới đăng ký K & R và đó là nội dung được viết ở đó ... Tôi sẽ chỉnh sửa câu hỏi của mình để thêm câu hỏi này. – dbarbosa

+3

@WhirlWind: Không, nó * không * phụ thuộc vào hệ thống. Việc áp dụng toán tử địa chỉ cho một mảng có ngữ nghĩa được xác định rõ ràng - nó phải tạo ra địa chỉ của chính mảng đó. Mảng được định nghĩa là một chuỗi các đối tượng liên tiếp của kiểu cơ sở, và do đó địa chỉ của mảng và địa chỉ của đối tượng đầu tiên là cùng một vị trí (nhưng với các kiểu khác nhau). – caf

-2

Tôi tin rằng đây là tối ưu hóa gcc. Hãy suy nghĩ về nó.

  • &test điểm đến địa chỉ của test
  • test điểm đến phần tử đầu tiên của test hoặc &test[0]
  • [0] là như nhau (đối với hầu hết các phần) như *

Vì vậy, theo này &testcó thể khác với test nhưng tối ưu hóa gcc izes này đi bởi vì không có mục đích của việc có thêm một mức độ indirection tại thời điểm đó.

+2

Nó không phải là một tối ưu hóa gcc - đó là hành vi đó là yêu cầu của tiêu chuẩn C. – caf

+0

Có vẻ như @Earlz đã nhầm lẫn ý nghĩa và kiểm tra chính xác như tôi. – dbarbosa

+1

@ dba wow tôi đã.Câu trả lời được chấp nhận cho điều này hoàn toàn xóa nó lên – Earlz

5

Nếu bạn xác định một mảng như

char name[20]; 

name là mặc nhiên chuyển đổi thành char*, nhưng &name là loại char (*)[20] (một con trỏ đến một mảng của 20 ký tự). Các địa chỉ giống nhau.

Kiểm tra địa chỉ (&name + 1). Nó khác với hình thức &name theo số sizeof(char [20]).

+0

Cảm ơn, bạn đã làm cho nó dễ dàng hơn để hiểu James McNellis câu trả lời. – dbarbosa

+0

Niềm vui là tất cả của tôi :) –

2

Tên của một mảng, trong hầu hết các trường hợp, đánh giá đến địa chỉ của phần tử ban đầu của nó. Hai trường hợp ngoại lệ là khi nó là toán hạng của sizeof hoặc số & đơn nhất.

Unary & cung cấp địa chỉ của đối số của nó. Địa chỉ của một mảng giống như địa chỉ của phần tử ban đầu của nó, vì vậy (void*)&array == (void*)array sẽ luôn đúng.

array, khi được chuyển thành con trỏ thành phần tử ban đầu, có loại T *. Loại &arrayT (*)[n], trong đó n là số phần tử trong mảng. Do đó,

int* p = array;  // Works; p is a pointer to array[0] 
int* q = &array;  // Doesn't work; &array has type int (*)[10] 
int (*r)[10] = &array; // Works; r is a pointer to array 
+0

Cảm ơn bạn, tôi chỉ biết ngoại lệ của sizeof. – dbarbosa

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