我可以用分配的内存做我想做的事吗

Can I do what I want with allocated memory

我对已分配内存的操作是否有限制?(标准方式)

例如

#include <stdio.h>
#include <stdlib.h>

struct str{
    long long a;
    long b;
};

int main(void) 
{
    long *x = calloc(4,sizeof(long));
    x[0] = 2;
    x[3] = 7;
//is anything beyond here legal( if you would exclude possible illegal operations)
    long long *y = x; 
    printf("%lld\n",y[0]); 
    y[0] = 2;
    memset (x,0,16);
    struct str *bar = x;
    bar->b =  4;
    printf("%lld\n",bar->a); 
    return 0;
}

总结一下:

Can I recast the pointer to other datatypes, as long as the size fits?

您可以将1 重铸为最大与您分配的内存一样大的任何数据类型。但是,您必须写入一个值才能根据 6.5p6

更改 allcoated 对象的有效类型

Can I read before I write, then?
If not can I read after I wrote?

没有。除非另有说明(calloc否则为2,内存中的值是不确定的。它可能包含陷阱值。为了将值重新解释为另一种类型而进行的转换是 UB,并且违反了严格的别名 (6.5p7)

Can I use it with a struct smaller than the allocated memory?

是的,但那是一种浪费。


1 您需要先转换为 void*。否则你会从编译器那里得到关于不兼容指针类型的正当投诉。
2 即使如此,某些类型可能会陷入完全为 0 的位模式,所以这取决于。

y[0] 读取违反了严格的别名规则。您使用 long long 类型的左值来读取有效类型 long 的对象。

假设您省略了该行;下一个麻烦的部分是memset(x,0,16);This answer 认为 memset 不更新有效类型。标准不明确

假设memset保持有效类型不变;下一期是阅读 bar->a.

C 标准对此也不清楚。有人说 bar->a 意味着 (*bar).a,这是一个严格的别名违规,因为我们没有先将 bar 对象写入该位置。

其他人(包括我)说没问题:唯一用于访问的左值是bar->a;这是一个 long long 类型的左值,它访问一个有效类型 long long 的对象(由 y[0] = 2; 编写的对象)。

有一个 C2X 工作组正在致力于改进严格别名的规范以澄清这些问题。

大多数编译器都提供一种模式,在这种模式下,无论涉及何种数据类型,指针的读写都将按执行顺序作用于底层存储。该标准不要求编译器提供这种模式,但据我所知,所有高质量的编译器都会这样做。

根据他们公布的基本原理,该标准的作者向语言添加了别名限制,其目的是避免编译器在给定代码时做出悲观的别名假设:

float f;
float test(int *p)
{
  f=1.0f;
  *p = 2;
  return f;
}

请注意,在基本原理中给出的示例中[非常像上面],即使通过指针p修改f使用的存储是合法的,一个合理的人看起来在代码中没有理由认为这样的事情可能会发生。另一方面,许多编译器编写者认识到,如果给定如下内容:

float f;
float test(float *p)
{
  f=1.0f;
  *(int*)p = 2;
  return f;
}

人们必须故意愚钝地认为代码不太可能修改 float 使用的存储,因此没有理由认为高质量的编译器不应该考虑写入 *(int*)p 作为对 float.

的潜在写入

不幸的是,在这期间,编译器编写者在 type-based 别名 "optimizations" 方面变得越来越激进,有时其方式明显且不可否认地超出了标准所允许的范围。除非程序永远不需要在不同时间访问不同类型的任何存储,否则我建议在支持它的编译器上使用 -fno-strict-aliasing 选项。否则,可能会有符合标准并在今天运行的代码,但在编译器的未来版本中会失败,该编译器的 "optimizations".

变得更加激进。

PS--禁用type-based别名可能会在某些情况下影响代码的性能,但正确使用restrict限定的变量和参数应该避免悲观别名的代价假设。稍加小心,使用这些限定符将实现与激进别名相同的优化,但更安全。