C 这是否违反了严格的别名规则?
考虑以下C代码:C 这是否违反了严格的别名规则?,c,gcc,c99,type-punning,C,Gcc,C99,Type Punning,考虑以下C代码: extern void CheckIfPtrInHeap( void* p ); void TakePtr( void** p, size_t n ) { for( size_t i = 0 ; i < n ; ++i ) CheckIfPtrInHeap( p[ i ] ); } typedef size_t val_t[ 6 ]; extern void* stack_top; void Func() { val_t val; TakePt
extern void CheckIfPtrInHeap( void* p );
void TakePtr( void** p, size_t n )
{
for( size_t i = 0 ; i < n ; ++i )
CheckIfPtrInHeap( p[ i ] );
}
typedef size_t val_t[ 6 ];
extern void* stack_top;
void Func()
{
val_t val;
TakePtr( (void**) &val, ( (size_t) stack_top - (size_t) &val ) / sizof( size_t ) );
}
或
甚至
void** ptr = (void**) (void*) &val;
或者(告诉编译器‘嘿,我知道我在做什么’,至少如果我正确理解了restrict
)
或
严格的别名规则只不过是这样一个事实:通过另一种类型的左值访问一种类型的对象是非法的(诸如
void*
和char*
以及其他明显的情况除外)。不多也不少
仅仅有两个、三个或10000个指针同时指向同一个对象,不管它们是否都是同一类型,与严格的别名规则无关。您需要取消对其中一个的引用,才能发生冲突
这也与restrict
关键字完全无关,该关键字与类型无关,只是通知编译器指针是唯一的
无论使用第二个参数玩什么游戏,TakePtr
都是违反规则的,这仅仅是因为它访问一个size\t
数组,就好像它是一个void*
数组一样
联合不会有帮助,因为分配给联合的一个字段,然后访问另一类型的字段也是不合法的。在这种情况下,也会发生与取消引用错误指针时完全相同的坏事
通过不同类型的左值访问值的唯一半正确方法是通过
memcpy
。并不是说结果是自动定义好的。表示类型A的有效值的位模式可能不表示类型B的有效值。但如果不是这种情况,则可以将其memcpy,并保证提供正确的值。这取决于TakePtr()
的具体操作。为什么要将指针投射到大小\u t
?正确的强制转换为uintptr\t
。TakePtr()
对它的参数有什么作用?@FilipeGonçalves在实际的代码TakePtr
中标记并清除垃圾收集,因此它以指向堆栈的指针为例,长度-这只是要复制的代码,实际的函数采用类似(size_t)stack_top-(size_t)的值&vals
它是否曾经取消引用指针?@FilipeGonçalves请参见编辑,添加了一个更完整的TakePtrWell实现,可以稍微清除一些内容-那么这是否意味着编译器发出有关强制转换的警告非常正确?一个合适的解决方案是让TakePtr使用大小作为参数?不,请不要size\t**
size\u t**
指向类型为size\u t*
的对象。您没有可以指向的size.*
类型的对象。您有一个大小\u t
数组。使用size\u t*
访问它。我实际上拥有的是一块内存,我知道它包含指针,但无法知道它们指向什么,因为它们是使用某种形式的malloc获得的。问题是,这段内存碰巧以size\u t数组的形式传递给我(实际上可能不是size\u t,而是一个整数,它被引导为具有与指针相同的大小),我无法更改它..因此,您以size\u t
数组的形式传递给您。在上帝的绿色地球上,是什么让你试图通过size\t**
类型的指针访问它?顺便说一句,你有一个指针数组被双关到一个整数数组,这意味着已经违反了别名规则,现在担心有点晚了。
void* p1 = (void*) &val;
void** ptr = &p1;
void** ptr = (void**) (void*) &val;
void* restrict p1 = (void*) &val;
void** ptr = &p1;
val_t* p1 = &val;
void** p2 = NULL;
memcpy( p2, p1, sizeof( p1 ) );