为啥我的 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.hva_*() 宏就是这样的宏。它们通常扩展到类似于 OP 尝试编写的内联代码(仅正确),并且它们不需要与任何库链接。比如stdarg.h通常是GCC提供的头文件之一,而不是系统C库。 @caf 我原则上同意,但是当我在开发一个爱好内核时,我在使用这种方法时遇到了一些困难。标准标头可能取决于其他标头,并且可能与您尝试执行的操作相冲突。自己编写通常更简单(或者至少不那么令人头疼)。 一般情况下这可能是正确的,但stdarg.h 是一种特殊情况 - 作为独立标题之一(以及float.hiso646.hlimits.hstdbool.h , stddef.hstdint.h),它不应该拖入任何不愉快的东西,并且应该可以安全地在内核环境中使用。【参考方案2】:

您必须阅读适用于您平台的calling conventions,即如何在您的目标处理器/操作系统上传递函数参数,以及如何保存寄存器。并非所有参数都在堆栈上传递。根据参数的数量及其类型,可能会出现许多复杂的情况。

【讨论】:

我用的是x86,没有底层操作系统。我正在使用 gcc 和我的编译器。您的链接对我说的是它可能使用寄存器。因此,从堆栈中弹出(当参数在寄存器中时)可能会检索到错误的值。如果这是真的,你能推荐一个解决我想要实现的目标的方法吗? 有没有办法将其隔离为真正的原因(而不仅仅是我上面的逻辑错误?您所描述的内容是按照优化器内联代码的方式进行的,所以它没有' t 导致堆栈修改? 我想知道我怎么能说得更好:遵循目标的调用约定。奇怪的是,它们比您上面编写的代码要复杂得多。【参考方案3】:

我应该补充一点:如果你想像上面那样手动操作堆栈,你需要在汇编程序中进行,而不是在 C 中。C 语言遵循定义的标准,并且你在上面做的不是法律代码(即它的含义没有明确定义)。因此,允许编译器对其进行任何操作,例如以不适合您需要的奇怪方式对其进行优化。

【讨论】:

以上是关于为啥我的 C 代码片段不起作用?简化版可以。为 unsigned long long 传递不带 VA_ARGS 的 args的主要内容,如果未能解决你的问题,请参考以下文章

为啥软键盘在对话框片段内的 Web 视图中不起作用?

当指针指向数组时,为啥 operator(*) 的值不起作用?

为啥这个片段不起作用?断开的模型没有被实施

片段 getActivity 不起作用

为啥我的 C# IS 语句不起作用?

为啥我不能从 FragmentPagerAdapter 分离片段?