【问题标题】:C Constant structures containing arrays of different lengths on the stackC 常量结构,包含堆栈上不同长度的数组
【发布时间】:2015-02-20 15:37:44
【问题描述】:

我有兴趣在 C 中创建一个结构(在其他 POD 类型中)包含一个数组,然后在堆栈上创建它的全局实例。数组长度在编译时是已知的,但对于结构的每个实例都是不同的。结构的每个实例中的值都不会改变,因此应该设置为 const。

目前我有以下代码:

#ifdef __cplusplus
    extern "C" {
#endif

#include "stdio.h"


typedef struct A
{
   int x;
   int y;
   int* z;
} A_t;

const A_t test[2]  = { {1,1, (int[3]){1,1,1}     },
                       {2,2, (int[5]){2,2,2,2,2} }
                     };

int main( void )
{
    printf( "test[0]: %d %d (%d, %d, %d)\n",
        test[0].x,
        test[0].y,
        test[0].z[0],
        test[0].z[1],
        test[0].z[2] );
    printf( "test[1]: %d %d (%d, %d, %d, %d, %d)\n",
        test[1].x,
        test[1].y,
        test[1].z[0],
        test[1].z[1],
        test[1].z[2],
        test[1].z[3],
        test[1].z[4] );
    printf( "\n\n" );


    return;
}

#ifdef __cplusplus
    }
#endif

这将在堆非常有限的嵌入式系统上运行,所以我想避免 malloc,除非我绝对必须这样做。如果可能的话,我也想坚持使用 C89,因为 VS2008 不支持 C99(我认为)。

这在 GCC(通过 MinGW 的 4.8.1)中运行良好,但在 Visual Studio 2008 中无法编译。有人对我如何让它在两个编译器上工作有任何建议吗?

作为参考,这是来自 VS2008 的错误消息:

c:\temp\test_variable_length_arrays\bob.c(15):错误 C2059:语法 错误:'{'

c:\temp\test_variable_length_arrays\bob.c(15):错误 C2059:语法 错误:'}'

c:\temp\test_variable_length_arrays\bob.c(17):错误 C2059:语法 错误:'}

提前感谢所有 cmets。

【问题讨论】:

  • 我不确定我是否理解;你想使用 C89 因为 VS2008 支持它?顺便说一句,可变长度数组仅在 C99 中可用 - C89/C90 不支持它们(C++ 也不支持)。
  • 这不是 vla。而c89不支持vla。
  • 我同意 VLA 不能很好地描述我想做的事情;但是,我不确定正确的术语是什么。我不想在运行时改变数组长度,但我确实需要能够在编译时拥有不同长度的数组,由相同的结构定义描述。
  • GoBusto,感谢您发现错字,我已更正。我的意思是 VS2008 对 C99 的支持很少(如果有的话)。
  • 你可以声明你的结构有灵活的数组成员。像这样:struct s { int n; double d[]; };。使用方法:int m = /* some value */; struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));。详情请参考c11标准6.7.2.1。

标签: c struct stack


【解决方案1】:

您可以全局分配内部结构,然后在运行时分配给结构,例如

int x[] = {1,1,1};
int y[] = {3,3,3,3,3};

int main()
{
    A_t str = {2, 2, y};
}

我相信一些编译器还会让您将str 的定义移动到全局范围以使其完全静态。

【讨论】:

  • 由于初始化器y是一个常量表达式,你可以将str移到全局作用域。
  • 谢谢,这是一种可能性(它适用于 GCC 和 VS2008);但是,我们正在考虑让“测试”数组(在我上面的代码示例中)超过 100 个元素,这可能会变得混乱。此外,可能需要将 int* 更改为另一种结构类型的可变长度数组(取决于客户要求),因此这可能会很快膨胀:)
  • @BriantheDog:您可能想编写一个脚本或其他 C 程序来生成初始化代码;至少这样你就不必在每次发生变化时都手动修改它。
  • 我认为这是最好的方法——使用脚本编写结构定义和初始化代码。复合文字是在 C99 中添加的,因此在 VS2008 中不存在 - 它们是在 VS2013 中添加的(see here)。我会将此标记为解决方案(并要求我们的团队升级到 VS2013!)。感谢大家的建议。
【解决方案2】:

请参阅 cmets 了解 (int[3]){1,2,3} 语法。但是不能保证这些数组会在内存中持久存在(它们是临时的)。您可以将它们定义为全局变量,并设置指向它们的指针:

static int az1[] = {1, 2, 3};
static int az2[] = {1, 2, 3, 4, 5};
const A_t test[2]  = { {1,1, az1 },
                       {2,2, az2 }
                     };

【讨论】:

  • int *p = (int []){2, 4}; 这个表达式是有效的。参见 C11 标准 p104 示例 1。这是一个复合字面量初始化。
  • 不幸的是,复合文字落后于 C89/C90,这是 Visual Studio 2008 支持的最新标准。
【解决方案3】:

Visual Studio 对以下内容感到窒息:

const A_t test[2]  = { {1,1, (int[3]){1,1,1}     },
                       {2,2, (int[5]){2,2,2,2,2} }

具体来说,复合文字 (int[3]){1,1,1}(int[5]){2,2,2,2,2};复合文字是由 C99 引入的,不会被 Visual Studio 等 C89 编译器识别。

这个要由 Visual Studio 编译吗?你可以用 MinGW(Windows 原生 GNU 编译器)或 cygwin 下的 gcc 来构建它吗?如果没有,slugonamission 的解决方案可能是阻力最小的路径。

【讨论】:

  • 不幸的是,它必须使用 Linux 中的 gcc(用于芯片固件)和 Windows 中的 VS2008(用于客户界面软件)编译 - 否则一夜之间的 Jenkins 测试将失败,我会得到一个电子邮件 :) 我试图避免做 slugonamission 建议的事情,因为我的“测试”数组中可能有超过 100 个元素,然后会变得非常繁琐。
  • @BriantheDog:那是……不幸。我将在我的其他评论中重复该建议;您可能想要编写脚本或其他程序来生成初始化代码,而不是自己手动修改。
猜你喜欢
  • 2014-03-14
  • 2018-04-10
  • 1970-01-01
  • 2011-04-20
  • 2018-04-27
  • 1970-01-01
  • 2017-02-03
  • 1970-01-01
相关资源
最近更新 更多