使用宏来定义两个局部变量但具有相同的地址

时间:2014-12-01 04:37:05

标签: c linux gcc macros gnu

代码如下:

#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"能行得通。

1 个答案:

答案 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位系统上有所作为)
  • memsetmemcpy的宏不比strcpy更安全;在强健代码中,您应将此strcpy更改为snprintf(ms.str, sizeof ms.str, "%s", str);