【问题标题】:C99 Variable Length Array Max sizes and sizeof FunctionC99 可变长度数组最大大小和 sizeof 函数
【发布时间】:2015-12-18 05:54:48
【问题描述】:

我正在尝试在我的 C 代码中使用可变长度数组 (VLA),并试图澄清我对它们应该做什么和不应该做什么的理解。

我的函数中有以下 sn-p:

void get_pdw_frame_usb(pdws_t *pdw_frame, pdw_io_t *pdw_io)
{
...
unsigned char buf[pdw_io->packet_size];
unsigned char *pdw_buf;
memset(buf, '\0', sizeof(buf));

pdw_io 是一个数据结构,其中包含packet_size,其类型为size_t

char数组buf用于存储usb批量传输包的内容

我正在尝试使用 C99 VLA 方法在此处将其实例化为自动变量。然后我试图确保它的内容都是零。

我遇到了一些问题。

首先,如果pdw_io->packet_size 设置为 8(非常小),那么 buf 设置为一个合理的值,即使用 gdb 进行调试,我可以如下检查:

(gdb) p buf
$27 = 0xbffe5be8 "\270", <incomplete sequence \370\267>

如果 pdw_io-&gt;packet_size 设置为 12008(稍微大一点),那么我会得到以下看起来不太好的结果:

(gdb) p buf
$29 = 0xbffe2d08 ""

对于 VLA,12008 个字符是否太大?或者也许 gdb 输出并不值得担心,它只是看起来有点像它没有分配任何东西给我?

此外,在检查 buf 的大小时,我在两种情况下都得到以下信息:

(gdb) p sizeof(buf)
$30 = 0

我预计第一次是 8,第二次是 12008

我认为应该可以通过 VLA 以这种方式使用 sizeof 函数是不是错了?

我的问题是随后的 USB 批量传输失败,我想尝试排除它可能与我使用 VLA 有关的事实,这对我来说是一个新领域..

更新

编写了以下最小、完整且希望可验证的程序来尝试确认我的观察结果:

#include <stdio.h>

void test_vla(size_t n)
{
    unsigned char buf[n];
    printf("sizeof buf = %zu\n", sizeof buf);    
}

int main()
{
    test_vla(12008);
    return 0;
}

现在,如果我使用 gdb 中断 printf 语句并运行 p sizeof buf 我得到 0 但 printf 输出 12008。

gdb 版本是:

(gdb) show version
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1

【问题讨论】:

  • @Olaf 什么都没有,计算错误。
  • 请注意,编译器不需要支持 VLA:s 即可与 C11 兼容。这意味着出于可移植性的原因应该避免使用 VLA:s,并且它们可能会在未来的 C 标准中被删除。
  • @bph gdb 将 char 数组打印为以零结尾的字符串,因此输出告诉您的只是第一个字符为零。我认为您在这里没有实际问题。
  • 您的更新没有使用 VLA,所以它当然不会触发问题。旧版本的 gdb 不支持 VLA 上的sizeof;较新的版本可以。请参阅我的更新答案。

标签: c c99 variable-length-array


【解决方案1】:

您遇到的问题是gdb 中的一个错误(或者更准确地说是缺少的功能)。 gdb 无法正确处理应用于 VLA(可变长度数组)的 sizeof 运算符。

来自 gdb 邮件列表的This message 表明对 VLA 上的sizeof 的支持已在gdb 中实现,但只是最近才实现的。显然它不在您和我都使用的版本(gdb 7.7.1)中。如果没有该修复,它会错误地将 VLA 的大小打印为 0。您的代码本身应该正确运行;只是 gdb 处理不当。

您的代码没有什么特别的问题,只要 (a) 它是使用支持 VLA 的编译器编译的,并且 (b) 数组的大小是正数且不太大。 (C90 不支持 VLA,除了可能作为扩展,在 C99 中作为标准功能引入,在 C11 中成为可选功能。)

一种可能的解决方法是修改您的程序,将sizeof vla 的值保存到一个变量,然后您可以从gdb 打印该变量。

gdb 的另一个问题是打印 VLA 对象本身与打印固定大小的数组对象的行为不同。它显然将 VLA 视为指向其第一个元素的指针,而不是数组对象。

这是一个说明问题的gdb 成绩单:

GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
[SNIP]
(gdb) list
1       #include <stdio.h>
2       #include <string.h>
3       int main(void) {
4           int len = 6;
5           char vla[len];
6           const size_t vla_size = sizeof vla;
7           char arr[6];
8           strcpy(vla, "hello");
9           strcpy(arr, "world");
10      }
(gdb) break 10
Breakpoint 1 at 0x400600: file c.c, line 10.
(gdb) run
Starting program: /home/kst/c 

Breakpoint 1, main () at c.c:10
10      }
(gdb) print sizeof vla
$1 = 0
(gdb) print vla_size
$2 = 6
(gdb) print sizeof arr
$3 = 6
(gdb) print vla
$4 = 0x7fffffffdc10 "hello"
(gdb) print arr
$5 = "world"
(gdb) print arr+0
$6 = 0x7fffffffdc40 "world"
(gdb) continue
Continuing.
[Inferior 1 (process 28430) exited normally]
(gdb) quit

对于 VLA 来说 12008 个字符是否太大?

可能不会。对于大多数实现,VLA 可以和固定大小的数组一样大。两者之间没有真正的区别(就内存分配而言):

{
    int size = 12008;
    char buf[size];
}

{
    int buf[12008];
}

许多系统限制了您可以在堆栈上分配的内存量,但 12008 字节的数组不太可能突破这些限制。

不过,如果您要分配大型数组,最好通过malloc() 进行分配(这意味着您需要为每个分配的对象显式调用free())。

【讨论】:

  • 我刚刚通过 gdb 运行代码,pdw_io->packet_size 设置为 8。prinf 语句输出 8,gdb 提示输出中的 p sizeof(buf) 输出 0。所以我很高兴我的VLA 正在按预期工作,我只是错误地认为运行 p sizeof(buf) 会给我相同的结果。我只是在 pdw_io->packet_size 设置为 12008 以及 printf 输出 12008 和 gdb 输出 0 的情况下运行它,所以行为相同。太好了,所以我知道 VLA 对于 12008 字节的大小可以正常工作 - 我的问题一定出在其他地方,所以回到绘图板..
猜你喜欢
  • 2012-12-04
  • 1970-01-01
  • 2015-03-18
  • 2012-01-29
  • 2015-11-12
  • 1970-01-01
  • 2014-08-16
  • 2010-10-10
  • 1970-01-01
相关资源
最近更新 更多