为什么gcc需要__attribute__((__malloc__))
?通过声明malloc
(和类似的函数(作为返回restrict
的指针(void *restrict malloc(size_t)
(,同一信息难道不应该是可通信的吗?
这种方法似乎会更好,因为除了不需要非标准特性外,它还允许将其应用于通过指针(int malloc_by_arg(void *restrict*retval, size_t size);
("返回"的函数。
即使非常相似,当添加restrict
或__attribute__((malloc))
时,相同的函数也会产生不同的优化效果。考虑这个例子(包括在这里作为__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 processn");
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 processn");
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 processn");
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/caloc的函数,__attribute__((__malloc__))
的使用看起来比restrict
更有用。
__attribute__((__malloc__))
和restrict
具有不同的行为来优化代码,即使它们的定义非常相似。这让我认为"合并"它们是没有意义的,因为编译器通过不同的方式实现了不同的优化。即使两者同时使用,生成的代码也不会比只使用其中一个(__attribute__((__malloc__))
或restrict
,具体取决于情况(的优化程度最高的代码更优化。程序员根据自己的代码选择哪一个更合适也是如此。
为什么__attribute__((__malloc__))
不是标准?我不知道,但IMO,从定义的角度来看,这些相似之处和从行为的角度来看的差异无助于以清晰、差异化和通用的方式将两者整合到标准中。
在我的测试中,即使基于没有属性的函数,它也可以通过以下命令优化代码:~/gcc1.1.0-install/bin/arch64-linux-gnu-gcc test2.c-O3-S
main:
.LFB23:
.cfi_startproc
stp x29, x30, [sp, -16]!
.cfi_def_cfa_offset 16
.cfi_offset 29, -16
.cfi_offset 30, -8
mov w0, 4
mov x29, sp
bl my_malloc
adrp x1, .LANCHOR0
mov w0, 0
ldp x29, x30, [sp], 16
.cfi_restore 30
.cfi_restore 29
.cfi_def_cfa_offset 0
str wzr, [x1, #:lo12:.LANCHOR0]
ret
.cfi_endproc
.LFE23:
.size main, .-main