为什么此声明取消引用编译器特有的类型为准的指针警告? [英] Why is this claimed dereferencing type-punned pointer warning compiler-specific?

查看:93
本文介绍了为什么此声明取消引用编译器特有的类型为准的指针警告?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读各种 帖子 堆栈溢出 RE:解引用类型-punned指针错误.我的理解是,该错误实质上是编译器警告,指出通过不同类型的指针访问对象的危险(尽管对于char*似乎有例外),这是一种可以理解且合理的警告.

I've read various posts on Stack Overflow RE: the derefercing type-punned pointer error. My understanding is that the error is essentially the compiler warning of the danger of accessing an object through a pointer of a different type (though an exception appears to be made for char*), which is an understandable and reasonable warning.

我的问题特定于以下代码:为什么强制将指针的地址强制转换为void**的条件(通过-Werror导致错误)?

My question is specific to the code below: why does casting the address of a pointer to a void** qualify for this warning (promoted to error via -Werror)?

此外,此代码针对多个目标体系结构进行了编译,只有其中一种会生成警告/错误-这是否暗示它在合法上是特定于编译器版本的缺陷?

// main.c
#include <stdlib.h>

typedef struct Foo
{
  int i;
} Foo;

void freeFunc( void** obj )
{
  if ( obj && * obj )
  {
    free( *obj );
    *obj = NULL;
  }
}

int main( int argc, char* argv[] )
{
  Foo* f = calloc( 1, sizeof( Foo ) );
  freeFunc( (void**)(&f) );

  return 0;
}

如果我的理解是正确的,void**仍然只是一个指针,则应该安全地进行转换.

If my understanding, stated above, is correct, a void**, being still just a pointer, this should be safe casting.

是否有一种解决方法不使用左值来解决此特定于编译器的警告/错误? IE.我知道这一点以及为什么这可以解决问题,但是我想避免使用这种方法,因为我想利用freeFunc() NULL 使用预期的out-arg:

Is there a workaround not using lvalues that would pacify this compiler-specific warning/error? I.e. I understand that and why this will resolve the issue, but I would like to avoid this approach because I want to take advantage of freeFunc() NULLing an intended out-arg:

void* tmp = f;
freeFunc( &tmp );
f = NULL;

问题编译器(之一):

user@8d63f499ed92:/build$ /usr/local/crosstool/x86-fc3/bin/i686-fc3-linux-gnu-gcc --version && /usr/local/crosstool/x86-fc3/bin/i686-fc3-linux-gnu-gcc -Wall -O2 -Werror ./main.c
i686-fc3-linux-gnu-gcc (GCC) 3.4.5
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

./main.c: In function `main':
./main.c:21: warning: dereferencing type-punned pointer will break strict-aliasing rules

user@8d63f499ed92:/build$

不兼容的编译器(众多)之一:

Not-complaining compiler (one of many):

user@8d63f499ed92:/build$ /usr/local/crosstool/x86-rh73/bin/i686-rh73-linux-gnu-gcc --version && /usr/local/crosstool/x86-rh73/bin/i686-rh73-linux-gnu-gcc -Wall -O2 -Werror ./main.c
i686-rh73-linux-gnu-gcc (GCC) 3.2.3
Copyright (C) 2002 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

user@8d63f499ed92:/build$


更新:我进一步发现,警告似乎是专门用-O2编译时生成的(仍然仅使用注明的问题编译器")


Update: I've further discovered the warning appears to be generated specifically when compiled with -O2 (still with the noted "problem compiler" only)

推荐答案

类型为void**的值是指向类型为void*的对象的指针. Foo*类型的对象不是void*类型的对象.

A value of type void** is a pointer to an object of type void*. An object of type Foo* is not an object of type void*.

类型Foo*void*之间存在隐式转换.此转换可能会更改值的表示形式.同样,您可以编写int n = 3; double x = n;,这具有将x设置为值3.0的明确定义的行为,但是double *p = (double*)&n;具有未定义的行为(实际上,不会将p设置为指向以下内容的指针" 3.0"(在任何常见体系结构上).

There is an implicit conversion between values of type Foo* and void*. This conversion may change the representation of the value. Similarly, you can write int n = 3; double x = n; and this has the well-defined behavior of setting x to the value 3.0, but double *p = (double*)&n; has undefined behavior (and in practice will not set p to a "pointer to 3.0" on any common architecture).

如今,不同类型的对象指针具有不同表示形式的体系结构已很少见,但C标准允许它们.在(旧的)旧机器中,有 word指针是内存中一个单词的地址,而 byte指针是一个单词的地址以及该单词的字节偏移量;在这样的体系结构上,Foo*是字指针,而void*是字节指针.有(稀有)带有 fat指针的机器,它们不仅包含有关对象地址的信息,而且还包含有关对象的类型,大小和访问控制列表的信息.指向确定类型的指针可能与void*具有不同的表示形式,void*在运行时需要其他类型信息.

Architectures where different types of pointers to objects have different representations are rare nowadays, but they are permitted by the C standard. There are (rare) old machines with word pointers which are addresses of a word in memory and byte pointers which are addresses of a word together with a byte offset in this word; Foo* would be a word pointer and void* would be a byte pointer on such architectures. There are (rare) machines with fat pointers which contain information not only about the address of the object, but also about its type, its size and its access control lists; a pointer to a definite type might have a different representation from a void* which needs additional type information at runtime.

这种机器很少见,但C标准允许.一些C编译器利用此权限将类型标记的指针视为与众不同的指针,以优化代码.指针混叠的风险是限制编译器优化代码能力的主要限制,因此编译器倾向于利用此类权限.

Such machines are rare, but permitted by the C standard. And some C compilers take advantage of the permission to treat type-punned pointers as distinct to optimize code. The risk of pointers aliasing is a major limitation to a compiler's ability to optimize code, so compilers tend to take advantage of such permissions.

编译器可以自由地告诉您您做错了什么,或者悄悄地执行了您不需要的操作,或者悄悄地执行了您想要的操作.未定义的行为允许其中任何一种.

A compiler is free to tell you that you're doing something wrong, or to quietly do what you didn't want, or to quietly do what you wanted. Undefined behavior allows any of these.

您可以将freefunc设为宏:

#define FREE_SINGLE_REFERENCE(p) (free(p), (p) = NULL)

这带有宏的通常限制:缺乏类型安全性,p被评估两次.请注意,如果p是指向已释放对象的单个指针,则这样做只给您提供安全的地方,不会留下悬空的指针.

This comes with the usual limitations of macros: lack of type safety, p is evaluated twice. Note that this only gives you the safety of not leaving dangling pointers around if p was the single pointer to the freed object.

这篇关于为什么此声明取消引用编译器特有的类型为准的指针警告?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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