(此问题涉及汇编语言。)我有点困惑。我已经遇到过许多Windows函数,这些函数应该返回Handle,否则,它们将返回NULL。为什么随后的检查会检查零?零不等于NULL。
例如:
GetModuleHandleA:
https://docs.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf- libloaderapi-getmodulehandlea


评论

旁注:我认为最初所有这些方法都记录为在失败时返回零,但现在大多数都切换为“ NULL”,但是您仍然可以找到“零”-docs.microsoft.com/zh-cn/windows/win32/api/ libloaderapi /…(这只是问题中的函数的Ex版本)

#1 楼

在C语言和许多其他低级编程语言中,术语NULL等效于0
C标准要求将NULL的#define d设置为“实现定义值”,但是所有实现都选择了(出于明显的原因)为此使用0。因此,如果您尝试对NULL进行“查看定义”,那么许多IDE会将您放置在#define NULL 0行或类似的行中。
从严格的标准遵循角度来看,正确的方法是使用NULL而不是0,这就是大多数开发人员的工作。但是,编译器(或在false情况下为预处理器)会将其转换为NULL
一些高级语言(例如javascript和C ++)使用特殊表达式来表示null。一个示例是C ++的#define NULL 0,由于C ++ 11是0的必需定义。 Javascript使用特殊对象nullptr

评论


评论不作进一步讨论;此对话已移至聊天。

– 0xC0000022L♦
12月7日8:23

#2 楼

在查看Windows API调用或反汇编C / C ++代码时,NULL始终为0,在Visual Studio中,它是在vcruntime.h中定义的。必须为零,例如在.NET C#代码中,例如:
#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

将编译为通用中间语言(CIL)。您可以使用ldnull看到null不只是零。
if (args == null)
{
    Console.WriteLine("null!");
}


评论


指针上下文中的0在C和C ++中具有特殊含义。此定义不能明确告诉您空指针的对象表示形式为0x00000000。 (在x86的所有主流C ++实现中以及几乎所有主流C和C ++实现中都是这种情况。历史上有一些例外情况:NULL宏何时不为0?在其中提到了一些并引用了C标准re:0源与使用的位模式。)

– Peter Cordes
12月4日16:48



这意味着ISO C ++不保证memset(ptr_array,0,16)会将指针初始化为NULL,或者在OP的情况下,比较将在生成的asm中对照零进行检查。

– Peter Cordes
12月4日16:49



#3 楼

尽管要求将源中的文字0(void*)0(在指针上下文中)评估为空指针,但ISO C和C ++允许实现将非零位模式用作空指针的对象表示。 NULL。在C或C ++中,基于源定义(例如#define NULL 0)的推理是不够的。空值。这使像0这样的不可移植代码按预期工作,等效于将每个元素设置为memset(ptr_array, 0, size)的循环。在询问源代码级的非零定义,但是我认为这在现代C语言中是不允许的。答案提到了一些具有非零空指针位模式的历史机器。 (即,您会在asm中看到类似NULL这样的代码)

请记住,在asm中,指针只是64位(或32位)整数。 do {...} while(p = p->next);的整个思想是带内信号传输,不是什么甚至不是指针大小的整数的特殊东西。因此,我们必须选择一个常数。
NULL是一个方便的哨兵值,因为与非检查其他任何值相比,许多ISA在非零值上的分支效率更高。例如ARM有0可以分支到非零分支,而无需cbnz。 x86的代码大小优化为cmp / test eax, eax而不是jnz / cmp eax, 0。 (使用CMP reg,0 vs OR reg,reg测试一个寄存器是否为零?)。如果FLAGS已由另一条算术指令设置,则不需要jnz,但这对于空指针测试是不寻常的:通常,您不先对指针进行数学运算,然后对NULL进行运算。
(您没有看到在您的asm中进行了优化,因为您的调试版本会在测试之前存储到内存中。)
另外,test很容易生成。在寄存器中创建大量指令可能需要较大的指令或大多数指令。 (例如x86 0而不是xor eax,eax)。零初始化静态存储(例如mov eax, imm32)可以在BSS中而不是static int *table = NULL;-现代系统对BSS进行零初始化。实际上那里有系统管理的东西,例如中断处理程序表的开始。因此.data可以是有效地址,也可以等于0。这有点烂,所以这实际上可能是想要空指针的非零对象表示形式的地方。 @Simon Richter评论了有关如何入侵ARM编译器以将0用作NULL位模式的问题。确保NULL引用实际上是错误的。 (请记住,C和C ++中的未定义行为不是必需的,但是这样做肯定很方便。)