2

我在学校的老师一直将NULL转换为(struct foo *),然后再将其分配给指向结构 foo 的指针。例如:

struct foo {
   // smthg else
   struct foo *ptr;
};

struct foo *bar;
bar = (struct foo*)malloc(sizeof(struct foo));
bar->ptr = (struct foo *) NULL;

让我感到困惑的是,他唯一一次测试该值是否为 NULL(检查链表是否已结束)他执行以下操作

if (aux->ptr != (struct foo *) NULL) {
   ...
}

我问他为什么,如果要“增加” NULL 的大小以防止错误或其他什么。但是不,他告诉我(我认为非常不情愿)这是为了防止编译器可能出现一些错误(wtf?)。这真的有必要吗?

编辑:

他教的语言是 C。他可能不知道在 C 中强制转换 malloc 是不必要的。我想知道他只是小心还是无能。:P

4

6 回答 6

7

回答您的问题:不,没有必要强制NULL转换为特定的指针类型。编译器为它做了一个特殊情况,NULL并允许将其分配给任何指针类型,而不会出现指针类型转换警告。

同样,在与 比较时也不需要强制转换NULL

if (aux->ptr != NULL) {
于 2012-05-09T05:31:58.360 回答
4

The correct form of

struct foo *bar = (struct foo*)malloc(sizeof(struct foo));

in C is always

struct foo *bar = malloc(sizeof *bar);

Your professor is wrong and probably picked up bad habits from trying to use malloc in C++ (which is generally frowned upon, for good reasons).

于 2012-05-09T05:51:36.733 回答
2

曾经有错误#define NULL 0的编译器具有并且 0 不是 (C89) C 标准所要求的空指针常量。这些天来,您不太可能遇到这样的编译器。

它曾经在上下文中很重要,例如execl("cmd", "arg0", "arg1", NULL);因为 NULL 需要是一个空指针常量,但是......这些天,NULL 是一个空指针常量,因此即使在那里也不需要强制转换。对于赋值操作,您可以只写0而不是强制转换 NULL。在execl()示例中,您不能只写 0;那是一个int并且不一定是空指针常量。

于 2012-05-09T06:02:33.287 回答
1

没有必要将 NULL 强制转换为特定的指针类型,因为 NULL 被 #defined 为 0。我能想到的唯一原因是有教育意义的,因为在编写程序时记住对象和指针的类型是件好事.

在 C++ 中,您将使用 nullptr 进行初始化并使用 g++ -std=c++0x 进行编译

最后,有必要在 C++ 中转换 malloc 的返回值,而不是在 C 中。不过,在 C++ 中,您应该使用“new”。

于 2012-05-09T05:38:13.843 回答
1

在 C 中,malloc不需要将结果转换为变量类型。在 C++ 中,它必须是,否则你会得到一个编译时错误(不能隐式转换void *your_class *)。

如果您的老师试图教您正确的编码原则,则需要演员表。如果你的老师试图教你 C 为 C++ 做准备,那么学习使用演员表是很有必要的。老师说“为了防止编译器可能出现一些错误”并没有什么“wtf”——你可能应该更经常地接受他的建议。假设你的教授是无知的可能不是要走的路。

于 2012-05-09T05:28:01.610 回答
-2

这个问题的正确答案是你的老师没有错,他/她也不是无知。他/她只是在教你好习惯。使用显式强制转换不是必须的,但至少有两个很好的理由应该这样做。

  1. 代码可移植性

    使用显式转换可确保您的代码可在 ANSI C 编译器、前 ANSI 编译器以及最重要的非 ANSI C 兼容编译器之间移植。您的老师的代码不应该与任何编译器产生错误或警告,兼容或不兼容,过去、现在或未来。

    如果在早于 ANSI C 或不符合 ANSI 的任何平台上编译,请编写代码:

    pMyObject = NULL;
    

    在某些系统上,可能会生成编译器警告,如下所示:

    警告:“=”从“void*”转换为“struct foo*”
    警告:“=”从“int”转换为“struct foo*”
    

    编译器警告的目的是提醒您注意潜在的问题,因此即使使用单个警告进行编译的代码也不应被视为生产质量代码,除非通过显式强制转换来修复警告。

  2. 代码可读性

    使用显式强制转换大大提高了可读性。例如,以下基于Microsoft Windows SDK的真实世界示例:

    hMyWindow = CreateWindow(NULL, NULL, WM_OVERLAPPED, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
    如果(hMyWindow == NULL)
        ...
    

    除非您参考 Microsoft Windows SDK,否则很明显您将无法理解传递给此函数的参数。但是,如果我写:

    hMyWindow = CreateWindow((LPCTSTR) NULL, (LPCTSTR) NULL, WM_OVERLAPPED, 0, 0, 100, 100, (HWND) NULL, (HMENU) NULL, (HINSTANCE) NULL, (LPVOID) NULL);
    如果 (hMyWindow == (HWND) NULL)
        ...
    

    然后我马上就知道了:1)变量的类型hWindow,2)返回类型CreateWindow(),3)每个函数参数的类型。几乎所有我需要知道的东西来理解这段代码,我不必扫描当前文件,或者寻找另一个头文件,或者浪费时间浏览互联网寻找 API 文档。这使得代码自我记录,如果您处于许多没有内置代码浏览支持的非图形环境之一,这一点尤其重要。

在 ANSI C99(ISO/IEC 9899:1999,第 6.5.16.1 节)中,ANSI 同意 NULL 指针常量的概念,并且将NULL 指针常量分配给类型化对象的指针是合法的。

然而,你的老师教你上述方法的真正原因是因为他/她只是像任何好老师一样,为现实世界做好准备,而在现实世界中,ANSI C 标准不是法律。它不必遵循,在大多数情况下也不会遵循。如果要获得财务利润或市场份额,编译器公司会忽略或扩展任何标准。此外,存在许多专有的计算机系统,其中一些来自世界上最大的制造商,其中安装了非 ANSI C 兼容的编译器并用于编译 C 程序,因为它们必须支持非标准硬件。

这就是为什么在执行任何涉及的指针操作时使用显式强制NULL转换是一个非常好的习惯。它将确保您的代码可以快速理解,编译干净,并在您以后遇到的各种平台和编译器工具中按预期工作。

于 2012-05-09T09:58:04.613 回答