Ý nghĩa của int (*) (int *) = 5 (hoặc bất kỳ giá trị số nguyên nào)


88

Tôi không thể hình này ra:

int main() {
    int (*) (int *) = 5;
    return 0;
}

Bài tập trên biên dịch với g ++ c ++ 11. Tôi biết rằng đó int (*) (int *)là một con trỏ đến một hàm chấp nhận một (int *)đối số là và trả về một int, nhưng tôi không hiểu làm thế nào bạn có thể đánh đồng nó với 5. Lúc đầu, tôi nghĩ đó là một hàm liên tục trả về 5 (từ bài học gần đây của tôi trong F #, có thể là, haha), sau đó tôi nghĩ ngắn gọn rằng con trỏ hàm trỏ đến vị trí bộ nhớ 5, nhưng điều đó không hoạt động, rõ ràng và các giá trị hex cũng vậy.

Nghĩ rằng có thể là do hàm trả về một int và việc gán một int là ok (bằng cách nào đó), tôi cũng đã thử điều này:

int * (*) (int *) = my_ptr

ở đâu my_ptrlà kiểu int *, cùng kiểu với con trỏ hàm thứ hai này, như trong trường hợp đầu tiên với kiểu int. Điều này không biên dịch. Việc gán 5 hoặc bất kỳ giá trị int nào thay vì my_ptrcũng không biên dịch cho con trỏ hàm này.

Vậy bài tập nghĩa là gì?

Cập nhật 1

Chúng tôi xác nhận rằng đó là một lỗi, như được hiển thị trong câu trả lời tốt nhất. Tuy nhiên, vẫn chưa biết điều gì thực sự xảy ra với giá trị mà bạn gán cho con trỏ hàm hoặc điều gì xảy ra với việc gán. Bất kỳ giải thích (tốt) nào về điều đó sẽ được đánh giá rất cao! Vui lòng tham khảo các chỉnh sửa bên dưới để rõ hơn về vấn đề.

Chỉnh sửa 1

Tôi đang sử dụng gcc phiên bản 4.8.2 (trong Ubuntu 4.8.2)

Chỉnh sửa 2

Trên thực tế, đánh đồng nó với bất kỳ thứ gì hoạt động trên trình biên dịch của tôi. Ngay cả khi đánh đồng nó với một biến std :: string hoặc một tên hàm trả về một giá trị kép cũng hoạt động.

Chỉnh sửa 2.1

Điều thú vị là, việc biến nó thành một con trỏ hàm tới bất kỳ hàm nào trả về kiểu dữ liệu không phải là con trỏ, sẽ cho phép nó biên dịch, chẳng hạn như

std::string (*) () = 5.6;

Nhưng ngay sau khi con trỏ hàm đến một hàm trả về một số con trỏ, nó không biên dịch, chẳng hạn như với

some_data_type ** (*) () = any_value;

3
Hmm ... nó có vẻ không ổn, và clang không chấp nhận nó. Có thể là một phần mở rộng gcc (hoặc lỗi).
Wintermute

4
g ++ biên dịch, nhưng gcc không hoạt động:error: expected identifier or '(' before ')' token
tivn

3
@ 0x499602D Lưu ý rằng mã không cung cấp tên cho con trỏ. Với int *x = 5bạn đã đặt tên cho nó x. Với int * (*x) (int *) = 5nó sẽ không biên dịch. (mặc dù điều đó sẽ biên dịch dưới dạng mã C).
nos

5
Giảm bớt thử nghiệm: int(*) = 5;int(*);
Johannes Schaub - litb

Câu trả lời:


60

Đó là một lỗi trong g ++.

 int (*) (int *) 

là một tên kiểu.

Trong C ++, bạn không thể có một khai báo với tên kiểu mà không có định danh.

Vì vậy, điều này biên dịch với g ++.

 int (*) (int *) = 5;

và điều này cũng biên dịch:

 int (*) (int *);

nhưng cả hai đều là khai báo không hợp lệ.

CHỈNH SỬA :

TC đề cập đến lỗi bugzilla 60680 trong phần bình luận với một trường hợp thử nghiệm tương tự nhưng nó vẫn chưa được chấp thuận . Lỗi được xác nhận trong bugzilla.

EDIT2 :

Khi hai khai báo trên ở phạm vi tệp, g ++ đưa ra chẩn đoán chính xác (nó không đưa ra chẩn đoán ở phạm vi khối).

CHỈNH SỬA3 :

Tôi đã kiểm tra và tôi có thể tái tạo sự cố trên bản phát hành mới nhất của g ++ phiên bản 4 (4.9.2), phiên bản phát hành trước mới nhất 5 (5.0.1 20150412) và phiên bản thử nghiệm mới nhất 6 (6.0.0 20150412).


5
MSVC từ chối mã posted chỉnh sửa vớierror C2059: syntax error : ')'
Thời tiết Vane

Nếu nó là một tên kiểu, tại sao lại không có 'int (*) (int *) int_func;' công việc?
Konrad Kapp

1
Đối với bugzilla GCC, "MỚI" là một lỗi đã được xác nhận. (Các lỗi chưa được xác nhận là "KHÔNG ĐƯỢC XÁC NHẬN").
TC

4
@KonradKapp: nó hoạt động tốt nếu bạn nói int (*int_func)(int *); cái nào khai báo một con trỏ hàm có tên int_func.
Edward

3
@KonradKapp C ++ sử dụng ký hiệu infix để đặt số nhận dạng; cùng lý do đó là nó int x[5];và khôngint[5] x;
MM

28

Nó không phải là C ++ hợp lệ. Hãy nhớ rằng bởi vì trình biên dịch cụ thể của bạn tình cờ biên dịch nó không làm cho nó hợp lệ. Các trình biên dịch, giống như tất cả các phần mềm phức tạp, đôi khi có lỗi và đây dường như là một.

Ngược lại clang++than phiền:

funnycast.cpp:3:11: error: expected expression
    int (*) (int *) = 5;
          ^
funnycast.cpp:3:18: error: expected '(' for function-style cast or type construction
    int (*) (int *) = 5;
             ~~~ ^
funnycast.cpp:3:19: error: expected expression
    int (*) (int *) = 5;
                  ^
3 errors generated.

Đây là hành vi được mong đợi vì dòng vi phạm không phải là C ++ hợp lệ. Nó có mục đích là một phép gán (vì =) nhưng không chứa mã định danh.


9

Như các câu trả lời khác đã chỉ ra, đó là một lỗi

int (*) (int *) = 5;

biên dịch. Một giá trị gần đúng hợp lý của tuyên bố này sẽ có ý nghĩa là:

int (*proc)(int*) = (int (*)(int*))(5);

Bây giờ proclà một con trỏ đến hàm mong muốn địa chỉ 5là địa chỉ cơ sở của một hàm nhận int*và trả về một int.

Trên một số bộ vi điều khiển / bộ vi xử lý 5có thể là địa chỉ mã hợp lệ và có thể xác định được chức năng như vậy ở đó.

Trên hầu hết các máy tính đa năng, trang đầu tiên của bộ nhớ (địa chỉ 0-1023cho các trang 4K) cố tình không hợp lệ (không được ánh xạ) để bắt các nulltruy cập con trỏ.

Do đó, trong khi hành vi phụ thuộc vào nền tảng, người ta có thể mong đợi một cách hợp lý lỗi trang xảy ra khi *procđược gọi (ví dụ (*proc)(&v):). Trước thời điểm *procđược gọi, không có gì bất thường xảy ra.

Trừ khi bạn đang viết một trình liên kết động, gần như chắc chắn bạn không nên tính toán số lượng các địa chỉ và gán chúng cho các biến con trỏ đến hàm.


2
/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/cc1plus.exe -da so.cpp

Dòng lệnh này tạo ra rất nhiều tệp trung gian. Người đầu tiên trong số họ so.cpp.170r.expand, nói:

...
int main() ()
{
  int D.2229;
  int _1;

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  _1 = 0;
;;    succ:       3

;;   basic block 3, loop depth 0
;;    pred:       2
<L0>:
  return _1;
;;    succ:       EXIT

}
...

Điều này vẫn chưa trả lời chính xác điều gì sẽ xảy ra, nhưng nó phải là một bước đi đúng hướng.


Hấp dẫn. Mục đích của các tệp trung gian này là gì?
Konrad Kapp

@KonradKapp Để tạo mã máy từ mã người là một quá trình khá phức tạp (đặc biệt nếu bạn muốn trình biên dịch của mình tối ưu hóa đầu ra của nó). Vì quá trình biên dịch rất phức tạp, nó không được thực hiện trong một bước, hầu hết các trình biên dịch đều có một số dạng Biểu diễn Trung gian (IR).
11684

2
Một lý do khác để có IR là nếu bạn có IR được xác định rõ ràng, bạn có thể tách riêng front-end và back-end của trình biên dịch. (Ví dụ: front-end biên dịch C thành IR của bạn, back-end biên dịch IR thành mã máy Intel. Bây giờ nếu bạn muốn thêm hỗ trợ ARM, bạn chỉ cần back-end thứ hai. Và nếu bạn muốn biên dịch Go, bạn chỉ cần thứ hai front-end, và trên hết các trình biên dịch Go ngay lập tức hỗ trợ cả Intel và ARM vì bạn có thể tái sử dụng cả hai back-kết thúc.
11.684

@ 11684 OK, có lý. Rất thú vị. Tôi không thể xác định những gì ngôn ngữ Roland đã trong câu trả lời này mặc dù ... nó trông giống như một số loại lắp ráp trộn với C.
Konrad Kapp

IR không cần phải in được; Tôi không có ý tưởng những gì sử dụng gcc, đây chỉ đơn thuần có thể là một đại diện @KonradKapp in
11.684
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.