Tag: 严格别名

严格的别名和内存位置

严格别名会阻止我们使用不兼容的类型访问相同的内存位置。 int* i = malloc( sizeof( int ) ) ; //assuming sizeof( int ) >= sizeof( float ) *i = 123 ; float* f = ( float* )i ; *f = 3.14f ; 根据C标准,这将是非法的,因为编译器“知道” float左值不能访问int 。 如果我使用该指针指向正确的内存,如下所示: int* i = malloc( sizeof( int ) + sizeof( float ) + MAX_PAD ) ; *i = 456 […]

将缓冲区解释为结构的正确,可移植的方法

我的问题的背景是网络编程。 假设我想通过网络在两个程序之间发送消息。 为简单起见,假设消息看起来像这样,字节顺序不是问题。 我想找到一种正确,可移植且有效的方法来将这些消息定义为C结构。 我知道有四种方法:显式铸造,铸造联合,复制和编组。 struct message { uint16_t logical_id; uint16_t command; }; 明确的铸造: void send_message(struct message *msg) { uint8_t *bytes = (uint8_t *) msg; /* call to write/send/sendto here */ } void receive_message(uint8_t *bytes, size_t len) { assert(len >= sizeof(struct message); struct message *msg = (struct message*) bytes; /* And now use the message […]

所有指针都是从指向结构类型的指针派生的吗?

问题 是否所有指针都是从指向结构类型的指针派生的问题都不容易回答。 我认为这是一个重要问题,主要有以下两个原因。 A.缺少指向“任何”不完整或对象类型的指针,对方便的函数接口施加了限制,例如: int allocate(ANY_TYPE **p, size_t s); int main(void) { int *p; int r = allocate(&p, sizeof *p); } [ 完整代码示例 ] 指向“任何”不完整或对象类型的现有指针明确描述为: C99 §6.3.2.3 p1 : 指向void的指针可以转换为指向任何不完整或对象类型的指针。 […] 从指向“任何”不完整或对象类型的现有指针派生的指针,指向void的指针,严格地是指向void的指针,并且不需要使用从指向“任何”不完整的指针派生的指针进行转换或对象类型。 B.程序员根据他们对特定实现的经验,使用基于不需要的假设的约定,有意或无意地与指针的泛化相关,这种情况并不少见。 假设,例如可转换,可表示为整数或共享公共属性:对象大小,表示或对齐。 标准的话 根据C99 §6.2.5 p27 / C11 §6.2.5 p28 : […]所有指向结构类型的指针应具有相同的表示和对齐要求。 […] 其次是C99 TC3 Footnote 39 / C11 Footnote 48 : 相同的表示和对齐要求意味着可互换性作为函数的参数,函数的返回值和联合的成员。 […]

如何在符合C标准的情况下一般地转换指针的地址

通常使用隐式函数返回void *转换来指定带有分配的指针,就像malloc()一样: void *malloc(size_t size); int *pi = malloc(sizeof *pi); 我希望在传递目标指针的地址时执行相同的赋值,而不从函数内部(不在其体内,也不在参数中)显式地转换它的类型。 以下代码似乎实现了这一点。 我想知道代码是否完全符合(任何)C标准。 如果它不符合,我想知道是否有可能在符合(任何)C标准的情况下达到我的要求。 。 #include #include int allocate_memory(void *p, size_t s) { void *pv; if ( ( pv = malloc(s) ) == NULL ) { fprintf(stderr, “Error: malloc();”); return -1; } printf(“pv: %p;\n”, pv); *((void **) p) = pv; return 0; } int main(void) […]

C别名规则和memcpy

在回答另一个问题时,我想到了以下示例: void *p; unsigned x = 17; assert(sizeof(void*) >= sizeof(unsigned)); *(unsigned*)&p = 17; // (1) memcpy(&p, &x, sizeof(x)); // (2) 第1行打破了别名规则。 然而,第2行是好的。 别名规则。 问题是:为什么? 编译器是否具有关于memcpy等函数的特殊内置知识,还是有一些其他规则可以使memcpy正常运行? 有没有办法在标准C中实现类似memcpy的函数而不破坏别名规则?

GCC:严格别名警告的准确性

我正在尝试检查我的一些代码是否存在严格的别名违规,但在尝试理解严格的别名规则时,我似乎错过了一些东西。 想象一下以下代码: #include int main( void ) { unsigned long l; l = 0; *( ( unsigned short * )&l ) = 1; printf( “%lu\n”, l ); return 0; } 经典和基本的例子。 使用GCC 4.9( -Wall -fstrict-aliasing -Wstrict-aliasing -O3 ),它实际报告错误: dereferencing type-punned pointer will break strict-aliasing rules 但以下编译很好: #include int main( void ) { unsigned long l; […]

如何投射sockaddr_storage并避免违反严格别名规则

我正在使用Beej的网络指南,并遇到了一个别名问题。 他提出了一个函数来返回特定结构的IPv4或IPv6地址: 1 void *get_in_addr( struct sockaddr *sa ) 2 { 3 if (sa->sa_family == AF_INET) 4 return &(((struct sockaddr_in*)sa)->sin_addr); 5 else 6 return &(((struct sockaddr_in6*)sa)->sin6_addr); 7 } 这会导致GCC为第3行的sa吐出严格别名错误。据我所知,这是因为我这样调用这个函数: struct sockaddr_storage their_addr; … inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), connection_name, sizeof connection_name); 我猜测别名与their_addr变量的类型为sockaddr_storage并且另一个不同类型的指针指向同一内存的事实有关。 是解决这个问题的最佳方法sockaddr_storage , sockaddr_in和sockaddr_in6成为一个联盟? 看起来这应该是网络中的磨损领域,我只是找不到任何有最佳实践的好例子。 此外,如果有人能够准确解释别名问题发生的位置,我会非常感激。

使用字符类型上次写入时,使用非字符类型读取对象时的未定义行为

假设unsigned int没有陷阱表示,请执行下面标记为(A)和(B)的语句中的任何一个或两个引发未定义的行为,为什么或为什么不行为,以及(特别是如果您认为其中一个定义明确但另一个不定义)是的,您认为标准中存在缺陷吗? 我主要对当前版本的C标准(即C2011)感兴趣,但如果在标准的旧版本或C ++中有所不同,我也想知道这一点。 ( _Alignas在这个程序中被用来消除因对齐不充分而导致的任何UB问题。我在解释中讨论的规则虽然没有说明对齐。) #include #include int main(void) { unsigned int v1, v2; unsigned char _Alignas(unsigned int) b1[sizeof(unsigned int)]; unsigned char *b2 = malloc(sizeof(unsigned int)); if (!b2) return 1; memset(b1, 0x55, sizeof(unsigned int)); memset(b2, 0x55, sizeof(unsigned int)); v1 = *(unsigned int *)b1; /* (A) */ v2 = *(unsigned int *)b2; /* (B) */ […]

C内存分配器和严格别名

即使在阅读了很多关于严格别名规则之后,我仍然感到困惑。 据我所知,不可能实现遵循这些规则的合理的内存分配器,因为malloc永远不能重用释放的内存,因为内存可以用于在每次分配时存储不同的类型。 显然这不可能是正确的。 我错过了什么? 如何实现遵循严格别名的分配器(或内存池)? 谢谢。 编辑:让我用一个愚蠢的简单例子来澄清我的问题: // s == 0 frees the pool void *my_custom_allocator(size_t s) { static void *pool = malloc(1000); static int in_use = FALSE; if( in_use || s > 1000 ) return NULL; if( s == 0 ) { in_use = FALSE; return NULL; } in_use = TRUE; return pool; } […]

从指向const的指针中删除const是否遵循C中的严格别名,并引用相同的对象?

C中的以下代码是否定义了行为? int main() { const int i = 0; return *(int*)(&i); } 我问因为6.5 / 7列出了“与对象的有效类型兼容的类型的合格版本”作为有效别名。 但是对象的有效类型是const int ,我不认为int是const int的限定版本(尽管反之亦然)。 int和const int都不兼容(6.7.3 / 10)。 此外,6.3.2.3 / 2表示您可以通过添加限定符来转换指针类型,并且结果指针是相等的。 6.3.2.3/7表示你可以转换任何两种指针类型(因此允许使用cast (int*)(&i)本身)。 但是并没有说结果指针指的是同一个对象,甚至它是相等的。 它说的是它可以转换回原始类型(在本例中为const int* )。 也就是说,即使别名是合法的,我也不清楚标准是否保证我的指针转换确实会导致指向i的指针。 那么,标准是否实际上定义了我的代码的行为,如果是这样,这个定义在哪里? 我知道代码在实践中有效。 我想到了一个假设的(和奇怪的)实现,它不起作用。 我可以问这个实现是否符合标准(如果没有,它违反了哪个部分),但如果还有其他方面,我想象的实现无法符合,我不想浑水。 如果有人认为这将有助于他们回答问题,我将描述实施。