đối số va_list thực sự không phải là va_list


8

Khi cố gắng biên dịch mã này

#include <stdarg.h>

void bar_ptr(int n, va_list *pvl) {
    // do va_arg stuff here
}

void bar(int n, va_list vl) {
    va_list *pvl = &vl; // error here
    bar_ptr(n, pvl);
}

void foo(int n, ...) {
    va_list vl;
    va_list *pvl = &vl; // fine here
    va_start(vl, n);
    bar(n, vl);
    va_end(vl);
}

int main() {
    foo(3, 1, 2, 3);
    return 0;
}

trình biên dịch GCC in một cảnh báo về initialization from incompatible pointer typetrong barchức năng. Các tuyên bố giống hệt là tốt trong foo.

Có vẻ như loại hình tượng trưng va_listkhông phải là a va_list. Điều này có thể được kiểm tra dễ dàng với một xác nhận tĩnh như

_Static_assert(sizeof(vl) == sizeof(va_list), "invalid type");

trong barhàm. Với GCC, _Static_assertthất bại. Điều tương tự cũng có thể được kiểm tra trong C ++ với declytpestd::is_same.

Tôi muốn lấy địa chỉ của va_list vlđối số barvà chuyển nó làm đối số bar_ptr, để thực hiện suy nghĩ giống như mô tả trong chủ đề này . Mặt khác, tốt hơn là gọi bar_ptr(n, pvl)trực tiếp từ main, thay thế bar(n, vl).

Theo chú thích 253 của bản thảo cuối cùng của C11 ,

Được phép tạo một con trỏ tới a va_listvà chuyển con trỏ đó sang một hàm khác

Tại sao điều này không thể được thực hiện nếu va_listđược định nghĩa là đối số của hàm và không phải trong thân hàm?

Cách giải quyết:

Ngay cả khi điều này không trả lời câu hỏi, một cách giải quyết có thể là thay đổi nội dung barbằng cách sử dụng một bản sao cục bộ của đối số được tạo bằng va_copy:

void bar(int n, va_list vl) {
    va_list vl_copy;
    va_copy(vl_copy, vl);
    va_list *pvl = &vl_copy; // now fine here
    bar_ptr(n, pvl);
    va_end(va_copy);
}

2
Về trích dẫn tiêu chuẩn bạn có: Bạn không thực sự chuyển một con trỏ đến a va_list.
Một số lập trình viên anh chàng

4
Bạn được phép tạo một con trỏ tới va_list và chuyển con trỏ đó sang một hàm khác Bạn không chuyển con trỏ đến a va_list, bạn đang truyền thực tế va_list.
Andrew Henle

Tôi biết tôi đang vượt qua a va_list. Giả sử tôi có một hàm thứ ba void bar_ptr(va_list *pvl);, tôi muốn chuyển một con trỏ đến va_list vlhàm đó. Tôi sẽ chỉnh sửa câu hỏi để chỉ định nó.
Giovanni Cerretani

1
Điều này và câu hỏi này có thể hữu ích. Và cũng rất có khả năng trình biên dịch có cách xử lý đặc biệtva_list
Một số lập trình viên đã lập công vào

1
Cách va_copytiếp cận bạn có là giải pháp kinh điển cho vấn đề này. Tôi có một vài câu hỏi trong quá khứ về cơ bản đã kết luận điều này.
R .. GitHub DỪNG GIÚP ICE

Câu trả lời:


4

va_listđược tiêu chuẩn cho phép là một mảng, và thường là như vậy. Điều đó có nghĩa là va_listtrong một đối số hàm được điều chỉnh thành một con trỏ tới bất kỳ va_listphần tử đầu tiên bên trong nào.

Quy tắc kỳ lạ ( 7.16p3 ) về cách thức va_listđược thông qua về cơ bản phù hợp với khả năng va_listcó thể thuộc loại mảng hoặc loại thông thường.

Cá nhân tôi quấn va_listtrong một structvì vậy tôi không phải đối phó với điều này.

Sau đó, khi bạn chuyển con trỏ đến như vậy struct va_list_wrapper, về cơ bản, như thể bạn đã chuyển con trỏ tới va_list, và sau đó chú thích 253 áp dụng cho phép bạn có cả callee và người gọi thao tác giống nhau va_listthông qua một con trỏ như vậy.

(Điều tương tự áp dụng cho jmp_bufsigjmp_buftừ setjmp.h. Nói chung, loại mảng này để điều chỉnh con trỏ là một trong những lý do tại sao typedefs được gõ mảng tốt nhất nên tránh. Nó chỉ tạo ra sự nhầm lẫn, IMO.)


2
Nếu va_listcó thể là một mảng, thì tiêu chuẩn nói sai là đối tượng apcó thể được truyền như một đối số cho một chức năng khác, khác biệt (C 2018 7.16 3, aplà một đối tượng thuộc loại va_list).
Eric Postpischil

2
@EricPostpischil: Tiêu chuẩn nói rất nhiều sai lệch thứ và coi họ không-một-nhưng / WONTFIX miễn là mục đích rõ ràng để "ai đó" ... :-P
R .. GitHub DỪNG GIÚP ICE

2

Một giải pháp khác (chỉ C11 +):

_Generic(vl, va_list: &vl, default: (va_list *)vl)

Giải thích: nếu vlcó kiểu va_list, thì đó va_listkhông phải là kiểu mảng và chỉ cần lấy địa chỉ là ổn để va_list *chỉ vào nó. Mặt khác, nó phải có kiểu mảng, và sau đó bạn được phép đưa một con trỏ đến phần tử đầu tiên của mảng (bất kể đó là loại nào) cho một con trỏ tới mảng.

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.