代码如下:
#include"stdio.h"
#define MySTRING(ident, str) \
({\
char str_##ident[16]; \
memset((char *)str_##ident, 0x00, sizeof(str_##ident)); \
memcpy(str_##ident, (str), strlen((str))); \
str_##ident; \
})
int main(int argc, char **argv)
{
printf("%u, %u\n", MySTRING(qw, "1.1.1.1"), MySTRING(er, "2.2.2.2"));
}
Tetst结果:
[root@cza temp]# gcc -O0 ./fly.c
[root@cza temp]# ./a.out
3959297344, 3959297360
[root@cza temp]# gcc -O2 ./fly.c
[root@cza temp]# ./a.out
2017090240, 2017090240
似乎gcc乐观地对它产生了影响。
第二个结果不是我想要的,但在我的应用程序构建模板中,已经设置了O2。
我想知道为什么O2会让它变得不同,或者它是GCC的错误?
P.S。我的同事告诉我前缀" volatile"能行得通。
答案 0 :(得分:2)
您的代码是名为Statement Expression的gcc扩展程序。执行大括号中的语句,最终语句的值是表达式的值。当语句结束时,任何创建的对象都将被销毁。
在宏观中没有任何区别;你的代码(更新:原始代码)是:
printf("%u, %u\n", ({ char ip_qw[16]; ip_qw; }), ({ char ip_er[16]; ip_er; }) );
当ip_qw
块结束时,ip_qw
被销毁,因此释放内存以供ip_er
使用。这解释了为什么两者都可以看到相同的地址。
您的代码无效,因为printf
函数将在销毁后访问16字节数组的内容。
幸运的是,标准C有一个解决方案。保证按值返回的对象会挂起,直到进行函数调用的语句结束。数组不能返回数组,但结构可以,所以我们可以去:
#include <stdio.h>
#include <string.h>
struct MS
{
char str[16];
};
struct MS make_MS(char const *str)
{
struct MS ms;
strcpy(ms.str, str);
return ms;
}
#define MySTRING(s) make_MS(s).str
int main(int argc, char **argv)
{
printf("%s, %s\n", MySTRING("1.1.1.1"), MySTRING("2.2.2.2"));
}
注意:
%p
打印指针(在64位系统上有所作为)memset
和memcpy
的宏不比strcpy
更安全;在强健代码中,您应将此strcpy
更改为snprintf(ms.str, sizeof ms.str, "%s", str);
。