是否有正当理由在C程序的main()函数中声明变量static?

问题描述 投票:2回答:8

我知道C中static关键字的各种含义,但我的问题更具体:是否有任何正当理由将嵌入式C语言程序的main()函数中的某些变量声明为静态?

由于我们讨论的是在main()的大括号内声明的变量,因此范围已经是main()的本地。关于持久性,主函数位于调用堆栈的顶部,只要程序正在运行就无法退出。因此,从表面上看,似乎没有正当理由声明在main()中使用static。

但是,我注意到使用静态声明的效果是保持变量和数组不被放置在堆栈上。在堆栈大小有限的情况下,这可能很重要。

另一种可能的但却不常见的情况是main()以递归方式调用自身,在这种情况下,您可能需要一些变量在递归的不同级别持久存在。

是否有任何其他可能的正当理由在main()函数体内使用变量的静态声明?

c variables static embedded main
8个回答
2
投票

..有效的理由在main()函数体内使用变量的静态声明?

  1. 初始化 int main(void) { static int a; // initialized to 0 int b; // uninitialized
  2. 在C中,调用main()调用。 ref所以关于static变量的常见问题适用。 int main(void) { static int c; // Only one instance int d; // One instance per call ... main();
  3. 记忆位置。各种编译器将组织main()的变量。它不是由C指定的,因此是编译器相关的问题。

2
投票

变量除了类型外还有三个属性:

  • 范围(能见度)
  • 一生
  • 地点

最好根据代码的语义来选择这些属性。虽然static中的main()变量可能看起来与非静态变量具有相同的范围和生命周期,因此仅在位置上有所不同,但它没有相同的语义。如果 - 可能正在开发中 - 您决定将main()中的代码移动或重组为子例程,则使用static将导致此类代码的行为不同且可能不正确。

因此,我的建议是,您在static中使用main()的相同决定与任何其他函数一样,并且不将其视为与非静态语义相同的特殊情况。这种方法不会导致可重用或可维护的代码。

在任何情况下,你都必须将内存划分为堆栈,堆和静态空间,如果你有一个堆栈变量,整个过程的生命周期,你就会通过使用更多的堆栈来换取更少的静态空间来抢夺Peter付给Paul,所以它是不是真正的争论,除了内存不足是构建时问题而不是运行时这一事实,但如果这是一个严重的问题,你可能会考虑将所有或大多数变量作为理所当然的静态(必然排除reentrancy,因此递归和多线程)。如果堆栈空间确实是一个问题,那么递归在任何情况下都是一个坏主意 - 在大多数情况下它已经是一个坏主意,并且肯定是main()的递归。


2
投票

我看到在static中声明main变量的实际原因(大多数台式机或笔记本电脑操作系统上的大多数编译器):main中的局部变量消耗了call stack上的空间。 static变量占用调用堆栈外的空间(通常在.bss.data段中,至少在ELF可执行文件中)

如果该变量占用大量空间(想想一百万个整数数组),那将会产生很大的不同。

在当前(台式机,笔记本电脑,平板电脑)系统上,堆栈空间通常是有限的(到一个或几兆字节)。

在某些嵌入式处理器上,堆栈限制为小于1千字节。


0
投票

我想你已经说过了。它使得它们是静态的,对于main()不是很有趣,其次它使它们成为我称之为局部全局变量,它实际上将它们与全局变量(而不是堆栈)放在.data中,但是限制了它们的访问范围,如本地人。所以对于main(),我想原因是为了节省一些堆栈。如果你阅读堆栈溢出,虽然看起来有些编译器在main()上放了一个大堆栈框架,大部分时间都没有意义,所以你真的节省了空间吗?同样,如果它们位于main的堆栈上,除非你递归调用main,它们不会占用.data与堆栈相比更多或更少的空间。如果它们被优化为寄存器,那么你仍然会烧掉.data,你不会烧掉堆栈空间,所以它可能会花费你一点空间。


0
投票

尝试间接访问在与对象关联的不同线程中使用自动存储持续时间定义的对象(即局部变量)的结果是实现定义的(参见:6.2.4对象的存储持续时间,p5)。

如果希望代码可移植,则只应与使用静态,线程或分配的存储持续时间定义的线程对象共享。

在这个例子中,main中定义的自动object应该用static定义:

int main( void )
{
    CreateThreads();
    type object = { 0 };   //missing static storage-class specifier
    ShareWithThreads( &object );
}

0
投票

如前所述,主要原因是节省堆栈空间,并让您更好地了解程序的实际堆栈大小需求。

声明这些变量static的另一个原因是程序安全。这里需要考虑各种嵌入式系统设计规则:

堆栈应始终进行内存映射,以使其朝向无效内存增长,而不是朝向.bss.data。这意味着在堆栈溢出的情况下,程序有可能引发异常,而不是将丰田全部转移到静态变量上。

出于类似的原因,您应该避免在堆栈上声明大块数据,因为这会使代码更容易出现堆栈溢出。这尤其适用于小型,存储器受限的微控制器系统。


另一种可能但非常罕见的情况是main()以递归方式调用自身

那是胡说八道,没有理智的人会写这样的代码。在嵌入式系统中使用递归是非常值得怀疑的做法,通常会被编码标准所禁止。事实是,极少数情况下递归在任何程序中使用都是有意义的,无论是嵌入式还是非嵌入式。


-1
投票

一旦原因是可执行文件中的静态变量的偏移量在链接时确定并且可以依赖于在同一位置。

因此,主要级别的静态变量的有用目的是将程序版本数据包括为可读字符串或二进制可读数据,以后可用于分析/组织可执行文件,而无需具有原始源代码或任何特定于程序的实用程序,而不是十六进制转储程序。我们在部署无法依赖目标系统的程序时使用了这个hack来获得任何开发工具。

例如,

int main(void)
{
    // tag to search for revision number
    static char versionID[3] = {'V','E','R'};
    // statically embedded program version is 2.1
    static unsigned char major_revision = 2;
    static unsigned char minor_revision = 1;

    printf("\nHello World");
    return 0;
}

现在可以在不运行程序的情况下确定程序的版本:

$ od -c -x hello-world | grep“V E R”

0010040 001 002 V E R G C C :( U b u n t


-2
投票

实际上,这是一个巨大的原因!

如果在static(或任何函数...)中将变量声明为main(),则表示此局部变量也是静态的。

这意味着,如果这个函数调用自己......(main()可能不会这样做,虽然它当然可以......)......每个递归实例都会看到相同的值,因为这个变量不是在堆栈上分配。 (因为,嗯,“它是static!”)

“堆栈大小”不是使用static.的正当理由(事实上,它与它没有任何关系。)如果你担心有“堆叠”的空间来存储东西,那么正确的做法是“将它存储在堆中,而不是“使用指针变量”。 (与任何变量一样,它可以是静态的,也可以不是。)

© www.soinside.com 2019 - 2024. All rights reserved.