2013-06-19 13 views
5

Tôi đã viết một printsel myselef sử dụng va_list/va_arg/va_start/va_end/va_arg.sử dụng gcc biên dịch một dự án cho thấy "tham chiếu chưa xác định thành` hủy bỏ ""

typedef char *va_list; 
#define _AUPBND    (sizeof (acpi_native_int) - 1) 
#define _ADNBND    (sizeof (acpi_native_int) - 1) 
#define _bnd(X, bnd)   (((sizeof (X)) + (bnd)) & (~(bnd))) 
#define va_arg(ap, T)   (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND)))) 
#define va_end(ap)    (void) 0 
#define va_start(ap, A)   (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND)))) 

Lúc đầu, tôi sao chép các macro từ kernel linux và printf có thể in 32-bit integer đúng nhưng không thể in 64-bit số nguyên và in đôi/float có thể thất bại hoặc collapse.Then tôi kiểm tra mã và Tôi đoán va_ * có thể có lỗi, vì vậy tôi sử dụng __builtin_va_ * thay vì của kernel và_ *.

typedef __builtin_va_list va_list; 
#define va_start(v,l) __builtin_va_start(v,l) 
#define va_end(v)  __builtin_va_end(v) 
#define va_arg(v,l)  __builtin_va_arg(v,l) 

Nhưng dấu nhắc gcc "undefined reference to` abort '", vì vậy tôi viết abort trống() và myprintf hoạt động đúng. Câu hỏi của tôi là:

  1. Tại sao Linux kernel của va_list/va_arg/va_start/va_end/va_arg có thể không được sử dụng cho printf 64-bit số nguyên và đôi/nổi?
  2. Khi tôi sử dụng __builtin_va_start/__builtin_va_arg/__builtin_va_end/__builtin_va_list, tại sao gcc nhắc "không xác định tham chiếu đến abort'"? But I can not find the definition of __builtin_va_ *`, Ở đâu rồi định nghĩa của họ?
+2

Làm thế nào về một ?Ngoài ra '_bnd' (_bnd được cho là ot là _va_align) được định nghĩa khác nhau trong đó:' #define __va_align (ty) ((sizeof (ty) + sizeof (int) - 1) & ~ (sizeof (int) - 1)) ' –

+0

Đồng ý với @Armin; tại sao bạn không chỉ sử dụng tiêu đề chuẩn cho công cụ va_arg? –

+0

@ Charles Charlesworth thực sự, dự án của chúng tôi đang làm việc mà không cần hỗ trợ os và chúng tôi nead kiểm soát nơi để in, do đó stdio.h không phù hợp với yêu cầu của chúng tôi. – Ezio

Trả lời

0

Không cắt và dán mọi thứ từ các tiêu đề Linux. Thay vào đó, hãy đặt này tại hàng đầu của tập tin nguồn của bạn:

#include <stdarg.h> 

này sẽ cung cấp cho bạn mọi thứ bạn cần để sử dụng va_listva_arg Tuy nhiên, nó sẽ không kéo trong printf hoặc bất kỳ tiêu chuẩn I/O stuf. f (sống ở <stdio.h>).

1

gcc's __builtin_va_arg() rõ ràng sẽ gọi abort() (ít nhất là trên một số nền tảng hoặc tình huống) nếu được gọi với đối số loại không thể được chuyển trong phần ... của danh sách đối số cho cuộc gọi hàm.

Ví dụ, do chương trình khuyến mãi một char hoặc float thông qua như là một cuộc tranh cãi như vậy sẽ được thăng int hay double. Truy cập các đối số đó là va_arg(ap,char) hoặc va_arg(ap,float) là hành vi không xác định và gcc có thể gọi abort() trong trường hợp đó - hoặc có thể làm điều gì đó khác (trình biên dịch MinGW của tôi sẽ thực thi lệnh không hợp lệ để gây ra sự cố).

Bạn có thể thấy một cái gì đó như thế này khi biên soạn:

In file included from D:\temp\test.c:2:0: 
D:\temp\test.c: In function 'foo': 
D:\temp\test.c:12:16: warning: 'char' is promoted to 'int' when passed through '...' [enabled by default] 
    c = va_arg(ap,char); 
       ^
D:\temp\test.c:12:16: note: (so you should pass 'int' not 'char' to 'va_arg') 
D:\temp\test.c:12:16: note: if this code is reached, the program will abort 

Các 'định nghĩa' của __builtin_va_* được biên dịch vào trình biên dịch (đó là lý do tại sao 'BUILTIN' là một phần của tên).

Theo các macro Linux để truy cập varargs sẽ thấy rằng các macro này không được sử dụng khi xây dựng hạt nhân Linux. Tôi không chắc chắn chính xác khi các macro đó có hiệu lực, nhưng chúng sẽ không hoạt động với các bản dựng x64/x86-64/amd64 vì ABI trên nền tảng đó không hoàn toàn dựa trên nền tảng xếp chồng. Xem phần 3.5.6 của "Giao diện nhị phân ứng dụng hệ thống V - Phụ gia xử lý kiến ​​trúc AMD64" để biết chi tiết.