文章目录
- 参考
- [gcc 内部的宏定义](#gcc 内部的宏定义)
- 代码
- 汇编
- 调用
- [在 SEI CERT C++ Coding Standard 这个标准里](#在 SEI CERT C++ Coding Standard 这个标准里)
- 示例
- 实例
- 宏里的使用
参考
gcc 内部的宏定义
宏定义:
使用的时builtin_va_end 宏定义。
c
#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)
使用到了下列内置函数来实现va-start、end、arg相关的宏。
c
/* Expand EXP, a call to __builtin_va_start. */
static rtx
expand_builtin_va_start (tree exp)
{
rtx nextarg;
tree valist;
location_t loc = EXPR_LOCATION (exp);
代码
c
int add(int first, int second, ... )
{
int r=first + second;
va_list va;;;
va_start(va, second);
while(int v= va_arg(va,int))
{
r+=v;
}
va_end(va);
return r;
}
汇编
c
962 0000000000400c56 <add(int, int, ...)>:
963 400c56: 55 push %rbp
964 400c57: 48 89 e5 mov %rsp,%rbp
965 400c5a: 48 83 ec 68 sub $0x68,%rsp 申请的栈空间是 0x68,但是使用的远远大于这个值。
966 400c5e: 89 bd 2c ff ff ff mov %edi,-0xd4(%rbp) 六个寄存器参数都用上了
967 400c64: 89 b5 28 ff ff ff mov %esi,-0xd8(%rbp) 而且将这六个参数放到超远的栈上。
968 400c6a: 48 89 95 60 ff ff ff mov %rdx,-0xa0(%rbp)
969 400c71: 48 89 8d 68 ff ff ff mov %rcx,-0x98(%rbp)
970 400c78: 4c 89 85 70 ff ff ff mov %r8,-0x90(%rbp)
971 400c7f: 4c 89 8d 78 ff ff ff mov %r9,-0x88(%rbp)
972 400c86: 84 c0 test %al,%al 看着调用一直时0,是否用到floating 的变量?几个
973 400c88: 74 20 je 400caa <add(int, int, ...)+0x54> 如果又floating的数值,将floating寄存器的值放到 栈
974 400c8a: 0f 29 45 80 movaps %xmm0,-0x80(%rbp) floating的值放到floating 寄存器。
975 400c8e: 0f 29 4d 90 movaps %xmm1,-0x70(%rbp)
976 400c92: 0f 29 55 a0 movaps %xmm2,-0x60(%rbp)
977 400c96: 0f 29 5d b0 movaps %xmm3,-0x50(%rbp)
978 400c9a: 0f 29 65 c0 movaps %xmm4,-0x40(%rbp)
979 400c9e: 0f 29 6d d0 movaps %xmm5,-0x30(%rbp)
980 400ca2: 0f 29 75 e0 movaps %xmm6,-0x20(%rbp)
981 400ca6: 0f 29 7d f0 movaps %xmm7,-0x10(%rbp)
982 400caa: 8b 95 2c ff ff ff mov -0xd4(%rbp),%edx
983 400cb0: 8b 85 28 ff ff ff mov -0xd8(%rbp),%eax
984 400cb6: 01 d0 add %edx,%eax 第一个和第二个参数相加放到eax
985 400cb8: 89 85 4c ff ff ff mov %eax,-0xb4(%rbp) 然后放到 栈
986 400cbe: c7 85 30 ff ff ff 10 movl $0x10,-0xd0(%rbp) 将16 放到 栈
987 400cc5: 00 00 00
988 400cc8: c7 85 34 ff ff ff 30 movl $0x30,-0xcc(%rbp) 将0x30 放到栈
989 400ccf: 00 00 00
990 400cd2: 48 8d 45 10 lea 0x10(%rbp),%rax 将rbp + 16 的值放到rax,使用到了上一个函数栈。
991 400cd6: 48 89 85 38 ff ff ff mov %rax,-0xc8(%rbp) ,放到栈
992 400cdd: 48 8d 85 50 ff ff ff lea -0xb0(%rbp),%rax 将rbp -0xb0的地址放到rax
993 400ce4: 48 89 85 40 ff ff ff mov %rax,-0xc0(%rbp) 放到栈
994 400ceb: 8b 85 30 ff ff ff mov -0xd0(%rbp),%eax 将rbp-0xd0的值放到eax
995 400cf1: 83 f8 2f cmp $0x2f,%eax 对比0x2f 为什么比对2f?
996 400cf4: 77 23 ja 400d19 <add(int, int, ...)+0xc3>
997 400cf6: 48 8b 85 40 ff ff ff mov -0xc0(%rbp),%rax
998 400cfd: 8b 95 30 ff ff ff mov -0xd0(%rbp),%edx
999 400d03: 89 d2 mov %edx,%edx
1000 400d05: 48 01 d0 add %rdx,%rax
1001 400d08: 8b 95 30 ff ff ff mov -0xd0(%rbp),%edx
1002 400d0e: 83 c2 08 add $0x8,%edx
1003 400d11: 89 95 30 ff ff ff mov %edx,-0xd0(%rbp)
1004 400d17: eb 12 jmp 400d2b <add(int, int, ...)+0xd5>
1005 400d19: 48 8b 85 38 ff ff ff mov -0xc8(%rbp),%rax
1006 400d20: 48 8d 50 08 lea 0x8(%rax),%rdx
1007 400d24: 48 89 95 38 ff ff ff mov %rdx,-0xc8(%rbp)
1008 400d2b: 8b 00 mov (%rax),%eax
1009 400d2d: 89 85 48 ff ff ff mov %eax,-0xb8(%rbp)
1010 400d33: 83 bd 48 ff ff ff 00 cmpl $0x0,-0xb8(%rbp)
1011 400d3a: 74 0e je 400d4a <add(int, int, ...)+0xf4>
1012 400d3c: 8b 85 48 ff ff ff mov -0xb8(%rbp),%eax
1013 400d42: 01 85 4c ff ff ff add %eax,-0xb4(%rbp) 将 eax 加到栈里
1014 400d48: eb a1 jmp 400ceb <add(int, int, ...)+0x95>
1015 400d4a: 8b 85 4c ff ff ff mov -0xb4(%rbp),%eax 最终的结果放到eax
1016 400d50: c9 leaveq
1017 400d51: c3 retq
调用
c
int main()
{
printf( "abc=%d\n", add(1,3,4,5,8,9,10);
return 1;
}
c
0000000000400968 <main>:
400968: 55 push %rbp
400969: 48 89 e5 mov %rsp,%rbp
40096c: 48 83 ec 08 sub $0x8,%rsp 按16 字节对齐。多8个字节占栈,如果多出来一个参数
400970: 6a 0a pushq $0xa
400972: 41 b9 09 00 00 00 mov $0x9,%r9d
400978: 41 b8 08 00 00 00 mov $0x8,%r8d
40097e: b9 05 00 00 00 mov $0x5,%ecx
400983: ba 04 00 00 00 mov $0x4,%edx
400988: be 03 00 00 00 mov $0x3,%esi
40098d: bf 01 00 00 00 mov $0x1,%edi
400992: b8 00 00 00 00 mov $0x0,%eax 把 eax 清空
400997: e8 aa fe ff ff callq 400846 <add(int, int, ...)>
40099c: 48 83 c4 10 add $0x10,%rsp 释放栈
4009a0: 89 c6 mov %eax,%esi
4009a2: bf b2 0a 40 00 mov $0x400ab2,%edi
4009a7: b8 00 00 00 00 mov $0x0,%eax
4009ac: e8 3f fd ff ff callq 4006f0 <printf@plt>
4009b1: b8 01 00 00 00 mov $0x1,%eax
4009b6: c9 leaveq
4009b7: c3 retq
在 SEI CERT C++ Coding Standard 这个标准里
提到了更安全的C++定义方式。 这种方式将编程从运行时变参,转移到了编译时,更安全。
示例
https://en.cppreference.com/w/cpp/language/parameter_pack
c
#include <iostream>
void tprintf(const char* format) // base function
{
std::cout << format;
}
、、 这个会产生多少个函数来?
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
for ( ; *format != '\0'; format++ ) {
if ( *format == '%' ) {
std::cout << value;
tprintf(format+1, Fargs...); // recursive call
return;
}
std::cout << *format;
}
}
int main()
{
tprintf("% world% %\n","Hello",'!',123);
}
实例
变参传递到另一个函数里:
c
static inline void abc(int level, const char *format, ...)
{
char buff[UMAX_LOG_SIZE];
int msgLen;
va_list arglist;
memset(buff, 0, sizeof(buff));
va_start(arglist, format);
msgLen = vsnprintf(buff, UMAX_LOG_SIZE, format, arglist);
va_end(arglist);
宏里的使用
下面这个宏,只包含有,三个点所代表的参数;不包含三个点以外有名称的参数。
go
__VA_ARGS__
#define _FUNC1_(tn, constness, ct, Method, ...) \
class mock_##Method { \
public:\
RESULT_(tn, __VA_ARGS__) ct Method( \ // 这里__VA_ARGS__, 不包含 tn,constness,ct和Method