当前位置: 首页 > 知识库问答 >
问题:

严格别名是否会阻止您通过不同的类型写入char数组?

虞修平
2023-03-14

我的理解是,C中的严格别名是在basic中定义的。11级:

(11) 如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:

    < li>(11.1)对象的动态类型, < li>(11.2)对象动态类型的cv限定版本, < li>(11.3)与对象的动态类型类似的类型(在conv.qual中定义), < li>(11.4)对应于对象动态类型的有符号或无符号类型, < li>(11.5)与对象的动态类型的cv限定版本相对应的有符号或无符号类型, < li>(11.6)聚合或联合类型,在其元素或非静态数据成员(递归地包括子聚合或包含联合的元素或非静态数据成员)中包含上述类型之一, < li>(11.7)一种类型,它是对象的动态类型的基类类型(可能是cv限定的) < Li >(11.8)< code > char 、< code>unsigned char或< code>std :: byte类型。

根据我的阅读,根据11.8,这始终是合法的,因为程序通过无符号字符类型的glvalue访问x的存储值:

int x = 0xdeadbeef;
auto y = reinterpret_cast<unsigned char*>(&x);
std::cout << y[1];

我很好奇使用别名到无符号字符数组的指针:

alignas(int) unsigned char[4] x;
auto y = reinterpret_cast<int*>(x);
*y = 0xdeadbeef;

这是违反严格走样的吗?我的解读是它不是,然而我刚刚在另一个帖子上被告知它是。仅针对basic.lval,在我看来没有UB,因为程序不试图访问存储的值:它存储一个新值而不读取它,只要后续读取使用< code>x,就不会发生冲突。

共有2个答案

谢裕
2023-03-14

有许多调用 UB 的构造,但哪些高质量的编译器无论如何都应该正确处理。使用字符类型存储来保存其他类型就是其中之一。否则,要求 char[] 的构造函数生成指向对齐存储的指针是没有意义的。

C89的作者认为没有必要全面描述适用于任何特定目的的高质量实现需要可预测的行为的每一种情况。基本原理认识到,实现可能是符合要求的,但质量太低,基本上是无用的,并认为没有必要禁止实现以损害其有用性的方式运行。每一个后续的C或C标准都继承了C89的部分,这些部分从未打算完全完成,而且没有一个完全完成了这些部分。

该标准不区分

>

  • 调用UB的动作,但即使是最迟钝的编译器编写者也会认识到它们的行为应该是可预测的(例如,结构 foo {int x;} s; s.x=1;);

    适合各种目的的质量编译器应可预测地处理哪些操作,但哪些低质量编译器或仅适用于其他目的的高质量编译器可能不处理;

    某些编译器可能会以可预测的方式处理的操作,但通常不应该期望任何其他编译器进行此类处理——即使是那些针对相同目的(平台、应用程序字段等)的编译器。

    用特定的对齐方式声明一个< code>char[],使用一次命名数组来捕获它的地址(并且永远不要再使用命名数组),并将其用作可以保存其他类型的原始存储,应该属于上面的第一类(特别是因为——如上所述——否则对齐保证就没有多大用处了)。编译器可能不会识别任何指针与原始数组的关系,因此可能不会意识到对这种指针的操作可能会与< code>char[](*)交互,但是如果该数组不再作为< code>char[]使用,编译器就没有理由在意。

    (*)例如,鉴于

    char foo[10];
    
    int test(int *p)
    {
      if (foo[1])
        *p = 1;
      return foo[1];
    }
    

    实现可能会缓存并重用从foo[1]读取的第一个值,而不认识到写入*p可能会改变底层存储。但是,如果命名的左值foo在第一次获取其地址后从未使用过,编译器对缓存左值foo的读取是否安全的假设就无关紧要了,因为不会有任何假设。

  • 卢深
    2023-03-14

    关于“访问”的定义:

    http://eel.is/c草案/defns.access

    3.1访问[defns.access]
    ⟨执行时间动作⟩ 读取或修改对象的值

    换句话说,存储价值也是“访问”。它仍然是UB。

     类似资料:
    • 我正在阅读关于reinterpret_cast的笔记,它是混淆现象(http://en.cppreference.com/w/cpp/language/reinterpret_cast)。 我写了代码: 我认为这些规则在这里不适用: T2是对象的(可能是cv限定的)动态类型 T2和T1都是指向相同类型T3的指针(可能是多级的,可能在每个级别都是cv限定的)(因为C11) T2是一个聚合类型或并集类

    • 我的理解是,由于所谓的“严格混淆现象规则”,以下代码在C中具有未定义的行为。 特别是,C编译器可能会完全忽略赋值,因为它可以假定

    • 问题内容: 我正在尝试为Swift中的简单命令行批处理脚本同步读取URL的内容。为了简单起见,我正在使用cURL- 我知道我可以使用NSURLSession。我也在OSX上使用Swift的开源版本来构建它。 问题在于,如果已将stdout重定向到管道,则在某些URL上,NSTask永远不会终止。 但是,如果删除管道或更改URL,则任务成功。 直接使用Terminal中的curl直接运行任何示例都可

    • 基本上,当启用严格别名时,此代码是否合法? 这里,我们通过另一种类型的指针(指向<code>void*</code>的指针)访问一种类型(<code<int*</code>)的对象,因此我认为这确实是一种严格的别名冲突。 但是,试图突出未定义行为的样本使我怀疑(即使它不能证明它是合法的)。 首先,如果我们将<code>int*</code>和<code>char*</code>别名,我们可以根据优

    • 当我从json中获取数据时,我首先创建一个类,并定义json中的所有变量,以获得严格的数据类型,就像我的json文件一样 所以在角我使类像这样 我不知道如何为下面的json数据创建类 我试图在类中添加数组,但没有得到在angularjs中处理它的最佳方法。