c - 为什么我的 C 代码片段不起作用?简化版可以。为 unsigned long long 传递不带 VA_ARGS 的参数

标签 c stack parameter-passing

基本上我正在为专用系统编写 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 函数。也许值得指出 此代码确实适用于 ints、char 字符串或任何其他传递的 % 标志 而不是指针。 所做 的工作与未签名的工作之间的唯一区别 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 基础知识,请随意对其进行建设性的:-)

最佳答案

以这种方式编码是个坏主意。使用 stdarg.h

在极少数情况下(我根据名称 kprintf 推测)您正在从事爱好内核或嵌入式项目并希望避免使用标准库,我建议至少编写您的自己的(体系结构和编译器特定的)一组符合众所周知的接口(interface)和代码的 stdarg 宏。这样你的代码就不会通过取消引用最后一个参数的地址而看起来像这样的 WTF。

您可以制作一个存储最后已知地址的 va_list 类型,并且您的 va_arg 宏可以适本地对齐该类型的 sizeof它通过并相应地推进指针。对于我为 x86 制定的大多数约定,每种类型都被提升为 32 位...

关于c - 为什么我的 C 代码片段不起作用?简化版可以。为 unsigned long long 传递不带 VA_ARGS 的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4550100/

相关文章:

powershell - 使用脚本中的参数调用第二个脚本

c++ - 通过引用函数 C++ 传递迭代器指向的对象

c - 如何使用 fork() 调用创建 k 个进程,其中 k 不是 2 的幂

字符设备捕获多个 (int) ioctl 参数

c - strtok 是否可以使用字符串(作为分隔符)?

algorithm - 检查两个字符串是否是字谜

stack - 可以仅使用一个堆栈来实现类似Forth的语言吗?

linux - 运行一个大小为 "Unlimited Stack"的应用会有什么影响?

带有 fscanf() 的 C 程序神秘地停止工作

ruby-on-rails - 将参数从 View 传递到 Controller 。然后回到一个 View