为什么有必要将NULL转换为此宏中的类型?

Tel*_*hus 5 c macros null struct expression

我对Eric Roberts的C编程抽象中的一些代码有疑问.他使用自己的几个库来简化读者的工作并教授如何编写库.(该书的所有图书馆代码都可以在这个网站上找到.)

一个库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
 */
Run Code Online (Sandbox Code Playgroud)

Roberts打算使用如下代码:

    typedef struct {
        string name;
        /* etc. */
    } *employeeT;
    employeeT emp;
    emp = New(employeeT);
Run Code Online (Sandbox Code Playgroud)

他更喜欢使用指向记录的指针作为类型名称,而不是记录本身.因此New提供了分配此类struct记录的通用方法.

在宏观中New,我不明白的是:sizeof *((type)) NULL).如果我正确地阅读它,它会说"将取消引用的强制转换的大小视为给定调用中代表的NULL任何struct类型type".我想我理解解除引用:我们想为结构分配足够的空间; 指针的大小不是我们需要的,所以我们取消引用以获得底层记录类型的大小.但我不明白铸造NULL类型的想法.

我的问题:

  1. 你可以演员NULL?那有什么意思?
  2. 为什么演员必要?当我尝试删除它时,编译器说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)
    
    Run Code Online (Sandbox Code Playgroud)

编辑:正如下面许多人所指出的,这两个例子不一样:

/* How genlib uses the macro. */
New(struct MyStruct*)
/* How I was using my macro. */
struct MyStruct *ptr; New(ptr)
Run Code Online (Sandbox Code Playgroud)

为了记录,这不是功课.我是一名业余爱好者,试图在C处进行改进.此外,就我所知,代码没有任何问题.也就是说,我不是在问我怎样才能做出与众不同的事情.我只是想更好地理解(1)它是如何工作的,以及(2)为什么它必须以它的方式编写.谢谢.

tem*_*def 5

问题是宏需要获取指针类型指向的类型的大小.

例如,假设您具有指针类型struct MyStruct*.如果不从这个表达式中移除星星,你将如何获得大小struct MyStruct?你不能写

sizeof(*(struct MyStruct*))
Run Code Online (Sandbox Code Playgroud)

因为那不是合法的C代码.

另一方面,如果你有一个类型的变量struct MyStruct*,你可以这样做:

struct MyStruct* uselessPointer;
sizeof(*uselessPointer);
Run Code Online (Sandbox Code Playgroud)

因为sizeof实际上没有评估它的参数(它只是确定表达式类型的静态大小),所以这是安全的.

当然,在宏中,您无法定义新变量.但是,您可以struct MyStruct*通过强制转换现有指针来构成随机指针.在这里,NULL是一个很好的候选人 - 它是一个现有的指针,你可以合法地投射到struct MyStruct*.因此,如果你要写

sizeof(* ((struct MyStruct*)NULL))
Run Code Online (Sandbox Code Playgroud)

代码会

  1. 转换NULL为a struct MyStruct*,产生静态类型的指针struct MyStruct*.
  2. 确定通过取消引用指针将形成的对象的大小.由于指针有类型struct MyStruct*,它指向一个类型的对象struct MyStruct,因此产生类型struct MyStruct.

换句话说,它是一种获取指针类型对象的简单方法,以便您可以取消引用它并获取基础类型的对象.

我曾在其他一些宏上与Eric合作,他是预处理器的真正专业人士.我对此并不感到惊讶,我并不感到惊讶,这很棘手,但它确实很聪明!

作为一个注释 - 在C++中,这种技巧在引入declval实用程序类型之前是常见的,实用程序类型是此操作的一个不太常见的版本.

希望这可以帮助!