c语言中参数列表可变的函数写法,如printf

本文介绍了一个自定义的类似printf的可变参数函数实现过程,包括如何通过内联汇编和标准C库函数va_start等处理可变参数,并展示了如何打印这些参数。

研究了一段时间反汇编,突然发现多年以前羡慕的printf样式的函数我也会写了

如下面的这个函数(测试环境VS2008)

DWORD Fun(int n,...)       //n为参数书目,最多处理100个参数,并且每个参数要是一个4字节的变量,如DWORD/int/long/HANDLE
{
 DWORD para[100];      //最多处理100个参数,如果用动态内存方式可以处理参数数量不收限制
 for(int i=0;i<n;i++)
 {
  __asm  //从栈中的参数列表把参数复制到para数组里
  {
   lea ebx,[ebp+12]        //[ebp+12]为第1个参数(把n算做第0个参数)
   mov eax,i     
   mov edx,[ebx+eax*4] 
   lea eax,[para+eax*4]
   mov [eax],edx
  }
 }
 for(int i=0;i<n;i++)
 printf("%u/n",para[i]);  //把这些参数依次打印到屏幕
 return 7;
}

 

 

int _tmain(int argc, _TCHAR* argv[])
{
 DWORD d=Fun(10,65,3,7,88,23,11,8,9,7734,3784);  //调用这个可变参数的函数

return 0;

}

 

堆栈平衡由编译器在编译的时候控制。由于第0个参数只是简单的说明一下传递过来的参数数目没有对参数类型有任何说明,所以只能正确处理单个参数占4个字节的参数。

printf函数使用了一些其它机制来获取参数列表,单原理应该不会变:

 

int __cdecl printf (const char *format,...)  /*stdout 'PRINT', 'F'ormatted*/
{
        va_list arglist;
        int buffing;
        int retval;

        _VALIDATE_RETURN( (format != NULL), EINVAL, -1);

        va_start(arglist, format);  //能够使arglist指向盏中的参数列表

        _lock_str2(1, stdout);
        __try {

        buffing = _stbuf(stdout);

        retval = _output_l(stdout,format,NULL,arglist);

        _ftbuf(buffing, stdout);

        }
        __finally {
            _unlock_str2(1, stdout);
        }

        return(retval);
}

 

 

这些va_start,va_arg,va_end之类的东西暂时还不太会用。

 

char* arglist;
va_start(arglist, format);  //这个宏能直接得到参数列表首地址,比用一段内联汇编方便多了

 于是前面的那个函数就可以修改成如下样子:

 

DWORD Fun3(int n,...)
{
 char* arglist;
 va_start(arglist,n);
 for(int i=0;i<n;i++)
 {
  printf("%X/n",((DWORD*)arglist)[i]);
 }
 return 7788;
}

 

 

<think> 我们正在讨论C语言中自定义函数参数问题。根据引用,C语言支持参数个数可变函数(通过...语法),但用户现在关心的是如何“确定参数的含义”。这涉及到函数设计时的参数命名、类型指定和文档说明等方面。 用户的问题可以分为两部分: 1. 如何定义函数参数(包括固定参数可变参数) 2. 如何确定参数的含义(即如何让调用者清楚每个参数的作用) 对于固定参数,我们可以通过参数的类型和名称来表明含义;对于可变参数,则需要通过其他方式(如格式字符串或显式指定参数个数)来约定。 回答步骤: 1. 解释固定参数的定义和含义确定方法(参数类型、名称、注释) 2. 解释可变参数的定义和含义确定方法(通常需要至少一个固定参数来传递可变参数的信息,如printf的格式字符串) 3. 举例说明 同时,根据引用[3]中的例子,我们可以展示一个可变参数函数如何获取参数,并解释如何确定每个参数的含义。 注意:引用[2]提到了函数参数的求值顺序问题,但用户的问题是关于参数含义的确定,因此可能不直接相关,但可以简要提及参数求值顺序的陷阱。 最后生成相关问题。 </think>### C语言中自定义函数并确定参数的含义 在C语言中,自定义函数时可以通过以下方式确定参数的含义: 1. **参数声明和类型指定** 在函数定义中明确声明参数类型和名称是最基本的方式: ```c // 函数声明 int calculate_area(int width, int height); // 函数实现 int calculate_area(int width, int height) { return width * height; // 参数名直接说明含义 } ``` 这里`width`和`height`清晰表明了参数的物理意义。 2. **使用注释文档** 通过注释说明参数含义(Doxygen风格): ```c /** * @brief 计算两个数的加权和 * @param base 基础值 * @param ratio 权重系数 (0.0-1.0) * @param offset 偏移量 * @return 计算结果 */ double weighted_sum(double base, double ratio, double offset) { return base * ratio + offset; } ``` 3. **结构体封装相关参数** 当参数较多时,使用结构体封装并命名: ```c typedef struct { int x; int y; int radius; } CircleParams; void draw_circle(CircleParams params) { // 通过结构体成员名明确含义 printf("Drawing circle at (%d,%d) r=%d", params.x, params.y, params.radius); } ``` 4. **枚举类型替代魔法数字** 用枚举增强参数可读性: ```c typedef enum { LOGIN, LOGOUT, UPDATE } ActionType; void handle_action(ActionType action) { switch(action) { case LOGIN: /* 处理登录 */ break; case LOGOUT: /* 处理登出 */ break; case UPDATE: /* 处理更新 */ break; } } ``` 5. **可变参数的约定**(参考引用[1][3]) 对于可变参数函数,需通过首个参数约定后续参数含义: ```c // 首个参数定义格式:%d=整型, %f=浮点, %s=字符串 void print_values(const char* fmt, ...) { va_list args; va_start(args, fmt); while(*fmt) { if(*fmt == '%') { switch(*(++fmt)) { case 'd': printf("%d", va_arg(args, int)); break; case 'f': printf("%f", va_arg(args, double)); break; case 's': printf("%s", va_arg(args, char*)); break; } } fmt++; } va_end(args); } // 调用示例:print_values("%d%f%s", 10, 3.14, "text"); ``` ### 注意事项 1. **避免歧义名称** 如`process(data)`改为`process_image(img_data)` 2. **参数顺序一致性** 保持相似函数参数顺序统一(如坐标总是(x,y)) 3. **编译器警告** 启用`-Wstrict-prototypes`检查参数类型不匹配 4. **参数求值顺序**(参考引用[2]) C标准未定义参数求值顺序,避免`func(i++, i--)`这类依赖顺序的写法 --- ### 相关问题 1. 如何防止可变参数函数出现类型安全问题? 2. 在C语言中如何实现默认参数值的功能? 3. 结构体作为函数参数时,传值和传地址有何性能差异? 4. 函数参数中的`const`关键字有什么作用? [^1]: C语言中实现参数个数不确定的函数 [^2]: C语言函数参数的秘密 [^3]: C语言函数不确定参数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值