是否允许编译器优化realloc?

是否允许编译器优化realloc?,c,gcc,clang,language-lawyer,compiler-optimization,C,Gcc,Clang,Language Lawyer,Compiler Optimization,我遇到了这样一种情况,对realloc进行不必要的调用将非常有用。然而,似乎Clang和GCC都没有做这样的事情((godbolt.org))——尽管我看到通过多次调用malloc进行优化 例如: void *myfunc() { void *data; data = malloc(100); data = realloc(data, 200); return data; } 我希望它可以优化为如下内容: void *myfunc() { return

我遇到了这样一种情况,对
realloc
进行不必要的调用将非常有用。然而,似乎Clang和GCC都没有做这样的事情((godbolt.org))——尽管我看到通过多次调用
malloc
进行优化

例如:

void *myfunc() {
    void *data;
    data = malloc(100);
    data = realloc(data, 200);
    return data;
}
我希望它可以优化为如下内容:

void *myfunc() {
    return malloc(200);
}

为什么Clang和GCC都没有优化它不允许他们这样做吗?

允许编译器优化对被认为是的函数的多个调用,即没有任何副作用的函数

所以问题是
realloc()
是否是纯函数

C11标准委员会草案N1570对
realloc
功能进行了说明:

7.22.3.5 realloc功能 ... 2.
realloc
函数解除分配ptr指向的旧对象,并返回一个指向新对象的指针,该对象的大小由size指定。新对象的内容应与解除分配前的旧对象的内容相同,以新旧尺寸中较小者为准。新对象中超出旧对象大小的任何字节都有不确定的值

返回 4.
realloc
函数返回指向新对象的指针(该指针可能与指向旧对象的指针具有相同的值),如果无法分配新对象,则返回空指针

请注意,编译器无法在编译时预测每次调用将返回的指针值

这意味着
realloc()
不能被视为纯函数,编译器无法优化对它的多次调用

难道不允许他们这样做吗

可能,但在这种情况下未进行优化可能是由于角点函数的差异


如果剩余150字节的可分配内存,
data=malloc(100);数据=realloc(数据,200)
返回
NULL
,消耗(泄漏)100字节,剩余50字节

data=malloc(200)
返回
NULL
,消耗0字节(无泄漏),剩余150字节

在这种狭窄的情况下,不同的功能可能会妨碍优化


是否允许编译器优化realloc

也许——我希望这是允许的。然而,增强编译器以确定何时可以进行编译可能并不值得

成功
malloc(n)。。。realloc(p,2*n)
不同于
malloc(2*n)时,…
可能设置了一些内存


确保
,即使是空代码,也不会设置任何内存,这可能超出了编译器的设计。

如果作者认为这样做是值得的,一个编译器捆绑了自己的自包含版本的malloc/calloc/free/realloc,就可以合法地执行指定的优化。链接到外部提供的函数的编译器如果记录到它不将调用这些函数的精确顺序视为可观察到的副作用,那么仍然可以执行这样的优化,但这样的处理可能有点脆弱

如果没有在malloc()和realloc()之间分配或解除分配存储,则在执行malloc()时,realloc()的大小是已知的,并且realloc()的大小大于malloc()的大小,那么将malloc()和realloc()操作合并为单个较大的分配可能是有意义的。但是,如果在此期间内存状态可能发生变化,那么这种优化可能会导致本应成功的操作失败。例如,给定序列:

void *p1 = malloc(2000000000);
void *p2 = malloc(2);
free(p1);
p2 = realloc(p2, 2000000000);
在释放p1之前,系统可能没有2000000000字节可用于p2。如果要将代码更改为:

void *p1 = malloc(2000000000);
void *p2 = malloc(2000000000);
free(p1);
这将导致p2的分配失败。由于该标准从未保证分配请求会成功,因此此类行为不会不合规。另一方面,以下也是“一致性”实施:

void *malloc(size_t size) { return 0; }
void *calloc(size_t size, size_t count) { return 0; }
void free(void *p) {  }
void *realloc(void *p, size_t size) { return 0; }
这样的实现可能被认为比大多数其他实现更“有效”,但人们必须相当迟钝才能认为它非常有用,除非在很少的情况下,在从未执行的代码路径上调用上述函数


我认为该标准将明确允许优化,至少在像原始问题中那样简单的情况下。即使在可能导致本来可以成功的操作失败的情况下,标准仍然允许这样做。很可能,许多编译器不执行优化的原因是,作者认为这些好处不足以证明为确定安全和有用的情况所需的努力是合理的。

但您没有检查第一个malloc()的返回值,然后在第二个realloc()中使用它。它也可以是空的

编译器如何将两个调用优化为一个调用,而不对第一个调用的返回值进行不必要的假设

还有另一种可能的情况。FreeBSD a
realloc()
基本上是malloc+memcpy+释放旧指针


假设只剩下230字节的可用内存。在该实现中,
ptr=malloc(100)
后跟
realloc(ptr,200)
将失败,但单个
malloc(200)
将成功。

我的理解是,这样的优化可能会被禁止(特别是在
malloc
成功但以下
realloc
失败的情况下,这种情况确实不太可能发生)

您可以假设
malloc
realloc
总是成功的(这与C11标准背道而驰;还可以查看我的