Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 使用void指针干预时是否会出现严格的别名_C_Language Lawyer_Strict Aliasing - Fatal编程技术网

C 使用void指针干预时是否会出现严格的别名

C 使用void指针干预时是否会出现严格的别名,c,language-lawyer,strict-aliasing,C,Language Lawyer,Strict Aliasing,以下示例是否违反了严格的别名规则 在a.c.文件中 可以说存在冲突,因为我将struct some_struct指针发送到func,该指针将其强制转换为uint32_t指针,而不是访问该值 但是,由于func使用void指针,并且func与调用方位于不同的编译单元中,因此编译器无法“看到”这样的冲突 那么下面的例子呢,据我所知,没有违反,并且完全符合别名: extern func_takes_word(uint32_t word); void func(void *obj, size_t s

以下示例是否违反了严格的别名规则

在a.c.文件中

可以说存在冲突,因为我将
struct some_struct
指针发送到
func
,该指针将其强制转换为
uint32_t
指针,而不是访问该值

但是,由于
func
使用
void
指针,并且
func
与调用方位于不同的编译单元中,因此编译器无法“看到”这样的冲突


那么下面的例子呢,据我所知,没有违反,并且完全符合别名:

extern func_takes_word(uint32_t word);

void func(void *obj, size_t size_in_words)
{
    uint32_t word;

    for (int i = 0; i < size_in_words; i++)
    {
        // instead of calling memcpy, (or using union type punning) 
        // for learning purpose
        *(char *)&word = *((char *)obj+ (sizeof(uint32_t) * i)); 
        *(((char *)&word) + 1) = *((char *)obj+ (sizeof(uint32_t) * i)+1);
        *(((char *)&word) + 2) = *((char *)obj+ (sizeof(uint32_t) * i)+2);
        *(((char *)&word) + 3) = *((char *)obj+ (sizeof(uint32_t) * i)+3);
        func_takes_word(word);
    }
}
extern func___字(uint32_字);
void func(void*obj,大小(大写)
{
uint32_t单词;
for(int i=0;i

我说的对吗?

这是一个重复的问题,但我还是要写一个答案,因为我找不到一个更早的答案来讨论通过
void*
转换和单独编译的具体问题

首先,让我们设想一个更简单的代码版本:

#include <stddef.h>
#include <stdint.h>

extern void func_takes_word(uint32_t word);

struct __attribute__((packed, aligned(_Alignof(uint32_t)))) some_struct
{
    uint32_t num_0;
    uint32_t num_1;
    uint32_t num_2;
};

void some_func(void)
{
    struct some_struct stc = {0, 1, 59};

    for (size_t i = 0; i < sizeof(struct some_struct) / sizeof(uint32_t); i++)
        func_takes_word(*(((uint32_t *)&stc) + i));
}
#包括
#包括
外部无效功能字(uint32字);
结构属性(压缩、对齐)一些结构
{
uint32数量0;
uint32数量1;
uint32_t num_2;
};
作废某些功能(作废)
{
struct someu struct stc={0,1,59};
对于(size_t i=0;i
(GCC
\uuuuuuuu属性(打包、对齐(…))
注释的存在只是为了排除由于填充或未对齐而出现任何问题的可能性。如果您将其取出,我下面所说的一切仍然是正确的。)

根据C2011最直接的解释,该代码确实违反了“严格别名”规则(N1570:和)。类型
struct some\u struct
与类型
uint32\u t
不兼容。因此,使用声明类型为
struct some_struct
的对象的地址,将结果指针强制转换为类型
uint32\u t*
,添加非零偏移量,并取消对强制转换指针的引用,具有未定义的行为。其实就是这么简单。(编辑:如果指针没有偏移,则取消引用具有定义良好的行为,因为我完全忘记了隐藏在其中的特殊情况规则。感谢dbush指出这一点。)

许多人愤怒地抵制对标准的这种解释,并坚持认为委员会一定有别的意思,因为有数百万行(如果不是数十亿行的话)的“遗留”C代码正是这样做的,并且期望它能够工作。更不用说,在这种解释下,
offsetof
如何做有用的事情还不清楚。但是文本确实这么说了,没有其他合理的解释,标准相关章节的措辞自1989年ANSI C开始以来基本上没有变化。我认为我们必须假设委员会对修改文本缺乏兴趣,三十年来,尽管有几次正式要求澄清或更正,但这意味着它说出了他们希望它说的话


现在,关于通过
void*
强制转换和/或拆分操作,以便执行取消引用的代码看不到对象的原始“有效类型”:这些都没有区别。您的原始翻译单元对仍然有未定义的行为

通过
void*
进行强制转换没有任何区别,因为第6.5.p6节中的规则没有提到中间强制转换。他们只讨论内存中实际对象的“有效类型”,以及用于访问对象的左值表达式的类型。因此,在获取对象地址的时间和取消引用指针的时间之间,指针可能有什么类型并不重要(只要没有任何强制转换破坏信息,从对象类型到
void*
再向后的强制转换保证不会发生这种情况)

将操作拆分,使对象的原始“有效类型”对执行解引用的代码不可见(静态),这没有什么区别,因为C标准在决定是否允许访问之前,对编译器可以执行的分析的复杂程度没有任何限制。特别是,委员会明确认可了一种实现,即用“有效类型”标记内存的每个字节,并对每个解引用执行运行时检查(不是在标准文本中,而是在DR响应中,我不记得这是多久以前的事了,WG14的网站也不太可搜索)。在翻译阶段8(“链接时间优化”)和阶段7期间,还允许实现任意积极的内联和过程间分析。将您的原始程序折叠到我的“更简单的版本”是当前一代全程序优化编译器的能力范围


正如在对该问题的评论中指出的那样,您可以依赖于对特定实现的优化器的复杂程度的了解,或者依赖于实现的公开扩展(例如,
\uuuu属性\((noinline))
)来控制您是否获得了表现为
extern func_takes_word(uint32_t word);

void func(void *obj, size_t size_in_words)
{
    uint32_t word;

    for (int i = 0; i < size_in_words; i++)
    {
        // instead of calling memcpy, (or using union type punning) 
        // for learning purpose
        *(char *)&word = *((char *)obj+ (sizeof(uint32_t) * i)); 
        *(((char *)&word) + 1) = *((char *)obj+ (sizeof(uint32_t) * i)+1);
        *(((char *)&word) + 2) = *((char *)obj+ (sizeof(uint32_t) * i)+2);
        *(((char *)&word) + 3) = *((char *)obj+ (sizeof(uint32_t) * i)+3);
        func_takes_word(word);
    }
}
#include <stddef.h>
#include <stdint.h>

extern void func_takes_word(uint32_t word);

struct __attribute__((packed, aligned(_Alignof(uint32_t)))) some_struct
{
    uint32_t num_0;
    uint32_t num_1;
    uint32_t num_2;
};

void some_func(void)
{
    struct some_struct stc = {0, 1, 59};

    for (size_t i = 0; i < sizeof(struct some_struct) / sizeof(uint32_t); i++)
        func_takes_word(*(((uint32_t *)&stc) + i));
}
struct S { int x[1]; } s;
int test1(void)
{
  s.x[0] = 1;
}
struct S { int x[1], y[1], z[1]; } s;
int test2(int index)
{
  s.y[0] = 1;
  s.x[index] = 2;
  return s.y[0];
}
struct S { int x[1], y[1], z[1]; } s;
int test3(int index)
{
  s.y[0] = 1;
  ((int*)&s)[index] = 2;
  return s.y[0];
}