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 type
trong bar
chứ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_list
khô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 bar
hàm. Với GCC, _Static_assert
thất bại. Điều tương tự cũng có thể được kiểm tra trong C ++ với declytpe
và std::is_same
.
Tôi muốn lấy địa chỉ của va_list vl
đối số bar
và 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_list
và 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 bar
bằ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);
}
va_list
, bạn đang truyền thực tế va_list
.
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 vl
hàm đó. Tôi sẽ chỉnh sửa câu hỏi để chỉ định nó.
va_list
va_copy
tiế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.
va_list
.