简体   繁体   English

是否有正当的理由将变量声明为C程序的main()函数内部的静态变量?

[英]Are there valid reasons to declare variables static inside the main() function of a C program?

I am aware of the various meaning of the static keyword in C, but my question is more specific: Is there any valid reason to declare some variables in the main() function of an embedded C-language program as static? 我知道C中static关键字的各种含义,但我的问题更具体:是否有充分的理由将嵌入式C语言程序的main()函数中的某些变量声明为static?

Since we are talking about variables declared inside the braces of main(), the scope is already local to main(). 由于我们正在谈论的是在main()的花括号内声明的变量,因此作用域已经在main()本地了。 As regards persistence , the main function is at the top of the call stack and can't exit as long as the program is running. 关于持久性 ,主要功能在调用堆栈的顶部,并且只要程序正在运行就不能退出。 Hence, on the face of it, there would seem to be no valid reason to declare using static inside of main(). 因此,从表面上看,似乎没有正当理由在main()内部使用static进行声明。

However, I notice that an effect of using static declarations is to keep variables and arrays from being placed on the stack. 但是,我注意到使用静态声明的作用是避免将变量和数组放置在堆栈中。 In cases of a limited stack size, this could be important. 在堆栈大小有限的情况下,这可能很重要。

Another possible, but rather uncommon case is that of main() calling itself recursively, where you might need some variables to persist at different levels of the recursion. 另一种可能但不常见的情况是main()递归调用自身,在这种情况下,您可能需要一些变量才能在递归的不同级别持久化。

Are there any other possible valid reasons to use static declarations of variables inside the body of a main() function? 在main()函数体内使用变量的静态声明还有其他可能的正当理由吗?

.. valid reasons to use static declarations of variables inside the body of a main() function? ..在main()函数体内使用变量的静态声明的正当理由?

  1. Initialization 初始化

     int main(void) { static int a; // initialized to 0 int b; // uninitialized 
  2. In C, main() call be called. 在C中,将调用main()调用。 ref So the usual issues about static variables apply. ref因此,适用于static变量的常见问题。

     int main(void) { static int c; // Only one instance int d; // One instance per call ... main(); 
  3. Memory location. 内存位置。 Various compilers will organize main() 's variable. 各种编译器将组织main()的变量。 It is not specified by C, so a compiler dependent issue. 它不是由C指定的,因此是编译器相关的问题。

A variable has three attributes besides type: 变量除类型外还具有三个属性:

  • Scope (visibility) 范围(可见性)
  • Life-time 一生
  • Location 位置

Selection of these attributes is best done according to the semantics of the code. 最好根据代码的语义来选择这些属性。 While while a static variable in main() may appear to have the same scope and lifetime as a non-static so differ only in location, it does not have the same semantics . 尽管main()static变量似乎与非static变量具有相同的作用域和生存期,因此仅在位置上有所不同,但它的语义并不相同。 If - as is likely under development - you decide to move or reorganise the code in main() into a sub-routine, the use of static will cause such code to behave differently and potentially incorrectly. 如果-可能正在开发中-如果您决定将main()的代码移动或重新组织到子例程中,则使用static将导致此类代码的行为有所不同,并可能会导致错误的行为。

My advice therefore is that you use the same determination of the use of static in main() as you would any other function and not treat it as a special case semantically identical to a non-static. 因此,我的建议是,与在其他函数中一样,对main()static使用相同的确定,并且不要将它视为在语义上与非static相同的特殊情况 Such an approach does not lead to reusable or maintainable code. 这种方法不会导致代码可重用或可维护。

You have to partition the memory into stack, heap and static space in any case, if you have a stack variable with a lifetime of the entire process you rob Peter to pay Paul by using more stack in exchange for less static space, so it is not really an argument, except for the fact that insufficient memory is then a build-time issue rather then run-time, but if that is a serious concern, you might consider making all or most variables static as a matter of course (necessarily precluding reentrancy, and thus recursion and multi-threading). 在任何情况下,您都必须将内存划分为堆栈,堆和静态空间,如果您有一个堆栈变量具有整个过程的生命周期,则您会抢走Peter来支付Paul的费用,方法是使用更多的堆栈来交换较少的静态空间,因此除了内存不足是构建时而不是运行时的问题外,这并不是一个真正的争论,但是,如果这是一个严重的问题,那么您当然可以考虑使所有大多数变量保持静态(当然,重入,从而实现递归和多线程)。 If stack space were truly an issue, recursion would be a bad idea in any case - it is a bad idea in most cases already, and certainly recursion of main() . 如果堆栈空间确实是一个问题,那么在任何情况下,递归都是一个坏主意-在大多数情况下,这已经是一个坏主意,当然, main()递归也是如此。

I see a practical reason to declare a static variable inside main (with most compilers on most desktop or laptop operating systems): a local variable inside main is consuming space on the call stack . 我看到在main内部声明static变量的实际原因(大多数台式机或笔记本电脑操作系统上的大多数编译器): main内部的局部变量占用了调用堆栈上的空间。 A static variable consumes space outside the call stack (generally in the .bss or .data segment, at least in ELF executables) static变量会占用调用堆栈外部的空间(通常在.bss.data段中,至少在ELF可执行文件中)

That would make a big difference if that variable takes a lot of space (think of an array of million integers). 如果该变量占用大量空间(想像一百万个整数的数组),那将有很大的不同。

The stack space is often limited (to one or a few megabytes) on current (desktop, laptop, tablet) systems. 在当前(台式机,笔记本电脑,平板电脑)系统上,堆栈空间通常受到限制(限制为一个或几个兆字节)。

On some embedded processors, the stack is limited to less than a kilobyte. 在某些嵌入式处理器上,堆栈限制为小于一千字节。

I think you have said it. 我想你已经说过了。 It one makes them static which for main() is not very interesting, second it makes them what I call local globals, it essentially puts them in .data with the globals (and not on the stack), but restricts their scope of access like a local. 它使它们成为静态的,这对于main()来说不是很有趣,其次使它们成为我所谓的局部全局变量,它本质上将它们与全局变量一起放在.data中(而不是放在堆栈上),但限制了它们的访问范围,例如本地人。 So for main() I guess the reason is to save some stack. 因此,对于main(),我想原因是要节省一些堆栈。 If you read around stack overflow though it seems like some compilers put a big stack frame on main() anyway, which most of the time doesnt make sense, so are you really saving any space? 如果您阅读有关堆栈溢出的内容,尽管看起来有些编译器仍然在main()上放置了一个较大的堆栈框架,那么大多数情况下这没有任何意义,那么您真的节省了空间吗? Likewise if they are on the stack from main unless you recursively call main they are not taking any more or less space being in .data vs the stack. 同样,如果它们是从main到堆栈的,除非您递归调用main,它们不会占用.data与堆栈中的空间。 if they are optimized into registers, then you still burn the .data where you wouldnt have burned the stack space, so it could cost you a little space. 如果将它们优化为寄存器,那么您仍然会在.data上烧掉您本来不会烧掉堆栈空间的位置,因此可能会花费一些空间。

The result of trying to indirectly access an object defined with automatic storage duration, ie a local variable, in a different thread from which the object is associated with, is implementation defined (see: 6.2.4 Storage duration of objects, p5). 尝试间接访问以自动存储持续时间定义的对象(即局部变量)的结果是在实现中定义的(请参见:6.2.4对象的存储持续时间,p5),该对象与该对象处于不同的线程。

If you want your code to be portable you should only share with threads objects defined with static, thread, or allocated storage duration. 如果希望代码具有可移植性,则应仅与使用静态,线程或分配的存储持续时间定义的线程对象共享。

In this example, the automatic object defined in the main should have been defined with static: 在这个例子中,main中定义的自动object应该已经用static定义了:

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

As already mentioned, the main reason would be to save stack space and also give you a better idea of the actual stack size need of your program. 如前所述,主要原因是可以节省堆栈空间,还可以使您更好地了解程序的实际堆栈大小。

Another reason to declare such variables static would be program safety. 声明此类变量为static另一个原因是程序安全。 There are various embedded system design rules that need to be considered here: 这里需要考虑各种嵌入式系统设计规则:

The stack should always be memory-mapped so that it grows towards invalid memory and not towards .bss or .data . 堆栈应该始终是内存映射的,以便它向无效的内存而不是.bss.data增长。 That means that in case of stack overflow, there is a chance for the program to raise an exception, rather than going Toyota all over your static variables. 这意味着在堆栈溢出的情况下,程序有机会引发异常,而不是遍历Toyota的所有静态变量。

For similar reasons, you should avoid declaring large chunks of data on the stack, as that makes the code more prone to stack overflows. 出于类似的原因,您应该避免在堆栈上声明大块数据,因为这会使代码更容易出现堆栈溢出。 This applies particularly to small, memory-constrained microcontroller systems. 这尤其适用于受存储器限制的小型微控制器系统。


Another possible, but rather uncommon case is that of main() calling itself recursively 另一个可能但不太常见的情况是main()递归调用自身的情况

That's nonsense, no sane person would ever write such code. 那是胡说八道,没有理智的人会写这样的代码。 Using recursion in an embedded system is very questionable practice and usually banned by coding standards. 在嵌入式系统中使用递归是非常可疑的做法,通常会被编码标准所禁止。 Truth is, there are very few cases where recursion makes sense to use in any program, embedded or not. 事实是,在很少的情况下, 无论是否嵌入,在任何程序中可以使用递归。

Once reason is that the offset of a static variable in the executable file is determined at link time and can be relied upon to be at the same place. 原因之一是可执行文件中静态变量的偏移量是在链接时确定的,因此可以将其放在同一位置。

Therefore, a useful purpose of a static variable at the main level is to include program version data as readable strings or binary readable data that later can be used to analyze/organize executables without needing to have the original source code or any program-specific utilities, other than a hex dump program. 因此,主变量静态变量的一个有用目的是将程序版本数据包括为可读字符串或二进制可读数据,这些数据以后可用于分析/组织可执行文件,而无需原始源代码或任何特定于程序的实用程序,而不是十六进制转储程序。 We used this hack back in the day when deploying programs where the target system could not be relied upon to have any development tools. 我们在部署程序时就使用了这种技巧,因为这些程序无法依赖目标系统具有任何开发工具。

For example, 例如,

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;
}

Now the version of the program can be determined without running it: 现在无需运行程序即可确定程序的版本:

$ od -c -x hello-world | $ od -c -x hello-world | grep "VER" grep“ VER”

0010040 001 002 VERGCC : ( U bunt 0010040 001 002 VERGCC:(bun

Actually, there's an enormous reason! 实际上,有一个很大的原因!

If you declare a variable as static within main() (or, any function ...), you are declaring that this local variable is also static . 如果main() (或任何函数...)中将变量声明为static ,则说明此局部变量也是static

What this means is that, if this function calls itself ... (which main() probably wouldn't do, although it certainly could ...) ... each recursive instance would see the same value, because this variable isn't being allocated on the stack. 这意味着,如果此函数调用自身……(虽然肯定可以,但main()可能不会这样做)……每个递归实例将看到相同的值,因为此变量是“ t被分配在堆栈上。 (Because, ummm, "it is static !") (因为,嗯,“这是static !”)

"Stack size" is not a valid reason to use static. “堆栈大小” 不是使用static.的有效理由static. (In fact, it has nothing to do with it.) If you're concerned about having room "on the stack" to store something, the correct thing to do is to "store it in the heap, instead," using a pointer variable. (实际上,它与它无关。)如果您担心在“堆栈上”有空间来存储某些内容,则正确的做法是使用指针 “将其存储在堆中”变量。 (Which, like any variable, could be static, or not.) (与任何变量一样,它可以是静态的,也可以不是。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM