2012-03-04 43 views
39

Vì vậy, tôi đã tìm khi thực hiện chức năng gợi ý, bạn không cần operator & để có được địa chỉ của hàm ban đầu Chức năng:con trỏ và địa chỉ của một hàm

#include <stdio.h> 

double foo (double x){ 
    return x*x; 
} 

int main() { 

    double (*fun1)(double) = &foo; 
    double (*fun2)(double) = foo; 

    printf("%f\n",fun1(10)); 
    printf("%f\n",fun2(10)); 

    printf("fun1 = %p \t &foo = %p\n",fun1, &foo); 
    printf("fun2 = %p \t foo = %p\n",fun2, foo);  

    int a[10]; 

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

    return 0; 
} 

đầu ra:

>./a.out 
100.000000 
100.000000 
fun1 = 0x4004f4  &foo = 0x4004f4 
fun2 = 0x4004f4  foo = 0x4004f4 
    a = 0x7fff26804470 
&a = 0x7fff26804470 

Sau đó, Tôi nhận ra điều này cũng đúng với các mảng, có nghĩa là nếu bạn có int a[10] cả hai a&a trỏ đến cùng một vị trí. Tại sao với mảng và chức năng? Địa chỉ được lưu ở vị trí bộ nhớ có cùng địa chỉ với giá trị (địa chỉ) đang được lưu trong đó không?

+0

Tôi chưa bao giờ hiểu rõ lý do tại sao bạn không phải sử dụng toán tử địa chỉ để lấy địa chỉ của hàm. Tôi đã luôn luôn giả định rằng chỉ là một chút đường cú pháp. – Bill

+0

@Bill: Chính xác! Đó là những gì tôi luôn luôn sử dụng để suy nghĩ nhưng có vẻ như bạn không cần phải! – GradGuy

+0

Nếu địa chỉ được lưu trong cùng một vị trí bộ nhớ, thì không thể có dữ liệu mã/mảng chức năng thực tế! Bạn sẽ xử lý thực tế các mảng như con trỏ và các hàm được truyền dưới dạng đối số cũng như con trỏ, vì vậy bạn sẽ không cần toán tử addressof. Về cơ bản, 'giá trị' của một hàm hoặc một mảng là vô nghĩa.Chỉ có địa chỉ có ý nghĩa, vì vậy bạn nhận được rằng khi bạn truy vấn 'giá trị', và cũng có thể khi bạn truy vấn địa chỉ. –

Trả lời

58

Cho int a[10], cả hai a&a có cùng địa chỉ, có, nhưng các loại của chúng khác nhau.

a thuộc loại int[10]. Khi nó được chuyển đổi hoàn toàn thành kiểu con trỏ, con trỏ là loại int* và trỏ đến phần tử ban đầu của mảng. &a là loại int (*)[10] (có nghĩa là, một con trỏ đến một mảng mười số nguyên). Vì không có đệm trong một mảng nên cả hai con trỏ đều có con trỏ với cùng giá trị , nhưng con trỏ có các loại khác nhau .

Chức năng tương tự như mảng, nhưng không hoàn toàn giống nhau. Chức năng của bạn foo thuộc loại double(double). Bất cứ khi nào foo được sử dụng trong một biểu thức và không phải là toán hạng của toán tử & đơn nhất, nó được chuyển đổi hoàn toàn thành một con trỏ đến chính nó, là loại double(*)(double).

Vì vậy, đối với tất cả các mục đích thực tế, tên của hàm và con trỏ đến cùng chức năng có thể hoán đổi cho nhau. Có một số sự tinh tế, tất cả những gì tôi thảo luận trong một câu trả lời cho "Why do all these crazy function pointer definitions all work? What is really going on?" (Câu hỏi đó được hỏi về C++, nhưng các quy tắc cho các hàm không phải là tháng trong C++ giống như đối với các hàm trong C.)

8

Không, không có thêm lưu trữ dành riêng để trỏ đến hàm/mảng.

Với hầu hết các biến variable_name có ý nghĩa khác với địa chỉ của biến đó, vì vậy bạn cần sử dụng &variable để nhận địa chỉ.

Với chức năng hoặc mảng, function_name (tự nó, không theo sau dấu ngoặc đơn) không có bất kỳ ý nghĩa nào khác, do đó không có vấn đề gì khi diễn giải nó khi lấy địa chỉ của hàm.

Tương tự ngược lại: một con trỏ bình thường cần được dereferenced một cách rõ ràng, nhưng một con trỏ tới một hàm không (một lần nữa, vì không có sự giải thích hợp lý khác), do được đưa ra một con trỏ tới một hàm như:

int (*func)(param_list); 

sau đây là tương đương với nhau - cả hai cuộc gọi bất cứ chức năng func điểm tại địa chỉ:

(*func)(params); 

func(params); 
+2

Có vẻ như có một chút khác biệt mặc dù, đối với int [10], (* a) [5] không phải là cuộc gọi hợp lệ – GradGuy

1

về cơ bản, vì tên hàm được "nổi tiếng" là một chức năng, các & không phải là nghiêm nec tiểu luận. Hành vi này giống nhau đối với mảng. Nhớ lại rằng một hàm không phải là một biến, do đó, nó hoạt động hơi khác một chút so với đôi khi bạn có thể mong đợi.Nếu bạn có phiên bản thứ 2 của K & R, bạn có thể kiểm tra phần 5.11 về con trỏ đến chức năng, hay hướng dẫn tài liệu tham khảo ở cuối,

Mục hệ A7.1 con trỏ: Nếu kiểu của một biểu thức hoặc biểu hiện con là "mảng T" cho một số loại T, sau đó giá trị của biểu thức là một con trỏ đến đối tượng đầu tiên trong mảng và loại của biểu thức được thay đổi thành "con trỏ đến T." Chuyển đổi này không không diễn ra biểu thức là toán hạng của toán tử & , ... Tương tự, biểu thức kiểu "hàm trả về T", trừ khi được sử dụng làm toán hạng của toán tử &, được chuyển đổi thành "con trỏ đến hàm trả về T."

Mục A7.4.2 Nhà điều hành địa chỉ: Nhà điều hành đơn lẻ & lấy địa chỉ toán hạng của nó .... Kết quả là một con trỏ đến đối tượng hoặc hàm được tham chiếu bởi lvalue. Nếu loại toán hạng là T, loại của kết quả là "con trỏ đến T."

Theo như tôi biết, điều này cũng tương tự đối với C99.

0

printf ("fun1 =% p \ t & foo =% p \ n", fun1, foo);

Ở đây bạn được gọi foo bằng cách thông qua Function Pointer với pass by value

printf ("fun2 =% p \ t foo =% p \ n", fun2, & foo)

Tại đây, bạn đang gọi tới số &foo bằng cách chuyển Pointer với pass by reference

in bo trường hợp thứ bạn đang gọi printf chỉ với con trỏ hàm.

Hãy nhớ foo chính nó là function pointer value và `không phải là biến.

Tương tự xảy ra với mảng. int arr[10] dịch thành khối liên tục 10 số nguyên và địa chỉ của phần tử đầu tiên được lưu trữ thành arr. vì vậy arr cũng là một con trỏ.

0

fun&fun giống hệt nhau (ngoại trừ kích thước đó (f) là bất hợp pháp). a&a giống với số học con trỏ: a + 10 == &a + 1, vì 10*sizeof(*a) == sizeof(a) (trong đó sizeof(*a) == sizeof(int)).

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