我正在读取一个 stdarg.h(下面的链接(头文件,该文件定义va_arg宏
如下/*
* Increment ap to the next argument in the list while returing a
* pointer to what ap pointed to first, which is of type t.
*
* We cast to void* and then to t* because this avoids a warning about
* increasing the alignment requirement.
*/
#define va_arg(ap, t)
(((ap) = (ap) + __va_argsiz(t)),
*((t*) (void*) ((ap) - __va_argsiz(t))))
该行
((ap) = (ap) + __va_argsiz(t))
重新分配ap的值,但是我不明白逗号或行的用途
*((t*) (void*) ((ap) - __va_argsiz(t)))
链接到标准达格.h文件
我们需要返回到调用者ap
指向的内容,并推进ap
。等效的将是
old_ap = ap
ap = ap + argsiz
return *old_ap
但是,这将需要一个额外的变量,这很难(如果可能的话(以可移植的方式在宏中处理。相反,宏依赖于逗号表达式。它前进ap
,然后计算其旧值,该值成为逗号表达式的值,即整个宏的值。
有一个带有逗号运算符的表达式。
第一个子表达式
(((ap) = (ap) + __va_argsiz(t))
增加指针ap
。
第二个子表达式
*((t*) (void*) ((ap) - __va_argsiz(t))))
返回指针在其增量之前ap
指向的值。
如果您有与逗号运算符有关的问题,那么它允许在一个表达式中组合多个完整的表达式。
例如,考虑以下程序
#include <stdio.h>
int main(void)
{
int a[] = { 1, 2 };
size_t i = 0;
printf( "%dn", ( i++, a[i] ) );
return 0;
}
它的输出是
2
这里printf
调用的参数是带有逗号运算符的表达式。
或者一个更有趣的例子
#include <stdio.h>
int main(void)
{
int a[] = { 1, 2 };
size_t i = 0;
printf( "%dn", a[i] ), i++, printf( "%dn", a[i] );
return 0;
}
程序输出为
1
2
这里的表达式语句
printf( "%dn", a[i] ), i++, printf( "%dn", a[i] );
包含一个使用两个逗号运算符的表达式。
假设我有一个指向某些字符的char *
指针p
。 假设我要返回p
指向的字符,同时递增p
以指向下一个字符。 这很简单,这只是*p++
- C语言中完全基本的操作。
由于指针算术的工作方式,如果我有一个指向一些整数的int *
指针ip
,我可以做完全相同的事情。*ip++
返回ip
指向的整数,并ip
递增以指向下一个整数。 至关重要的是,如果我们在此操作之前和之后查看指针ip
的实际值,我们会发现它增加了sizeof(int)
,而不仅仅是 1。
现在,va_arg(ap, t)
有点像*ip++
。 它返回参数"指向"ap
,并递增ap
指向列表中的下一件事。但是,这是一个非常大的">但是",我们不一定想增加sizeof(int)
. 我们希望按sizeof(t)
递增,其中t
是调用者告诉我们的列表中当前预期的参数类型。 (我们可以假设指针ap
的实际类型是char *
,以便它按单个字节递增,以便向其添加sizeof(t)
是有意义的。
所以我们想要的是*(ap += sizeof(t))
的效果。 但是+=
给你前增量,我们想要后增量。 虽然 C 语言可以方便地区分前递增++
和后递增++
,但+=
运算符始终是预递增的。 没有后增量表单。
因此,您为va_arg
显示的代码必须使用由逗号运算符分隔的两个单独的表达式,以困难的方式模拟后增量+=
。 首先,它使用子表达式(ap) = (ap) + __va_argsiz(t)
将sizeof(int)
添加到ap
,然后通过减去刚刚添加的大小来返回ap
过去指向的内容:*((t*) (void*) ((ap) - __va_argsiz(t)))
。
逗号运算符总是按顺序做两件事:它做第一件事并丢弃结果,然后它做第二件事并返回第二件事的值。v_arg
的这一定义是为了利用该定义而安排的。 我们不关心第一个子表达式的值 — 第一个表达式的重点是它的副作用,即向ap
添加一些东西。 我们需要的值(即va_arg
的值(是第二个子表达式。
(换句话说,根据逗号运算符的工作方式,不可能编写一个表达式,首先返回ap
指向的内容,然后向ap
添加一些内容。 因此,这个va_arg
宏的作者别无选择,只能过早地进行加法,这意味着它必须在后半部分通过减法来抵消。
在这个va_arg
的定义中还有其他一些魔力——这并不奇怪,因为它是一个非常神奇的宏!——但这至少应该解释逗号运算符在那里做什么。