即使非常相似,当添加restrict 或__attribute__((malloc)) 时,相同的功能也会产生不同的优化。考虑这个例子(包括here作为__attribute__((malloc))的一个很好例子的参考):
#include <stdlib.h>
#include <stdio.h>
int a;
void* my_malloc(int size) __attribute__ ((__malloc__))
{
void* p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable
and discarded during compilation process\n");
return 0;
}
还有这个(没有属性的相同代码):
void* my_malloc(int size);
int a;
void* my_malloc(int size)
{
void* p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable
and discarded during compilation process\n");
return 0;
}
正如我们所料,带有 malloc 属性的代码比没有它的代码优化得更好(都带有-O3)。让我只包括差异:
没有属性:
[...]
call ___main
movl $4, (%esp)
call _malloc
testl %eax, %eax
je L9
movl $0, _a
xorl %eax, %eax
leave
.cfi_remember_state
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
L9:
.cfi_restore_state
movl $LC0, (%esp)
call _puts
movl $1, (%esp)
call _exit
.cfi_endproc
[...]
带属性:
[...]
call ___main
movl $4, (%esp)
call _my_malloc
movl $0, _a
xorl %eax, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
[...]
尽管如此,在这种情况下使用restrict 是毫无价值的,因为它不会优化生成的代码。如果我们修改原始代码以与restrict 一起使用:
void *restrict my_malloc(int size);
int a;
void *restrict my_malloc(int size)
{
void *restrict p = malloc(size);
if (!p) {
printf("my_malloc: out of memory!\n");
exit(1);
}
return p;
}
int main() {
int* x = &a;
int* p = (int*) my_malloc(sizeof(int));
*x = 0;
*p = 1;
if (*x) printf("This printf statement to be detected as unreachable and discarded \
during compilation process\n");
return 0;
}
asm代码和不带malloc属性生成的一模一样:
[...]
call ___main
movl $4, (%esp)
call _malloc
testl %eax, %eax
je L9
movl $0, _a
xorl %eax, %eax
leave
.cfi_remember_state
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
L9:
.cfi_restore_state
movl $LC0, (%esp)
call _puts
movl $1, (%esp)
call _exit
.cfi_endproc
[...]
所以对于类似 malloc/calloc 的函数,使用 __attribute__((__malloc__)) 看起来比 restrict 更有用。
__attribute__((__malloc__)) 和 restrict 有不同的行为来优化代码,即使它们的定义非常相似。这让我认为没有必要“合并”它们,因为编译器通过不同的方式实现了不同的优化。即使两者同时使用,生成的代码也不会比仅使用其中一个的最优化代码更优化(__attribute__((__malloc__)) 或restrict,视情况而定)。程序员的选择也是如此,根据他/她的代码知道哪个更适合。
为什么__attribute__((__malloc__)) 不是标准的?我不知道,但 IMO,从定义的角度来看,这些相似之处和从行为角度来看的差异无助于以清晰、差异化和通用的方式将两者整合到标准中。