2013-04-24 34 views
9

Tôi liên tục gặp vấn đề thiết lập bộ xử lý tín hiệu trong mã GTK +, không cần một số thông số và bị cám dỗ để sử dụng cùng chức năng với trình xử lý cho một số tín hiệu. đối số (những cái tôi quan tâm) giống nhau. Có phải là an toàn (theo nghĩa là nó không phải là hành vi không xác định, thay vì ý nghĩa thực dụng hơn về "nó hoạt động trên PC của tôi?") Để chuyển con trỏ tới các hàm với API GObject khi các hàm đó có ít hơn đối số hơn là chúng thực sự sẽ nhận được từ quá trình phát tín hiệu?Có an toàn khi gọi hàm C với nhiều đối số hơn dự kiến ​​không?

Hoặc, để ly dị điều này khỏi GTK +, mã này có được không?

/* Note: No void *userdata argument! */ 
void show(int x) { 
    printf("x = %d\n", x); 
} 

void do_stuff(void (*fn)(int, void *), void *userdata) { 
    static int total = 0; 
    (*fn)(total, userdata); 
    total++; 
} 

void doitnow(void) { 
    do_stuff(&show, NULL); 
} 

Để biết thêm tín dụng, hãy thảo luận về ý nghĩa của các loại giá trị trả lại khác nhau giữa chữ ký chức năng và trang web cuộc gọi.

Chỉnh sửa: An almost-identical question đầu dò "loại chức năng tương thích" chặt chẽ hơn và rút ra an answer directly addressing my concrete problem - điều khiển chuỗi xử lý tín hiệu GObject. TL; DR: Có, đó là hành vi không xác định, nhưng thực tế là thành ngữ (mặc dù không bắt buộc) trong một số bộ công cụ.

+0

Tôi gần như chắc chắn rằng việc chuyển đổi con trỏ hàm theo cách đó là UB. – Flexo

+3

@Flexo Chuyển đổi con trỏ hàm là không sao. Sử dụng nó cho bất cứ điều gì khác hơn là chuyển đổi trở lại, mặt khác, ... –

+0

BTW, không cần '(* fn)' trong do_stuff. Chỉ cần gọi 'fn (total, userdata)'. Vì 'fn' là một con trỏ hàm, toán tử gọi hàm' (total, userdata) 'thực hiện chính xác những gì bạn mong đợi. – Jens

Trả lời

8

Đó là một cách rõ ràng hành vi không xác định, mỗi 6.5.2.2, đoạn 9:

Nếu chức năng được xác định với một kiểu không tương thích với các loại (của biểu thức) được trỏ đến bởi sự biểu hiện mà biểu thị hàm được gọi, hành vi không xác định.

Loại rằng show được định nghĩa với,

void show(int x) 

không tương thích với các loại biểu thức được trỏ đến bởi con trỏ qua đó nó được gọi là,

void (*fn)(int, void *) 

Ngoài ra một cách rõ ràng trong 6.3.2.3, đoạn 8:

Một điểm er đến một chức năng của một loại có thể được chuyển đổi thành một con trỏ đến một chức năng của loại khác và ngược lại; kết quả sẽ so sánh với con trỏ ban đầu. Nếu một con trỏ được chuyển đổi được sử dụng để gọi một hàm có kiểu không tương thích với kiểu được tham chiếu, hành vi này không được xác định.

Đối với chức năng, "loại tương thích" được đặc trưng trong 6.7.6.3 (15) [6.7.5.3 (15) trong C99]:

Đối với hai loại chức năng để tương thích, cả hai đều có trách nhiệm chỉ định các kiểu trả về tương thích. Hơn nữa, danh sách kiểu tham số, nếu cả hai đều có mặt, phải đồng ý về số lượng tham số và sử dụng dấu chấm lửng; các thông số tương ứng phải có các loại tương thích. Nếu một kiểu có danh sách kiểu tham số và kiểu khác được chỉ định bởi một khai báo hàm không phải là một phần của định nghĩa hàm và có chứa một danh sách định danh rỗng, danh sách tham số sẽ không có dấu chấm lửng và kiểu của mỗi tham số tương thích với loại kết quả từ việc áp dụng các quảng cáo đối số mặc định.Nếu một loại có danh sách loại tham số và loại khác là được chỉ định bởi định nghĩa hàm chứa danh sách số nhận dạng (có thể trống), cả hai phải đồng ý về số tham số và loại của mỗi tham số mẫu thử phải tương thích với loại kết quả từ việc áp dụng các quảng cáo đối số mặc định cho loại của số nhận dạng tương ứng. (Trong việc xác định loại tương thích và loại hỗn hợp, mỗi thông số được khai báo với hàm hoặc loại mảng được lấy như có kiểu được điều chỉnh và mỗi tham số được khai báo với loại đủ điều kiện được lấy làm phiên bản không đủ tiêu chuẩn của loại được khai báo.)

Phần thích hợp nhất ở đây là số đối số phải giống nhau.

+0

howd bạn tìm kiếm nhanh như vậy? Bạn có biết các tiêu chuẩn trên đầu ngón tay của bạn? :-) –

+0

Không tốt, nhưng tôi biết nơi để tìm trong trường hợp này. –

+0

Câu trả lời của bạn đã giúp tôi tìm ra một nửa câu trả lời khác - ý nghĩa của "các loại tương thích" liên quan đến các chức năng. Bạn có muốn thêm nó ở đây, trước khi tôi chấp nhận điều này? Dường như là 6.7.5.3 §15. –

-1

đọc http://www.unixwiz.net/techtips/win32-callconv-asm.htmlhttp://www.csee.umbc.edu/~chang/cs313.s02/stack.shtml Dường như nếu bạn đang chuyển nhiều hơn, nó sẽ không gây ra sự cố vì trước tiên các thông số được đẩy. Vì vậy, bất cứ điều gì là không cần thiết, vẫn vào ngăn xếp người gọi. Nhưng cách ngược lại chắc chắn sẽ gây ra vấn đề.

+0

Điều đó có thể xảy ra trên một số triển khai nhưng không thay đổi hành vi không xác định theo tiêu chuẩn. – Flexo

+0

Nó có thể là OK nếu người gọi dọn dẹp ngăn xếp, nhưng những gì về callee - nó sẽ không lấy thông số không chính xác do khung stack tham số quá khổ? –

+0

Nếu các đơn đặt hàng đúng trong các thông số chính xác, phần còn lại của các tham số thực sự nằm bên dưới các tham số đó trong khung ngăn xếp, bên dưới địa chỉ trả về, bên dưới con trỏ cơ sở. Vì vậy, khi callee đang đọc các thông số, nó đọc đến những gì nó được cho là để đọc, phần còn lại chỉ nằm ở đó bị ảnh hưởng. Nhưng trong số những người mà callee có nghĩa vụ phải đọc, nếu có điều gì sai, thì sẽ có vấn đề. – abasu

2

đây là hành vi không xác định trong tiêu chuẩn C, nhưng tiêu chuẩn C cũng ủy nhiệm hỗ trợ cho các hàm với đối số variadic và cách trình biên dịch duy nhất triển khai sau là cho phép trước đó.

dạng thành ngữ đặc biệt này được hỗ trợ bởi mọi trình biên dịch và nền tảng được hỗ trợ bởi GLib, và đã được nhiều năm - thậm chí trước khi GLib được tạo ra - vì vậy rất khó có thể bị phá vỡ.

+0

Điểm truy cập: 20 năm trước, tôi đã sử dụng MS QuickC, người ta có thể yêu cầu sử dụng quy ước gọi "pascal" (thứ tự đảo ngược đối số trên ngăn xếp - đẩy đối số đầu tiên trước thay vì cuối). Nhận được số lượng các đối số sai trong trường hợp như vậy sẽ phá vỡ. Tất nhiên một bộ công cụ là miễn phí để xác định đi những nền tảng mà đây là một vấn đề, và chỉ hỗ trợ những nơi mà nó không phải là. –

+0

vì vậy bạn đã nói với một trình biên dịch * rất cũ trên một nền tảng không được hỗ trợ theo bất kỳ cách nào bởi GLib, nhiều năm trước khi GLib thậm chí còn nghĩ đến, để làm điều gì đó mô phỏng ngôn ngữ và mọi thứ khác. đây sẽ là một vấn đề ... làm sao? :-) – ebassi

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