【问题标题】:Declare an array in gcc statement-expression and return a pointer to it?在 gcc 语句表达式中声明一个数组并返回一个指向它的指针?
【发布时间】:2017-03-22 01:56:42
【问题描述】:

我已经考虑过了。 这是代码:

#define IP_VAL(id) ({ \
    char ip[32] = {0}; \
    sprintf(ip,"192.168.0.%d",id&0xff); \
    (char *)ip; \
})

确实有效,不知道是不是宏会出问题?

我是这样使用的:

function(IP_VAL(id));

但我不确定这绝对没问题。 ip[32] 是否超出操作范围?

非常感谢您编辑我的问题:)

我已经测试了这样的简单示例:

main(){
    int a = 1;
    int b = 2;
    a = b;
    a = ({
            int c = 3;
            c;
        });
    a = ({
            int d = 4;
            d;
        });
    a = ({
            int c = 5;
            c;
        });
    ({
        int c = 6;
        c;
    });
    ({
        int c = 7;
        c;
    });
    ({
        int c = 8;
        c;
    });
}

然后 objdump 它:

080483ed <main>:
 80483ed:   55                      push   %ebp
 80483ee:   89 e5                   mov    %esp,%ebp
 80483f0:   83 ec 20                sub    $0x20,%esp
 80483f3:   c7 45 e0 01 00 00 00    movl   $0x1,-0x20(%ebp)
 80483fa:   c7 45 e4 02 00 00 00    movl   $0x2,-0x1c(%ebp)
 8048401:   8b 45 e4                mov    -0x1c(%ebp),%eax
 8048404:   89 45 e0                mov    %eax,-0x20(%ebp)
 8048407:   c7 45 e8 03 00 00 00    movl   $0x3,-0x18(%ebp)
 804840e:   8b 45 e8                mov    -0x18(%ebp),%eax
 8048411:   89 45 e0                mov    %eax,-0x20(%ebp)
 8048414:   c7 45 ec 04 00 00 00    movl   $0x4,-0x14(%ebp)
 804841b:   8b 45 ec                mov    -0x14(%ebp),%eax
 804841e:   89 45 e0                mov    %eax,-0x20(%ebp)
 8048421:   c7 45 f0 05 00 00 00    movl   $0x5,-0x10(%ebp)
 8048428:   8b 45 f0                mov    -0x10(%ebp),%eax
 804842b:   89 45 e0                mov    %eax,-0x20(%ebp)
 804842e:   c7 45 f4 06 00 00 00    movl   $0x6,-0xc(%ebp)
 8048435:   c7 45 f8 07 00 00 00    movl   $0x7,-0x8(%ebp)
 804843c:   c7 45 fc 08 00 00 00    movl   $0x8,-0x4(%ebp)
 8048443:   c9                      leave  
 8048444:   c3                      ret

所以我认为'c'和'd'就像'a'和'b'一样,它们是'main()'堆栈中的变量。不同之处在于它们的范围。在示例中,每个“c”都有不同的地址。

我认为宏 'IP_VAL' 有效,但我不知道 gcc 编译选项是否会影响它?

【问题讨论】:

  • C 还是 C++?择其一,而不是两者兼而有之。
  • 肯定不行。使用函数。
  • 宏不是函数!
  • 我删除了 C++ 标签,因为您要求使用 C 并且您的代码没有显示任何 C++。 C 和 C++ 是不同的语言
  • 还要注意 compound statements 是一个 GCC 扩展。这不是标准的 C 功能。

标签: c arrays gcc macros c-preprocessor


【解决方案1】:

这不是标准 C,而是 gcc 扩展。它没有被安全使用。

ip 数组的生命周期仅限于声明它的范围。您将指向该数组的指针返回到语句表达式的外部,该数组不再存在。有人建议生命周期是完整表达式的持续时间,但 GCC 文档中似乎没有任何内容支持这种说法。取消引用此指针可能会导致任何结果。它似乎完全靠运气。

将数组声明为static 可以解决问题,但是静态本地数组可以让您编写一个不依赖于 gcc 扩展的简单函数。

解决问题的另一种方法如下:

#define IP_VAL(id) \
    ( \
     ( \
      { \
       struct { char ip[32]; } p; \
       sprintf (p.ip, "192.168.0.%d", id&0xff); \
       p; \
      } \
     ).ip \
    )

此版本没有任何指向任何可能过期的局部变量的指针。

【讨论】:

  • 这是一个很好的例子,但我无法理解两个例子之间的本质区别。
  • 一个版本具有 (1) 一个作用域变量和 (2) 一个指向该变量的指针,该变量可在作用域外访问。另一个有 (1) 临时结构内的临时数组,它衰减到指向其第一个元素的指针 (2) 所述指针在临时仍然存在时被访问。为了更好地可视化这一点,请将两个宏都重写为函数。一个返回一个指向其局部变量的指针,另一个返回一个按值的结构。
  • 为什么不简单:#define IP_VAL(id) ({ \ char ip[32] = {0}; \ sprintf(ip,"192.168.0.%d",id&0xff); \ ip ; \ }) 我假设这会按值返回数组,它的类型是 char[32],因此在父表达式中使用是安全的
  • @user377178 您不能按值传递数组,没有这样的事情。正是因为如此,才需要结构。
  • 我现在可以看到这个了。谢谢。我的实验表明 gcc 确实保留了缓冲区,即使在通过指针传递时也是如此。更安全然后抱歉。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-23
  • 2015-08-31
  • 2016-08-04
  • 2020-09-16
  • 1970-01-01
相关资源
最近更新 更多