2010-03-27 26 views
151

Trong bit sau đây của mã, giá trị con trỏ và địa chỉ con trỏ khác nhau như mong đợi.Làm thế nào để một địa chỉ của mảng bằng với giá trị của nó trong C?

Nhưng giá trị và địa chỉ mảng thì không!

Làm cách nào để thực hiện điều này?

Output

my_array = 0022FF00 
&my_array = 0022FF00 
pointer_to_array = 0022FF00 
&pointer_to_array = 0022FEFC 
#include <stdio.h> 

int main() 
{ 
    char my_array[100] = "some cool string"; 
    printf("my_array = %p\n", my_array); 
    printf("&my_array = %p\n", &my_array); 

    char *pointer_to_array = my_array; 
    printf("pointer_to_array = %p\n", pointer_to_array); 
    printf("&pointer_to_array = %p\n", &pointer_to_array); 

    printf("Press ENTER to continue...\n"); 
    getchar(); 
    return 0; 
} 
+2

Tôi đã thêm một câu trả lời với sơ đồ cho câu hỏi này hai năm trở lại đây ['Trở về (& mảng)' return?] (Http://stackoverflow.com/a/15177499/1673391) –

Trả lời

161

Tên của một mảng thường đánh giá đến địa chỉ của phần tử đầu tiên của mảng, vì vậy array&array có cùng giá trị (nhưng loại khác nhau, vì vậy array+1&array+1 sẽ không bằng nếu mảng là hơn dài hơn 1 phần tử).

Có hai ngoại lệ cho điều này: khi tên mảng là toán hạng của sizeof hoặc đơn nhất & (địa chỉ), tên này đề cập đến chính đối tượng mảng đó. Do đó sizeof array cho bạn kích thước tính theo byte của toàn bộ mảng, không phải kích thước của một con trỏ.

Đối với một mảng được xác định là T array[size], nó sẽ có loại T *. Khi/nếu bạn tăng nó, bạn sẽ đến phần tử tiếp theo trong mảng.

&array đánh giá đến cùng một địa chỉ, nhưng với cùng một định nghĩa, nó tạo con trỏ của loại T(*)[size] - tức là nó là một con trỏ tới một mảng chứ không phải một phần tử đơn lẻ. Nếu bạn tăng con trỏ này, nó sẽ thêm kích thước của toàn bộ mảng, chứ không phải kích thước của một phần tử đơn lẻ. Ví dụ: với mã như sau:

char array[16]; 
printf("%p\t%p", (void*)&array, (void*)(&array+1)); 

Chúng ta có thể mong con trỏ thứ hai lớn hơn 16 đầu tiên (vì đó là mảng 16 char). Kể từ% p thường chuyển con trỏ trong hệ thập lục phân, nó có thể giống như thế:

0x12341000 0x12341010 
+0

Yep, thêm 1 cho mỗi khách hàng tiềm năng cho các kết quả khác nhau. Bạn có biết mỗi loại thuộc về loại nào không? – Alexandre

+1

@Alexandre: '& array' là một con trỏ đến phần tử đầu tiên của mảng, trong đó' mảng' tham chiếu đến toàn bộ mảng. Sự khác biệt cơ bản cũng có thể được quan sát bằng cách so sánh 'sizeof (array)', với 'sizeof (& array)'. Tuy nhiên, lưu ý rằng nếu bạn truyền 'mảng' làm đối số cho một hàm, chỉ có' ​​& mảng' thực tế được chuyển. Bạn không thể vượt qua một mảng theo giá trị trừ khi nó được đóng gói với một 'struct'. – Clifford

+7

@Clifford: Nếu bạn chuyển mảng tới hàm, nó phân rã thành con trỏ thành phần tử đầu tiên của nó sao cho '& array [0]' được truyền đi, chứ không phải '& mảng' sẽ là con trỏ tới mảng. Nó có thể là một lựa chọn nit nhưng tôi nghĩ điều quan trọng là phải làm rõ; Các trình biên dịch sẽ cảnh báo nếu hàm có một mẫu thử nghiệm khớp với loại con trỏ được truyền vào. –

25

Đó là bởi vì tên mảng (my_array) là khác nhau từ một con trỏ đến mảng. Nó là một bí danh cho địa chỉ của một mảng và địa chỉ của nó được định nghĩa là địa chỉ của chính mảng đó.

Con trỏ là biến C bình thường trên ngăn xếp, tuy nhiên. Vì vậy, bạn có thể lấy địa chỉ của nó và nhận được một giá trị khác với địa chỉ mà nó giữ bên trong.

Tôi đã viết về chủ đề này here - vui lòng xem qua.

+1

Chính xác. –

+0

Không nên & my_array là hoạt động không hợp lệ vì giá trị của my_array không có trong ngăn xếp, chỉ my_array [0 ... length] là? Sau đó, tất cả sẽ có ý nghĩa ... – Alexandre

+0

@ Alexandre: Tôi không chắc chắn tại sao nó được cho phép, thực sự. –

22

Trong C, khi bạn sử dụng tên của một mảng trong một biểu thức (bao gồm cả đi qua nó để một chức năng), trừ khi đó là các toán hạng của toán tử địa chỉ-of (&) hoặc sizeof điều hành, nó phân rã đến một con trỏ đến phần tử đầu tiên của nó.

Tức là, trong hầu hết các ngữ cảnh, array tương đương với &array[0] ở cả loại và giá trị.

Trong ví dụ của bạn, my_array có loại char[100] phân rã thành char* khi bạn chuyển nó tới printf.

&my_array có loại char (*)[100] (trỏ tới mảng 100 char). Vì nó là toán hạng cho &, đây là một trong các trường hợp mà my_array không phân rã ngay lập tức thành con trỏ đến phần tử đầu tiên của nó. Con trỏ tới mảng có giá trị địa chỉ giống như một con trỏ đến phần tử đầu tiên của mảng như là một đối tượng mảng chỉ là một chuỗi liên tiếp các phần tử của nó, nhưng một con trỏ tới một mảng có một kiểu khác với một con trỏ. cho một phần tử của mảng đó. Điều này là quan trọng khi bạn làm số học con trỏ trên hai loại con trỏ.

pointer_to_array có kiểu char * - khởi điểm tại phần tử đầu tiên của mảng như đó là những gì my_array phân rã đến trong biểu thức khởi tạo - và &pointer_to_array có kiểu char ** (con trỏ đến một con trỏ đến một char).

Trong số này: my_array (sau khi phân rã để char*), &my_arraypointer_to_array tất cả các điểm trực tiếp tại một trong hai mảng hoặc phần tử đầu tiên của mảng và rất có giá trị cùng một địa chỉ.

1

Trên thực tế, &myarraymyarray cả hai đều là địa chỉ cơ sở.

Nếu bạn muốn thấy sự khác biệt thay vì sử dụng

printf("my_array = %p\n", my_array); 
printf("my_array = %p\n", &my_array); 

sử dụng

printf("my_array = %s\n", my_array); 
printf("my_array = %p\n", my_array); 
2

Lý do tại sao my_array&my_array kết quả trong cùng một địa chỉ có thể được dễ hiểu khi bạn nhìn vào bộ nhớ bố cục của một mảng.

Giả sử bạn có một mảng gồm 10 ký tự (thay vì 100 ký tự trong mã của bạn).

char my_array[10]; 

Memory cho my_array trông giống như sau:

+---+---+---+---+---+---+---+---+---+---+ 
| | | | | | | | | | | 
+---+---+---+---+---+---+---+---+---+---+ 
^ 
| 
Address of my_array. 

Trong C/C++, một mảng phân rã để con trỏ tới phần tử đầu tiên trong một biểu thức như

printf("my_array = %p\n", my_array); 

Nếu bạn kiểm tra xem phần tử đầu tiên của mảng nằm ở đâu, bạn sẽ thấy địa chỉ của nó giống với địa chỉ của mảng:

my_array[0] 
| 
v 
+---+---+---+---+---+---+---+---+---+---+ 
| | | | | | | | | | | 
+---+---+---+---+---+---+---+---+---+---+ 
^ 
| 
Address of my_array[0]. 
2

Trong ngôn ngữ lập trình B, trước đây là tiền thân của C, con trỏ và số nguyên được tự do hoán đổi cho nhau. Hệ thống sẽ hoạt động như mặc dù tất cả bộ nhớ là một mảng khổng lồ. Mỗi tên biến có hoặc là toàn cầu hoặc địa chỉ tương đối stack được liên kết với nó, cho mỗi tên biến mà những thứ duy nhất mà trình biên dịch phải theo dõi đó là biến toàn cục hay cục bộ và địa chỉ của nó liên quan đến toàn cầu đầu tiên hoặc biến cục bộ.

Cho một tuyên bố toàn cầu như i; [không có cần thiết phải chỉ định một loại, vì tất cả mọi thứ là một số nguyên/con trỏ] sẽ được xử lý bởi trình biên dịch như: address_of_i = next_global++; memory[address_of_i] = 0; và một tuyên bố như i++ sẽ được xử lý như: memory[address_of_i] = memory[address_of_i]+1;.

Tuyên bố như arr[10]; sẽ được xử lý là address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;. Lưu ý rằng ngay khi khai báo được xử lý, trình biên dịch có thể ngay lập tức quên mất arr là một mảng. Một tuyên bố như arr[i]=6; sẽ được xử lý là memory[memory[address_of_a] + memory[address_of_i]] = 6;. Trình biên dịch sẽ không quan tâm liệu arr có biểu thị một mảng hay không và i một số nguyên hoặc ngược lại. Thật vậy, nó sẽ không quan tâm nếu họ là cả hai mảng hoặc cả hai số nguyên; nó sẽ hoàn toàn vui vẻ tạo mã như được mô tả, mà không quan tâm liệu hành vi kết quả có thể hữu ích hay không.

Một trong các mục tiêu của ngôn ngữ lập trình C là phần lớn tương thích với B. Trong B, tên của một mảng [được gọi là "vectơ" trong thuật ngữ B] đã xác định biến giữ con trỏ ban đầu được gán để trỏ đến phần tử đầu tiên của phân bổ kích thước đã cho, vì vậy nếu tên đó xuất hiện trong danh sách đối số cho một hàm, hàm sẽ nhận được một con trỏ tới vectơ. Mặc dù C đã thêm các kiểu mảng "thực", tên của nó được liên kết cứng nhắc với địa chỉ của phân bổ chứ không phải là biến con trỏ ban đầu trỏ tới phân bổ, có mảng phân tách thành con trỏ tạo mã mà khai báo một mảng kiểu C hoạt động giống hệt nhau đến mã B khai báo một vector và sau đó không bao giờ sửa đổi biến giữ địa chỉ của nó.

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