我对Eric Roberts的一些代码有疑问。 Programming Abstractions in C。他使用自己的几个库来简化读者的事情并教授如何编写库。 (可以找到本书的所有库代码on this site。)
一个库genlib
提供了一个宏,用于指向struct
类型的指针的通用分配。我不了解宏的一部分。我将复制下面的代码,以及如何使用它的示例,然后我将更详细地解释我的问题。
/*
* Macro: New
* Usage: p = New(pointer-type);
* -----------------------------
* The New pseudofunction allocates enough space to hold an
* object of the type to which pointer-type points and returns
* a pointer to the newly allocated pointer. Note that
* "New" is different from the "new" operator used in C++;
* the former takes a pointer type and the latter takes the
* target type.
*/
#define New(type) ((type) GetBlock(sizeof *((type) NULL)))
/* GetBlock is a wrapper for malloc. It encasulates the
* common sequence of malloc, check for NULL, return or
* error out, depending on the NULL check. I'm not going
* to copy that code since I'm pretty sure it isn't
* relevant to my question. It can be found here though:
* ftp://ftp.awl.com/cseng/authors/roberts/cs1-c/standard/genlib.c
*/
Roberts打算按如下方式使用代码:
typedef struct {
string name;
/* etc. */
} *employeeT;
employeeT emp;
emp = New(employeeT);
他更喜欢使用指向记录的指针作为类型名称,而不是记录本身。因此New
提供了分配此类struct
记录的通用方法。
在宏观New
中,我不明白的是:sizeof *((type)) NULL)
。如果我正确地阅读了该内容,则表示"将NULL
的解除引用广播的大小视为给定调用中所代表的struct
类型type
" 。我想我理解解除引用:我们想为结构分配足够的空间;指针的大小不是我们需要的东西,所以我们取消引用以获得底层记录类型的大小。但我不理解将NULL
转换为类型的想法。
我的问题:
NULL
吗?这甚至意味着什么?为什么演员必要?当我尝试删除它时,编译器会说error: expected expression
。那么,sizeof *(type)
不是表达式吗?这使我感到困惑,因为我可以执行以下操作来获取任意指向结构的大小:
#define struct_size(s_ptr) do { \
printf("sizeof dereferenced pointer to struct %s: %lu\n", \
#s_ptr, sizeof *(s_ptr)); \
} while(0)
编辑:正如下面有很多人指出的那样,这两个例子并不相同:
/* How genlib uses the macro. */
New(struct MyStruct*)
/* How I was using my macro. */
struct MyStruct *ptr; New(ptr)
为了记录,这不是家庭作业。我是一名业余爱好者,试图在C处进行改进。此外,据我所知,代码没有任何问题。也就是说,我不会问我怎样才能做出与众不同的事情。我只是想更好地理解(1)它是如何工作的,以及(2)为什么它必须以它的方式编写。感谢。
答案 0 :(得分:5)
问题是宏需要获取指针类型指向的类型的大小。
例如,假设您具有指针类型struct MyStruct*
。如果不从此表达式中删除星号,您将如何获得struct MyStruct
的大小?你不能写
sizeof(*(struct MyStruct*))
因为那不是合法的C代码。
另一方面,如果你有struct MyStruct*
类型的变量,你可以这样做:
struct MyStruct* uselessPointer;
sizeof(*uselessPointer);
由于sizeof
实际上并未评估其参数(它只是确定表达式类型的静态大小),因此这是安全的。
当然,在宏中,您无法定义新变量。但是,您可以通过强制转换现有指针来构成指向struct MyStruct*
的随机指针。在这里,NULL
是一个很好的候选者 - 它是一个现有的指针,你可以合法地转换为struct MyStruct*
。因此,如果你要写
sizeof(* ((struct MyStruct*)NULL))
代码
NULL
投射到struct MyStruct*
,产生一个静态类型为struct MyStruct*
的指针。struct MyStruct*
,因此它指向struct MyStruct
类型的对象,因此产生struct MyStruct
类型。换句话说,这是一种获取指针类型对象的简单方法,以便您可以取消引用它并获取基础类型的对象。
我和Eric在其他一些宏上合作过,他是预处理器的真正专业人士。我对此并不感到惊讶,我并不感到惊讶,这很棘手,但它确实很聪明!
作为一个注释 - 在C ++中,这种技巧在引入declval
实用程序类型之前一直很常见,这是一个不太常见的操作版本。
希望这有帮助!
答案 1 :(得分:4)
这是一个黑客。它依赖于sizeof
运算符的参数实际上没有被评估的事实。
回答您的具体问题:
是的,NULL
只是一个指针文字。像任何其他指针一样,它可以被强制转换。
sizeof
对类型或表达式进行操作。 *(type)
既不会(在宏替换发生之后),也会出现语法错误。