符合标准的 C 实现是否可以 #define NULL 变得古怪 [英] Can a conforming C implementation #define NULL to be something wacky

查看:25
本文介绍了符合标准的 C 实现是否可以 #define NULL 变得古怪的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我之所以这么问是因为this线程.

尝试在其他人的回复下使用评论进行认真的来回讨论并不容易或有趣.所以我想听听我们的 C 专家的想法,而不是一次限制为 500 个字符.

C 标准对于NULL 和空指针常量几乎没有什么可说的.我只能找到两个相关部分.第一:

<块引用>

3.2.2.3 指针

一个整数常量表达式值 0,或这样的表达式强制转换为 void * 类型,称为 null指针常量.如果一个空指针常量被赋值或比较为了与指针相等,常量被转换为一个指针那种类型.这样的指针,称为空指针,保证比较不等于指向任何对象的指针或功能.

第二个:

<块引用>

4.1.5 通用定义

宏是

NULL

扩展为实现定义的空指针常量;

问题是,NULL 能否扩展为实现定义的空指针常量,与 3.2.2.3 中列举的空指针常量不同?

特别是,是否可以定义为:

#define NULL __builtin_magic_null_pointer

甚至:

#define NULL ((void*)-1)

我对 3.2.2.3 的解读是,它指定了 0 的整数常量表达式和转换为 void* 类型的 0 整数常量表达式必须属于实现识别的空指针常量的形式,但它并不是一个详尽的清单.我相信只要不违反其他规则,实现就可以自由地将其他源结构识别为空指针常量.

例如,可以证明

#define NULL (-1)

不是法律定义,因为在

if (NULL)做东西();

do_stuff() 不能被调用,而 with

if (-1)做东西();

do_stuff() 必须被调用;因为它们是等价的,所以这不能是 NULL 的合法定义.

但是标准说整数到指针的转换(反之亦然)是实现定义的,因此它可以将 -1 到指针的转换定义为产生空指针的转换.在这种情况下

if ((void*)-1)

将评估为 false,一切都会好起来的.

那么其他人怎么看?

我希望大家特别记住2.1.2.3 程序执行中描述的as-if"规则.它很大而且有点迂回,所以我不会在这里粘贴它,但它本质上是说一个实现只需要产生与标准描述的抽象机器相同的可观察到的副作用.它说任何优化、转换或编译器想要对你的程序做的任何其他事情都是完全合法的,只要程序的可观察副作用没有被它们改变.

因此,如果您想证明 NULL 的特定定义不合法,您需要想出一个可以证明它的程序.要么像我这样公然破坏标准中的其他条款,要么可以合法地检测编译器为使奇怪的 NULL 定义起作用而必须做的任何魔法.

Steve Jessop 找到了一个程序检测NULL 未定义为 3.2.2.3 中空指针常量的两种形式之一的方法示例,即对常量进行字符串化:

#define stringize_helper(x) #x#define stringize(x) stringize_helper(x)

使用这个宏,可以

puts(stringize(NULL));

并检测"NULL 不会扩展为 3.2.2.3 中的一种形式.这足以使其他定义非法吗?我就是不知道.

谢谢!

解决方案

在 C99 标准中,§7.17.3 声明 NULL扩展到一个实现定义的空指针常量em>".同时,第 6.3.2.3.3 节将 空指针常量定义为一个值为 0 的整数常量表达式,或者这样一个类型转换为 void * 的表达式".由于空指针常量没有其他定义,NULL 的符合定义必须扩展为值为 0 的整数常量表达式(或转换为 void *).

进一步引用 C FAQ 问题 5.5(强调):><块引用>C 标准的第 4.1.5 节声明 NULL 扩展为实现定义的空指针常量",这意味着实现可以选择使用哪种形式的 0 以及是否使用 `void *` 强制转换;见问题 5.6 和 5.7.此处的实现定义"并不意味着 NULL 可能被#defined 以匹配某些特定于实现的非零内部空指针值.

这是完全有道理的;由于标准要求指针上下文中的零整数常量编译为空指针(无论机器的内部表示是否具有零值),NULL 的情况 被定义为零,无论如何都必须处理.程序员不需要输入NULL来获得空指针;这只是一种风格约定(并且可能有助于捕获错误,例如当在非指针上下文中使用定义为 (void *)0NULL 时).

这里的一个混淆源似乎是标准使用的简洁语言,即它没有明确说明没有 other 值可能被视为空指针常量.然而,当标准说……被称为空指针常量"时,这意味着正是给定的定义被称为空指针常量.当(根据定义)标准定义什么符合时,它不需要通过说明什么是不符合来明确遵循每个定义.

I'm asking because of the discussion that's been provoked in this thread.

Trying to have a serious back-and-forth discussion using comments under other people's replies is not easy or fun. So I'd like to hear what our C experts think without being restricted to 500 characters at a time.

The C standard has precious few words to say about NULL and null pointer constants. There's only two relevant sections that I can find. First:

3.2.2.3 Pointers

An integral constant expression with the value 0, or such an expression cast to type void * , is called a null pointer constant. If a null pointer constant is assigned to or compared for equality to a pointer, the constant is converted to a pointer of that type. Such a pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

and second:

4.1.5 Common definitions

The macros are

NULL

which expands to an implementation-defined null pointer constant;

The question is, can NULL expand to an implementation-defined null pointer constant that is different from the ones enumerated in 3.2.2.3?

In particular, could it be defined as:

#define NULL __builtin_magic_null_pointer

Or even:

#define NULL ((void*)-1)

My reading of 3.2.2.3 is that it specifies that an integral constant expression of 0, and an integral constant expression of 0 cast to type void* must be among the forms of null pointer constant that the implementation recognizes, but that it isn't meant to be an exhaustive list. I believe that the implementation is free to recognize other source constructs as null pointer constants, so long as no other rules are broken.

So for example, it is provable that

#define NULL (-1)

is not a legal definition, because in

if (NULL) 
   do_stuff(); 

do_stuff() must not be called, whereas with

if (-1)
   do_stuff();

do_stuff() must be called; since they are equivalent, this cannot be a legal definition of NULL.

But the standard says that integer-to-pointer conversions (and vice-versa) are implementation-defined, therefore it could define the conversion of -1 to a pointer as a conversion that produces a null pointer. In which case

if ((void*)-1) 

would evaluate to false, and all would be well.

So what do other people think?

I'd ask for everybody to especially keep in mind the "as-if" rule described in 2.1.2.3 Program execution. It's huge and somewhat roundabout, so I won't paste it here, but it essentially says that an implementation merely has to produce the same observable side-effects as are required of the abstract machine described by the standard. It says that any optimizations, transformations, or whatever else the compiler wants to do to your program are perfectly legal so long as the observable side-effects of the program aren't changed by them.

So if you are looking to prove that a particular definition of NULL cannot be legal, you'll need to come up with a program that can prove it. Either one like mine that blatantly breaks other clauses in the standard, or one that can legally detect whatever magic the compiler has to do to make the strange NULL definition work.

Steve Jessop found an example of way for a program to detect that NULL isn't defined to be one of the two forms of null pointer constants in 3.2.2.3, which is to stringize the constant:

#define stringize_helper(x) #x
#define stringize(x) stringize_helper(x) 

Using this macro, one could

puts(stringize(NULL));

and "detect" that NULL does not expand to one of the forms in 3.2.2.3. Is that enough to render other definitions illegal? I just don't know.

Thanks!

解决方案

In the C99 standard, §7.17.3 states that NULL "expands to an implementation defined null pointer constant". Meanwhile §6.3.2.3.3 defines null pointer constant as "an integer constant expression with the value 0, or such an expression cast to type void *". As there is no other definition for a null pointer constant, a conforming definition of NULL must expand to an integer constant expression with the value zero (or this cast to void *).

Further quoting from the C FAQ question 5.5 (emphasis added):

Section 4.1.5 of the C Standard states that NULL "expands to an implementation-defined null pointer constant," which means that the implementation gets to choose which form of 0 to use and whether to use a `void *` cast; see questions 5.6 and 5.7. "Implementation-defined" here does not mean that NULL might be #defined to match some implementation-specific nonzero internal null pointer value.

It makes perfect sense; since the standard requires a zero integer constant in pointer contexts to compile into a null pointer (regardless of whether or not the machine's internal representation of that has a value of zero), the case where NULL is defined as zero must be handled anyhow. The programmer is not required to type NULL to obtain null pointers; it's just a stylistic convention (and may help catch errors e.g. when a NULL defined as (void *)0 is used in a non-pointer context).

Edit: One source of confusion here seems to be the concise language used by the standard, i.e. it does not explicitly say that there is no other value that might be considered a null pointer constant. However, when the standard says "…is called a null pointer constant", it means that exactly the given definitions are called null pointer constants. It does not need to explicitly follow every definition by stating what is non-conforming when (by definition) the standard defines what is conforming.

这篇关于符合标准的 C 实现是否可以 #define NULL 变得古怪的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆