C中的typeof括号是什么意思?
What does parentheses mean in typeof in C?
我正在研究Linux container_of
函数,发现typeof的括号的使用很迷惑
这里是函数的代码container_of
:
#define container_of(ptr, type, member) ({ \
const typeof( ((type*)0)->member) * __mptr =(ptr);\
(type*)( (char*)__mptr - offsetof(type,member) );})
在第二行,我们有
const typeof( ((type*)0)->member) * __mptr =(ptr);
不明白为什么要在右边加括号
网上搜了一下,找到了一些类似的用法。例如,从 https://gcc.gnu.org/onlinedocs/gcc/Typeof.html 中,我找到了一个函数 max(a,b)
#define max(a,b)
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
在这种情况下,我们确实有类似的用法typeof (a) _a = (a);
为什么要在右边加括号?我们可以把右边的括号去掉,变成typeof (a) _a = a;
吗?
提前致谢!
当使用 container_of
宏时,我们不知道参数将用于 ptr
。它可能是一个标识符名称,如 MyPointer
。但它可能是一个表达式,例如 MyArray + 3
.
宏中的常见做法是在替换列表中的参数周围使用括号。例如,如果我们定义 square
:
#define square(x) ((x) * (x))
然后square(3+4)
将替换为((3+4) * (3+4))
,也就是我们想要的49。如果我们改用:
#define square(x) (x * x)
那么square(3+4)
将被替换为(3+4 * 3+4)
,这将被评估为3 + 4*3 + 4
,即3 + 12 + 4
,即19,这是不需要的。
同样,在您显示的代码中,const typeof( ((type*)0)->member) * __mptr =(ptr);
、ptr
用于初始化名为 __mptr
的新定义对象。为了安全起见,它在括号中。它不会改变 typeof
的运作方式。
您不应删除这些括号。在这个特定的上下文中,通常不会为 ptr
传递太多会在没有括号的情况下破坏宏的内容,但是有一些理论上可能会破坏宏的表达式。
通过 #define
创建的宏执行直接标记替换。因此,在类函数宏的参数周围使用括号是一种很好的做法,因为它可以防止发生意外问题。
例如:
#define multiply(x, y) x * y
如果你在这里使用这个宏:
int z = multiply(1 + 3, 4 + 5) / 3;
您可能认为 z
包含值 12,但您会误会。
它将上面的内容扩展为:
int z = 1 + 3 * 4 + 5 / 3;
这将导致 14。使用括号可避免此问题。如果它被定义为:
#define multiply(x, y) ((x) * (y))
那么上面的例子将扩展为:
int z = ((1 + 3) * (4 + 5)) / 3;
这会给出预期的结果。
我正在研究Linux container_of
函数,发现typeof的括号的使用很迷惑
这里是函数的代码container_of
:
#define container_of(ptr, type, member) ({ \
const typeof( ((type*)0)->member) * __mptr =(ptr);\
(type*)( (char*)__mptr - offsetof(type,member) );})
在第二行,我们有
const typeof( ((type*)0)->member) * __mptr =(ptr);
不明白为什么要在右边加括号
网上搜了一下,找到了一些类似的用法。例如,从 https://gcc.gnu.org/onlinedocs/gcc/Typeof.html 中,我找到了一个函数 max(a,b)
#define max(a,b)
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
在这种情况下,我们确实有类似的用法typeof (a) _a = (a);
为什么要在右边加括号?我们可以把右边的括号去掉,变成typeof (a) _a = a;
吗?
提前致谢!
当使用 container_of
宏时,我们不知道参数将用于 ptr
。它可能是一个标识符名称,如 MyPointer
。但它可能是一个表达式,例如 MyArray + 3
.
宏中的常见做法是在替换列表中的参数周围使用括号。例如,如果我们定义 square
:
#define square(x) ((x) * (x))
然后square(3+4)
将替换为((3+4) * (3+4))
,也就是我们想要的49。如果我们改用:
#define square(x) (x * x)
那么square(3+4)
将被替换为(3+4 * 3+4)
,这将被评估为3 + 4*3 + 4
,即3 + 12 + 4
,即19,这是不需要的。
同样,在您显示的代码中,const typeof( ((type*)0)->member) * __mptr =(ptr);
、ptr
用于初始化名为 __mptr
的新定义对象。为了安全起见,它在括号中。它不会改变 typeof
的运作方式。
您不应删除这些括号。在这个特定的上下文中,通常不会为 ptr
传递太多会在没有括号的情况下破坏宏的内容,但是有一些理论上可能会破坏宏的表达式。
通过 #define
创建的宏执行直接标记替换。因此,在类函数宏的参数周围使用括号是一种很好的做法,因为它可以防止发生意外问题。
例如:
#define multiply(x, y) x * y
如果你在这里使用这个宏:
int z = multiply(1 + 3, 4 + 5) / 3;
您可能认为 z
包含值 12,但您会误会。
它将上面的内容扩展为:
int z = 1 + 3 * 4 + 5 / 3;
这将导致 14。使用括号可避免此问题。如果它被定义为:
#define multiply(x, y) ((x) * (y))
那么上面的例子将扩展为:
int z = ((1 + 3) * (4 + 5)) / 3;
这会给出预期的结果。