该函数返回-1。
然而,在下面的代码中,(void*)
的含义是什么:
没有它可以工作吗?
test = shmat(shm_id, NULL, 0);
if (test == (void *)-1) {
/* handle shmat failure */
}
该函数返回-1。
然而,在下面的代码中,(void*)
的含义是什么:
没有它可以工作吗?
test = shmat(shm_id, NULL, 0);
if (test == (void *)-1) {
/* handle shmat failure */
}
shmat
的原型如下:
void *shmat(int shmid, const void *shmaddr, int shmflg);
即它返回一个指向 void
的指针,而不是一个整数。
发生错误时,它将返回转换为指向 void 类型的指针的整数 -1。来自 Linux manpages shmat(2):
成功时,
shmat()
返回已连接共享内存段的地址;出错时,返回(void *)-1
,并设置errno
以指示错误原因。
这就是您必须正确进行比较以检查错误返回的方式。 C11 标准在 6.5.9p5-6 中对运算符 ==
给出了以下说明:
5 否则,至少有一个操作数是指针。如果一个操作数是指针,另一个操作数是空指针常量,则将空指针常量转换为指针类型。
如果一个操作数是指向对象类型的指针,而另一个操作数是指向限定或未限定版本的 void 的指针,则前者被转换为后者的类型。
6 仅当两个指针都是空指针,都是指向同一对象(包括指向对象及其开头的子对象的指针或函数),都是指向同一数组对象的最后一个元素之后的指针,或者其中一个是指向一个数组对象之后的最后一个元素的指针,而另一个是指向立即跟随第一个数组对象的不同数组对象的开始的指针时,两个指针才相等。109)
也就是说,标准为确切的2种转换定义了行为:一个操作数是 void 指针,另一个操作数是其他类型的指针;或者一个操作数是指针,另一个操作数是空指针常量(即0或(void*)0等)。由于没有转换时 -1
不是空指针常量也不是指针,所以标准没有为这种情况指定任何行为,因此行为未定义"by the omission of any explicit definition"。
test == -1
是错误的,test == (void *)-1
是正确的(ish)。
事实证明,这仍然是一个灰色地带。 -1
是一个 int
,在我的计算机上是 32 位。将整数转换为指针由实现定义。 GCC 手册说:
从指针到整数的转换会丢弃最高有效位,如果指针表示大于整数类型,则进行符号扩展[2];如果指针表示小于整数类型,则进行符号扩展[2];否则位保持不变。
脚注2说
GCC 的未来版本可以使用零扩展或使用目标定义的 ptr_extend 模式。 不要依赖符号扩展。
因此,这意味着 (void *)-1</
(void*)0
成为一个有效的地址,可以在其中放置对象。这就是为什么mmap
失败时不返回NULL
的原因。我不知道这是否也适用于shmat
,但这可能是此行为的原因。 - Florian Weimertest == (void *) -1L
或者甚至是test == (void *) -1LL
吗? - chqrliemmap
和shmat
的手册只提到了(void*)-1
,而clang
恰好将其实现为(void*)(inptr_t)-1
,我猜所有符合Posix标准的编译器都必须这样做,以保持规范的一致性。这是一个有趣的边角案例。 - chqrlieerrno
而不是试图从返回的指针中解码出错误状态。请注意,errno
只在出现错误时设置,因此您必须在调用shmat
之前显式清除它。 - Rodney这是类型转换 (type casting)。
函数 shmat()
返回一个 void*
,因此需要使用类型转换将 -1 (int
) 转换为正确的类型 (void*
)。
实际上,它可能会起作用,但你会收到一些来自编译器的警告。
永远不要忽略警告,它们可能隐藏着错误。