C语言 以下va_arg宏如何工作?



我正在读取一个 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的定义中还有其他一些魔力——这并不奇怪,因为它是一个非常神奇的宏!——但这至少应该解释逗号运算符在那里做什么。

相关内容

  • 没有找到相关文章

最新更新