在C语言中,在函数内部声明结构体

4

我有一个只能由一个函数访问的结构。该函数将诸如“k、K、kb、KB、m、M、mb、MB”之类的标记转换为实际单位。其目的是简化配置文件。

因此,假设我们有:

static uint32_t real_unit(const char *str)
{
    struct u2type {
      char key[3];
      uint32_t val;
    } const u2types[] = {
       { "k", KB_UNIT },
       { "K", KB_UNIT },
       { "kb", KB_UNIT },
       { "KB", KB_UNIT },
       { "m", MB_UNIT },
       { "M", MB_UNIT },
       { "mb", MB_UNIT },
       { "MB", MB_UNIT },
       { "g", GB_UNIT },
       { "G", GB_UNIT },
       { "gb", GB_UNIT },
       { "GB", GB_UNIT },
       { { 0 }, 0 }
    };

    ... code to look up str ...
}

我看过其他程序,其中u2type结构体会被声明为静态的(再次在函数内部),但我看不出这有什么用处。该结构体不会改变,每次进入函数时它都是相同的。这就是为什么我将其设置为const的原因。
然而,我已经看到很多人在函数内部做类似于“static struct foo { ... } const foos[] = { ... }”的事情,其中范围显然是明确的。
这样做有什么好处吗?我试图在来到SO之前研究ASM输出,以解决这些优化问题,但我不是一个汇编大师 :)
编辑:
是的,我知道这种方法很奇怪。一些项目只是有奇怪的要求(通常是由奇怪的人强制执行的)。然而,问题与函数的使用完全无关。
6个回答

13
将其设置为const和将其设置为static是两个不同的操作。
  • 如果它是const,每个函数调用都会获得自己的不可更改的结构体实例。
  • 如果它是静态的,则所有函数调用共享一个可更改的结构体实例。

听起来你想要的是一个既是静态又是const的结构体实例,这是一个合理的做法。

就性能而言,静态版本应该稍微占优势,因为结构体实例的构建只会执行一次。


编译器为什么不能将const版本也只构建一次? - aib
在C语言中,我不确定是否有任何东西需要这样做,但肯定不是必需的。 - anon
如果声明为 static const,它甚至可以被定位在代码段中(或附近),并且在运行时不需要额外的存储空间以及初始值。在嵌入式环境中,ROM 中只有代码而几乎没有可用 RAM 时,这一点可能非常重要。 - RBerteig

5
如果你将数组声明为静态的,它将被放置在可执行文件的数据段中,并且只会在第一次访问时初始化,甚至可能不会被初始化(因为它可能已经在可执行文件中被初始化)。
如果没有使用静态关键字,则数据将位于每个函数调用的堆栈上,并且每次调用函数时都会进行初始化。
只是一个小细节,当你说你看到过struct u2type是静态的代码时,这并不完全正确。虽然静态存储说明符出现在结构体之前,但它实际上适用于变量,在这种情况下是数组。即使有了静态关键字,结构体本身仍然是自动存储的。
static struct foo { ... } foos [] = { ... };

你可以随后执行以下操作:
struct foo foo1={ ... };

使用以下代码,foo1将是一个自动变量。


0

...

// so, do you want odor-free, or fast ?
switch (str[0]){
case 'g': case 'G':
  return GB_UNIT;
case 'k': case 'K':
  return KB_UNIT;
case 'm': case 'M':
  return MB_UNIT;
}

这就是我通常的做法。因此有这个问题,希望能够挤出我必须利用的每一分资源。 - Tim Post
我认为这是一个关键问题,因为它让我们区分代码效率和编码效率,而不是两者之间的权衡。事实上,通过一些代码生成器,你可以同时拥有两者。 - Mike Dunlavey

0
如果您在函数内部声明一个静态变量,它只会在第一次进入函数时初始化一次。如果您将其声明为非静态,则每次进入函数时都会初始化。
在您的情况下,这可能会有轻微的差异。使用 static,数组将分配在静态存储中,并且最多只会初始化一次。没有 static,它将在堆栈上分配,并且每次调用函数时都会初始化。

0

本地变量或常量分配在堆栈上。它们的存在仅在函数执行期间持续,其值在函数返回时丢失。每次调用函数时都会进行分配和赋值。

声明本地变量或常量为静态意味着其值将从一次调用到下一次保留。这是通过全局分配而不是在堆栈上实现的。只进行一次分配和赋值,在频繁调用大数据结构的函数中可能会导致性能提高。


静态变量很少或几乎不会在堆上分配。 - anon
全局变量也不会在堆上分配 - anon
我认为mouviciel的意思是“全局分配”,而不是“堆上分配”。 - Mike Dunlavey
谢谢Mike让我的回答更清晰。我会相应地进行更新。 - mouviciel

0

呃……至少将你的函数名更改为'case_insensitive_guess_unit'。这些大多数不是“真正的”单位,而那些是(例如 'K' 代表开尔文而不是千,'b' 通常是位,而'B' 则是字节)都不是你返回的单位。

如果规范是 k[b] -> 1000,m[b] -> 1000000 等,则使用简单的 if/else 可能更快且更清晰。


目的是在配置文件中进行宽容处理。还支持“自动”和其他变体,其中程序选择最小单位...即如果用户有120 GB磁盘和512 MB内存,则会选择MB。 - Tim Post
1
那么这与真实单位有什么关系呢?如果用户指定的数据是以Mb或MiB而不是MB为单位呢? - Pete Kirkham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接