【问题标题】:Integer array size in C without using dynamic memory allocationC中的整数数组大小而不使用动态内存分配
【发布时间】:2022-01-02 21:22:32
【问题描述】:

我需要声明一个大小为symbolnum的结构数组,但是因为symbolnum是变量C,当我编写以下代码时会产生错误:

extern int symbolnum;

struct SymbTab stab[symbolnum];

我已经试过了:

extern int symbolnum;
const int size = symbolnum;
struct SymTab stab[size];

有没有办法在不使用动态内存分配函数(如malloc())或使用非常大的数字初始化数组大小的情况下实现这一点?

【问题讨论】:

  • 使用malloc有什么问题?
  • stab 是全局变量还是局部变量?
  • @notthatbad 所以你需要告诉我们更多关于你的实际问题。 symbolnum 的内容是谁定义的,什么时候定义的?
  • @notthatbad 那么这是否意味着包含stab 的源文件是在构建时使用初始化程序创建的?
  • 另一种方法:声明 struct SymbTab stab[1024]; 并在您的程序中添加运行时检查以确保您的符号少于 1024 个。

标签: arrays c malloc size


【解决方案1】:

全局数组的大小必须在编译时固定。 VLA 只能在函数内部使用。

如果symbolnum 的值直到运行时才知道,您需要将stab 声明为指针类型并为其动态分配内存。

或者,如果数组不占用超过几十个 KB,您可以在 main 函数中定义 VLA 并设置一个指向它的全局指针。

【讨论】:

  • 谢谢。如果我在 main 中声明数组呢?
  • 在文件范围级别拥有 VLA 需要在文件范围内评估大小表达式,这是一个相当大的问题。这与 C 的基本原则之一相矛盾。“在main() 之外没有代码执行”
  • @notthatbad 你可以在main 中声明VLA,然后有一个指向它的全局指针。
【解决方案2】:

C11 和更高版本允许可变长度数组作为可选功能。 C99 允许它们作为强制性功能。但是,在任何情况下都不允许在文件范围内使用 VLA,这似乎是您想要实现的目标。

无论如何,文件范围的 VLA 就 C 语义而言是没有意义的。在文件范围内声明的对象具有静态存储持续时间,这意味着它们在程序执行开始时或之前存在,并一直存在到程序终止。这意味着在变量可以取其初始值(零或整数常量表达式)以外的任何值之前,需要数组长度,因此不妨直接使用该初始值。

此外,即使 C99 是当前标准,一些 C 实现(尤其是 MSVC)也从未实现 VLA,并且由于该功能在当前标准中是可选的,因此也不打算这样做。

所以,

有没有办法在不使用动态内存分配的情况下实现这一点 malloc() 之类的函数或使用非常 大数?

这在一定程度上取决于您的确切需求(以及您的 C 实现),但可能不是。一种可能性是在main() 中使用本地VLA,或者在其他一些函数中使用其执行包含对数组的全部需求。如果你的 C 实现支持它们,你可以在那里声明一个 VLA,并传递一个指向它的指针。但请注意,如果您需要的元素数量的上限确实是“一个非常大的数字”,那么这不太适合。 VLA 通常在堆栈上分配,这对它们的大小设置了相对严格的限制。

【讨论】:

  • 您提到是否使用 VLA 的一个引人注目的限制是堆栈内存的限制,但在大多数开发环境中,堆栈的大小可以更改以满足设计限制。这不会缓解您心中的堆栈问题吗?
  • @rykker,仅当可以在编译时选择所需元素数量的适当界限时,才能确保足够数量的堆栈。但是,将数组声明为具有所需大小上限的普通数组会更容易、更便携且语义相似。
  • @JohnBollinger 仅当可以在编译时选择所需元素数量的适当界限时这如何不适用于从malloc()获得的内存等人?如果您超额订阅资源,您将失败。如果你不知道限制,你就是在盲目编码。鉴于 Linux 上通常使用的内存过度分配模型,至少使用堆栈内存会立即失败,而不是“哦,你居然敢使用我说你可以拥有的内存?!?!我撒谎了!!这是 SIGKILL 给你的!BWAAHAAAHAA !!”并且您的客户想知道为什么该流程在启动 11 天后就终止了。
  • @AndrewHenle,在 VLA 的情况下,可以容纳的元素数量大约是二进制的参数,因为 rykker 建议通过开发工具设置可接受的堆栈大小来容纳它。对于malloc(),可以容纳的元素数量是执行环境的一个属性。我想有些场景会支持其中的每一个。但是,如果您正在考虑在编译时提供所需的空间,那么为什么要走神秘的路线来调整堆栈大小而不是仅仅声明一个最大大小的数组?
  • @JohnBollinger 堆栈大小只是进程初始线程的编译时参数。 Windows 和 POSIX 系统上每个其他线程的堆栈大小都是运行时参数。
【解决方案3】:

"有没有办法在不使用 malloc() 等动态内存分配函数或初始化数组大小的情况下实现这一点 使用一个非常大的数字?”

如果您可以使用variable length array (VLA),那么可以。下面说明一种方式...

在全局空间中使用struct 定义,(例如,.c 文件的顶部,或在 .h 文件中)本地 可以使用 VLA 创建 struct 的数组实例,请记住链接中提到的使用 VLA 的规定。然后可以将 VLA struct 数组作为函数参数传递,以便在被调用函数中使用,或者更新并返回,就像使用任何其他函数参数一样。这是一个简单的例子:

//define either at top of .c file in file global space
//or in a header file that is included in any .c.  Then
//the typedef num_s can be used to create instances where needed
//
typedef struct SymbTab{
    int iVal;
    double dVal;
} SymbTab_s;

void populate_struct(size_t symbolnum, SymbTab_s *stab);

int main(void)
{
    size_t symbolnum = 0;//(using size_t). note, as you describe, this 
                         //comes after flex analysis normally, but for 
                         //this demo user input is used for simple 
                         // example of dynamically sizing array.
    printf("Enter symbolnum of struct array:\n");
    scanf("%zu", &symbolnum);
    SymbTab_s stab[symbolnum]; //dynamically sized array of SymbTab_s
    memset(stab, 0, sizeof(SymbTab_s) * symbolnum); //initialize new array
    populate_struct(symbolnum,  stab); //pass as function argument and update values
    //demo updated values
    for(int i=0; i < symbolnum; i++)
    {
        printf("%d, %lf\n", stab[i].iVal, stab[i].dVal);
    }
    return 0;
}

//simple function to demo form of parameters
//note size parameter is passed first
void populate_struct(size_t symbolnum, SymbTab_s *stab)
{
   for(int i=0; i < symbolnum; i++)
   {
       stab[i].iVal = i;
       stab[i].dVal = 1.0*i;
   }
}

【讨论】:

    猜你喜欢
    • 2020-11-26
    • 2021-12-09
    • 2021-04-02
    • 1970-01-01
    • 2022-11-12
    • 2023-03-17
    • 2021-07-17
    • 2013-05-24
    相关资源
    最近更新 更多