为啥我的 C 代码片段不起作用?简化版可以。为 unsigned long long 传递不带 VA_ARGS 的 args
Posted
技术标签:
【中文标题】为啥我的 C 代码片段不起作用?简化版可以。为 unsigned long long 传递不带 VA_ARGS 的 args【英文标题】:Why doesn't my C code snippet work? Simplistic version does. Passing args without VA_ARGS for unsigned long long为什么我的 C 代码片段不起作用?简化版可以。为 unsigned long long 传递不带 VA_ARGS 的 args 【发布时间】:2010-12-28 22:38:44 【问题描述】:基本上,我正在为专用系统编写 printf 函数,因此我想在不使用 VA_ARGS 宏的情况下传递可选数量的参数。我敲了一个简单的例子,这段代码可以工作:
#include <stdio.h>
void func(int i, ...);
int main(int argc, char *argv);
int main(int argc, char *argv)
unsigned long long f = 6799000015ULL;
unsigned long long *g;
//g points to f
g = &f;
printf("natural: %llu in hex: %llX address: %x\n", *g, *g, g);
//put pointer onto stack
func(6, g, g);
return 0;
void func(int i, ...)
unsigned long long *f;
//pop value off
f = *(&i + 1);
printf("address: %x natural: %llu in hex: %llX\n", f, *f, *f);
但是,我试图将其转移到 的较大示例不起作用。
(在主函数中):
unsigned long long f = 6799000015ULL;
unsigned long long *g;
g = &f;
kprintf("ull test: 1=%U 2=%X 3=%x 4= 5=\n", g, g, g);
(我遇到问题的狡猾的 printf 函数。也许值得指出 此代码适用于整数、字符字符串或任何其他通过的 % 标志 值 而不是指针。 did 工作和未签名的唯一区别 long longs 是一个更大的值,所以我通过值来确保我不会增加 &format+ args 部分错误。这有意义吗?)
void kprintf(char *format, ...)
char buffer[KPRINTF_BUFFER_SIZE];
int bpos = 0; /* position to write to in buffer */
int fpos = 0; /* position of char to print in format string */
char ch; /* current character being processed*/
/*
* We have a variable number of paramters so we
* have to increment from the position of the format
* argument.
*/
int arg_offset = 1;
/*
* Think this through Phill. &format = address of format on stack.
* &(format + 1) = address of argument after format on stack.
* void *p = &(format + arg_offset);
* kprintf("xxx %i %s", 32, "hello");
* memory would look like = [ 3, 32, 5, "xxx", 32, "hello" ]
* get to 32 via p = &(format + 1); (int)p (because the int is copied, not a pointer)
* get to hello via p = &(format + 2); (char*)p;
*/
void *arg;
unsigned long long *llu;
arg = (void*) (&format + arg_offset);
llu = (unsigned long long*) *(&format + arg_offset);
while (1)
ch = format[fpos++];
if (ch == '\0')
break;
if (ch != '%')
buffer[bpos++] = ch;
else
ch = format[fpos++];
if (ch == 's')
bpos += strcpy(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, (char*)arg);
else if (ch == '%')
buffer[bpos++] = '%';
else if (ch == 'i')
bpos += int_to_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'x')
bpos += int_to_hex_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'o')
bpos += int_to_oct_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'X')
//arg is expected to be a pointer we need to further dereference.
bpos += unsigned_long_long_to_hex(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *llu);
else if (ch == 'U')
bpos += unsigned_long_long_to_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *llu);
else
puts("invalid char ");
putch(ch);
puts(" passed to kprintf\n");
arg_offset++;
arg = (void *)(&format + arg_offset);
llu = (unsigned long long*) *(&format + arg_offset);
buffer[bpos] = '\0';
puts(buffer);
(以及它继续调用的 unsigned long long 函数):
int unsigned_long_long_to_hex(char *buffer, int max_size, unsigned long long number)
return ull_number_to_str(buffer, max_size, number, BASE_HEX);
int unsigned_long_long_to_str(char *buffer, int max_size, unsigned long long number)
return ull_number_to_str(buffer, max_size, number, BASE_DECIMAL);
int ull_number_to_str(char *buffer, int max_size, unsigned long long number, int base)
int bufpos = 0;
unsigned int lo_byte = (unsigned int) number;
unsigned int hi_byte = (unsigned int) (number >> 32);
bufpos = number_to_str(buffer, max_size, lo_byte, base);
bufpos += number_to_str(buffer + bufpos, max_size, hi_byte, base);
return bufpos;
#define NUMERIC_BUFF_SIZE (11 * (ADDRESS_SIZE / 32))
int number_to_str(char *buffer, int max_size, int number, int base)
char *char_map = "0123456789ABCDEF";
int remain = 0;
char buff_stack[NUMERIC_BUFF_SIZE];
int stk_pnt = 0;
int bpos = 0;
/* with this method of parsing, the digits come out backwards */
do
if (stk_pnt > NUMERIC_BUFF_SIZE)
puts("Number has too many digits to be printed. Increasse NUMBERIC_BUFF_SIZE\n");
return 0;
remain = number % base;
number = number / base;
buff_stack[stk_pnt++] = char_map[remain];
while (number > 0);
/* before writing...ensure we have enough room */
if (stk_pnt > max_size)
//error. do something?
puts("number_to_str passed number with too many digits to go into buffer\n");
//printf("error. stk_pnt > max_size (%d > %d)\n", stk_pnt, max_size);
return 0;
/* reorder */
while (stk_pnt > 0)
buffer[bpos++] = buff_stack[--stk_pnt];
return bpos;
对不起,伙计们,我看不出我做错了什么。我很欣赏这是一个“代码墙”类型的场景,但希望有人能看到我做错了什么。感谢您可能不喜欢不使用 VA_ARGS 但我不明白为什么这种技术不应该起作用?而且,我也与 -nostdlib 链接。如果有人可以提供帮助,我将不胜感激。此外,这并不意味着是生产质量代码,所以如果我缺乏一些 C 基础知识,请随时提出建设性意见:-)
【问题讨论】:
【参考方案1】:以这种方式编码是个坏主意。使用stdarg.h
。
如果您正在开发一个爱好内核或嵌入式项目并希望避免使用标准库(我假设这是基于名称 kprintf
),我建议您至少编写自己的(架构和编译器)具体)一组符合众所周知的接口和代码的 stdarg 宏。这样一来,通过取消引用最后一个参数的地址,您的代码就不会看起来像这样的 WTF。
您可以创建一个va_list
类型来存储最后一个已知地址,并且您的va_arg
宏可以适当地对齐它传递的类型的sizeof
并相应地推进指针。对于我为 x86 处理的大多数约定,每种类型都提升为 32 位...
【讨论】:
请注意,来自stdarg.h
的va_*()
宏就是这样的宏。它们通常扩展到类似于 OP 尝试编写的内联代码(仅正确),并且它们不需要与任何库链接。比如stdarg.h
通常是GCC提供的头文件之一,而不是系统C库。
@caf 我原则上同意,但是当我在开发一个爱好内核时,我在使用这种方法时遇到了一些困难。标准标头可能取决于其他标头,并且可能与您尝试执行的操作相冲突。自己编写通常更简单(或者至少不那么令人头疼)。
一般情况下这可能是正确的,但stdarg.h
是一种特殊情况 - 作为独立标题之一(以及float.h
、iso646.h
、limits.h
、stdbool.h
, stddef.h
和 stdint.h
),它不应该拖入任何不愉快的东西,并且应该可以安全地在内核环境中使用。【参考方案2】:
您必须阅读适用于您平台的calling conventions,即如何在您的目标处理器/操作系统上传递函数参数,以及如何保存寄存器。并非所有参数都在堆栈上传递。根据参数的数量及其类型,可能会出现许多复杂的情况。
【讨论】:
我用的是x86,没有底层操作系统。我正在使用 gcc 和我的编译器。您的链接对我说的是它可能使用寄存器。因此,从堆栈中弹出(当参数在寄存器中时)可能会检索到错误的值。如果这是真的,你能推荐一个解决我想要实现的目标的方法吗? 有没有办法将其隔离为真正的原因(而不仅仅是我上面的逻辑错误?您所描述的内容是按照优化器内联代码的方式进行的,所以它没有' t 导致堆栈修改? 我想知道我怎么能说得更好:遵循目标的调用约定。奇怪的是,它们比您上面编写的代码要复杂得多。【参考方案3】:我应该补充一点:如果你想像上面那样手动操作堆栈,你需要在汇编程序中进行,而不是在 C 中。C 语言遵循定义的标准,并且你在上面做的不是法律代码(即它的含义没有明确定义)。因此,允许编译器对其进行任何操作,例如以不适合您需要的奇怪方式对其进行优化。
【讨论】:
以上是关于为啥我的 C 代码片段不起作用?简化版可以。为 unsigned long long 传递不带 VA_ARGS 的 args的主要内容,如果未能解决你的问题,请参考以下文章