使用 strlen() 在堆栈中分配缓冲区

Posted

技术标签:

【中文标题】使用 strlen() 在堆栈中分配缓冲区【英文标题】:Buffer allocation in stack with strlen() 【发布时间】:2014-09-07 02:27:21 【问题描述】:

我有以下代码:

void function(char *str)

    int i;
    char buffer[strlen(str) + 1];

    strcpy(buffer, str);
    buffer[strlen(str)] = '\0';

    printf("Buffer: %s\n", buffer);

我希望这段代码会引发编译时错误,因为在堆栈上分配的“缓冲区”具有运行时相关长度(基于 strlen())。但是在 GCC 中编译通过了。这是如何运作的?缓冲区是动态分配的,或者如果它仍然是堆栈本地的,那么分配的大小是多少?

【问题讨论】:

这是有效的 C99,是的,它是根据计算的大小在堆栈上分配的。 @mafso 为什么说它会导致未定义的行为? 您标记了两种不同的语言。这在(现代)C 中有效,但在(标准)C++ 中无效。你用的是哪个? @DrewMcGowen 对不起,我有点慢。所以我的理解是,这在非 C99 中是完全有效的,并且在每次调用 function() 时,缓冲区都会在堆栈上分配;并且大小将等于 strlen() 的计算结果? 【参考方案1】:

C99 允许可变长度数组。不在 C99 中编译代码不会产生任何错误,因为 GCC 还允许变长数组作为扩展。

6.19 Arrays of Variable Length:

在 ISO C99 中允许使用可变长度自动数组,作为扩展,GCC 在 C90 模式和 C++ 中接受它们

【讨论】:

【参考方案2】:

通过反汇编你的函数,你可以很容易地验证这一点:

$ objdump -S <yourprogram>

...
void function(char *str)

   4011a0:   55                      push   %ebp
   4011a1:   89 e5                   mov    %esp,%ebp
   4011a3:   53                      push   %ebx
   4011a4:   83 ec 24                sub    $0x24,%esp
   4011a7:   89 e0                   mov    %esp,%eax
   4011a9:   89 c3                   mov    %eax,%ebx
     int i;
     char buffer[strlen(str) + 1];
   4011ab:   8b 45 08                mov    0x8(%ebp),%eax
   4011ae:   89 04 24                mov    %eax,(%esp)
   4011b1:   e8 42 01 00 00          call   4012f8 <_strlen>
   4011b6:   83 c0 01                add    $0x1,%eax
   4011b9:   89 c2                   mov    %eax,%edx
   4011bb:   83 ea 01                sub    $0x1,%edx
   4011be:   89 55 f4                mov    %edx,-0xc(%ebp)
   4011c1:   ba 10 00 00 00          mov    $0x10,%edx
   4011c6:   83 ea 01                sub    $0x1,%edx
   4011c9:   01 d0                   add    %edx,%eax
   4011cb:   b9 10 00 00 00          mov    $0x10,%ecx
   4011d0:   ba 00 00 00 00          mov    $0x0,%edx
   4011d5:   f7 f1                   div    %ecx
   4011d7:   6b c0 10                imul   $0x10,%eax,%eax
   4011da:   e8 6d 00 00 00          call   40124c <___chkstk_ms>
   4011df:   29 c4                   sub    %eax,%esp
   4011e1:   8d 44 24 08             lea    0x8(%esp),%eax
   4011e5:   83 c0 00                add    $0x0,%eax
   4011e8:   89 45 f0                mov    %eax,-0x10(%ebp)
....

无论如何,这里的相关组件是sub %eax,%esp。这表明堆栈已根据之前返回的任何 strlen 进行扩展以获取缓冲区空间。

【讨论】:

以上是关于使用 strlen() 在堆栈中分配缓冲区的主要内容,如果未能解决你的问题,请参考以下文章

在linux中分配物理内存缓冲区

在 Linux 内核中分配用户空间内存

如果我们超过了java中ByteBuffer.allocate(48) NIO包类中分配缓冲区的容量怎么办

DirectBuffer及内存泄漏

DirectBuffer及内存泄漏

C++ STL 问题:分配器