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

时间:2014-06-07 20:17:38

标签: c macros struct null expression

我对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转换为类型的想法。

我的问题:

  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)
    
  3. 编辑:正如下面有很多人指出的那样,这两个例子并不相同:

    /* How genlib uses the macro. */
    New(struct MyStruct*)
    /* How I was using my macro. */
    struct MyStruct *ptr; New(ptr)
    

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

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))

代码

  1. NULL投射到struct MyStruct*,产生一个静态类型为struct MyStruct*的指针。
  2. 确定通过取消引用指针而形成的对象的大小。由于指针的类型为struct MyStruct*,因此它指向struct MyStruct类型的对象,因此产生struct MyStruct类型。
  3. 换句话说,这是一种获取指针类型对象的简单方法,以便您可以取消引用它并获取基础类型的对象。

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

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

    希望这有帮助!

答案 1 :(得分:4)

这是一个黑客。它依赖于sizeof运算符的参数实际上没有被评估的事实。

回答您的具体问题:

  1. 是的,NULL只是一个指针文字。像任何其他指针一样,它可以被强制转换。

  2. sizeof对类型或表达式进行操作。 *(type)既不会(在宏替换发生之后),也会出现语法错误。