从Linux编程界面
execl(prog, arg, (char *) 0); execl(prog, arg, (char *) NULL);
以上述最后一次通话的方式投放
NULL
是 通常是必需的,甚至在NULL
为 定义为(void *) 0
。这是因为,尽管C标准要求使用空指针 不同类型的人应该测试真实性以进行平等比较, 不需要不同类型的指针具有相同的内部 表示(尽管在大多数实现中都可以做到)。
而且,在可变函数中,编译器无法强制转换(void *) 0
指向适当类型的空指针。C标准对指针的规则作了一个例外 的不同类型不必具有相同的表示形式:指针 类型
char *
和void *
中的相同 内部代表。这意味着通过(void *) 0
在示例情况下,用(char *) 0
代替不是问题execl()
,但是通常情况下需要强制转换。
“通常需要以上述最后一次调用的方式铸造NULL
”
C标准是否要求将空指针表示为与(char*) 0
相同?
”在诸如execl()
之类的可变函数中,编译器无法将(void *) 0
强制转换为适当类型的空指针。”
(void *) 0
是否不是类型的空指针?
如果是,为什么编译器不能将(void *) 0
中的execl(prog, arg, (void*) 0)
强制转换为“适当类型的空指针”?
char *
和void *
的“指针必须具有相同的内部表示。这意味着传递(void *) 0
而不是(char *) 0
不会execl()
”示例中的问题。
编译器现在可以将(void *) 0
中的execl(prog, arg, (void*) 0)
强制转换为“适当类型的空指针”吗?
为什么它与我的观点2中的引用矛盾?
如果我将(void *) 0
中的execl(prog, arg, (void*) 0)
替换为0到任何类型的指针(例如(int *) 0
),则编译器可以将{{1 }}为“适当类型的空指针”?
谢谢。
对于非变量函数调用,例如在(int *) 0
中,编译器可以将execl(prog, arg, (int*) 0)
强制转换为“适当类型的空指针”吗?
谢谢。
答案 0 :(得分:8)
首先,在任何情况下编译器都不会“广播”。强制转换是源代码中要求转换的语法构造。
我假设当您谈论“编译器强制转换”时,您的意思是谈论隐式转换,即将一种类型的值转换为另一种类型的值的过程,没有强制转换运算符。
该标准准确规定了可以应用隐式转换的上下文;必须始终有一个目标类型。例如,在代码int x = Y;
中,表达式Y
可以是非int
的某种类型,但可以隐式转换为定义的int
。
除default argument promotions之外,没有隐式转换应用于与原型的...
部分相对应的函数参数。对于指针值,默认参数Promotions使其保持不变。
您的问题的一个共同点似乎是,编译器应以某种方式假装execl
的行为就像最后一个参数已存在原型一样。但是实际上没有,并且编译器没有特定功能的任何魔术行为。通过的就是获得的。
该标准指定表达式(char *)0
的值为空指针。它没有提到任何有关空指针的表示,并且可能有多个不同的表示形式都是空指针。
execl
函数规范指出,参数列表应以(char *)0
终止,char *
是类型void *
的值。类型char *
的值不是类型char *
的值,如上所述,在此上下文中没有隐式转换。
仍然没有隐式转换;您引用的文字是说您可以在这种特定情况下使用错误的类型参数(不提供原型参数;可以使用void *
,但可以使用int *
,反之亦然)。
这将是未定义的行为,您在第3点中引用的文本不适用于sigaction
。
struct sigaction *oldact
函数有一个原型;该参数为class Demo {
string text;
public:
void Text(const string& updatedText) { text = updatedText; }
const string& Text() const { return text; }
};
。当您尝试使用不同类型的值初始化原型参数(或任何变量)时,将尝试隐式转换为参数的类型。从任何空指针值到其他类型的空指针值都有隐式转换。此规则在C11 6.3.2.3/4中。这样代码就可以了。
答案 1 :(得分:4)
1)C标准是否要求将空指针表示为与
相同(char*) 0
是的,因为空指针常量的类型为void *
,并且因为void *
和char *
具有相同的表示形式。
这在C standard的6.3.2.3p3节中有详细说明:
整数常量表达式,其值为0,或者这样的整数 转换为void *类型的表达式称为空指针常量。
第6.2.5p28节:
指向void的指针应具有相同的表示形式,并且 对齐要求作为字符类型的指针。 48)
...
48)相同的表示和对齐要求是 旨在暗示可互换性作为函数的参数, 从函数和联合成员返回值。
2)
(void *) 0
是否不是类型的空指针? 如果是,为什么不能 编译器将(void *) 0
中的execl(prog, arg, (void*) 0)
转换为“空 适当类型的指针”?
它是一种类型的空指针,并且该类型是void *
。
execl
的定义是:
int execl(const char *path, const char *arg, ...);
因此它无法将第三个参数转换为适当的类型,因为它不知道适当的类型是什么,但是没关系,因为void *
和char *
在6.2中是可互换的.4p28和脚注48,如上所述。
3)编译器能否在
(void *) 0
中强制转换execl(prog, arg, (void*) 0)
现在为“适当类型的空指针”? 为什么它与我的观点2中的引用矛盾?
它仍然不能转换,因为它不知道合适的类型是什么。但同样,这并不重要,因为void *
和char *
是可互换的。
4)如果我将
(void *) 0
中的execl(prog, arg, (void*) 0)
替换为 到(int *) 0
之类的任何类型指针的编译器是否可以将(int *) 0
中的execl(prog, arg, (int*) 0)
强制转换为“适当类型的空指针”?
否,因为它再次不知道合适的类型是什么。在这种情况下,如果int *
和char *
的表示形式不同,您可能会遇到问题。
5)对于非变量函数调用,例如在
sigaction(SIGINT, &sa, (int*) 0)
中,编译器可以将(int *) 0
强制转换为“适当类型的空指针”吗? < / p>
是的,因为(int *)0
是空指针,并且因为空指针可以转换为任何其他指针。
答案 2 :(得分:4)
自C99起,the specification of va_arg
会部分读取
如果[作为参数传递给
va_arg
的类型]与实际的下一个参数的类型不兼容(根据默认参数Promotions的提升),则该行为是不确定的,除了以下情况:
- 一种类型是有符号整数类型,另一种类型是相应的无符号整数类型,并且值在两种类型中均可表示;
- 一种类型是指向void的指针,另一种类型是指向字符类型的指针。
第二个要点表示,对于使用va_arg
访问其参数的可变参数函数,调用形式为
variadic_function("a", "b", "c", (void *)0);
每当有效
variadic_function("a", "b", "c", (char *)0);
会的。
不幸的是,有一个陷阱:我找不到可变的标准库函数 1 的任何要求,以[按它们的方式]通过以下方式访问其参数:对va_arg
进行一系列调用。您可能在想,嗯,他们还要怎么做?实际上,它是va_arg
或手写的汇编语言,也许委员会不想要求手写的汇编语言完全等同,但是我不必担心它。
因此,您所引用的书在技术上是错误的。但是,我仍然会写
execl(prog, arg, (char *) NULL);
如果我首先要使用NULL(我通常更喜欢使用0
作为null指针常量),因为您不应编写依赖的代码扩展到((void *)0)
,然后
execl(prog, arg, 0);
无疑是不正确的。例如,execl
不会在0
为32位,int
为64位且数量为char *
的任何ABI上从该int
接收空指针。作为变量参数列表的一部分传递时,不带符号扩展名或零扩展为64位。
1 execl
不是 C 标准的一部分,但它是POSIX标准的一部分,并且任何在其中提供execl
的系统首先可能至少与POSIX的子集兼容。可以假定所有C标准的clause 7.1.4都适用于POSIX指定的功能。
答案 3 :(得分:0)
AFAI理解 default argument promotion 应用于var-args functions,除了指针,指针仍然保留(zwol的示例还支持 1 ) 。因此,当0
传递给var-args函数时,例如 exec() family ,它被识别为未经修饰的整数0
代替空指针。总而言之,execl(prog, arg, 0);
可以在空指针的内部表示和整数0
的内部表示相同但不是必须的系统上工作。
execl(prog, arg, NULL);
也可能会无意间受到以下任一条件的约束
NULL
定义为整数0
,则上述内容
说明。NULL
定义为null-pointer-constant,则为
(void*)0
,尽管在可变函数中编译器无法转换
(void*)0
指向适当类型的空指针,即使C
标准说char*
和void*
必须具有相同的内部
表示形式。 1 例如,execl
不会从0
为32位int
的任何ABI上的char *
接收空指针是64位,并且int
数量作为变量参数列表的一部分传递时不会对64位进行正负号或零扩展。