C中的第一个可执行语句
Posted
技术标签:
【中文标题】C中的第一个可执行语句【英文标题】:First executable statement in C 【发布时间】:2016-10-02 05:43:30 【问题描述】:main 真的是 C 程序中的第一个函数或第一个可执行语句吗?如果有一个全局变量int a=0;
呢?
我一直被教导 main 是程序的起点。但是,分配了一些值并且在我看来是可执行语句的全局变量呢?
【问题讨论】:
您需要了解/阅读将被初始化的事物与与执行本身有关的事物之间的区别。删除 main 看看会发生什么 无论如何,OP得到了很多好的答案,但问题显然只是关于他的全局变量和主要功能。哪个会先被执行。 【参考方案1】:静态存储持续时间的全局变量和一般对象在概念上被初始化在程序执行之前。
C11 (N1570) 5.1.2/1 执行环境:
所有具有静态存储持续时间的对象都应被初始化(设置为 它们的初始值)在程序启动之前。
给定一个托管环境,函数main
被指定为一个必需的入口点,程序从这里开始执行。它可能是以下两种形式之一:
int main(void)
int main(int argc, char* argv[])
其中参数的名称不需要与上面相同(这只是一个约定)。
对于独立的环境入口点是实现定义的,这就是为什么您有时会在嵌入式设备的 C 实现中遇到void main()
或任何不同的形式。
C11 (N1570) 5.1.2.1/1 独立环境:
在独立的环境中(C 程序的执行可能需要 没有任何操作系统好处的地方),名称和类型 程序启动时调用的函数有多少是实现定义的。
【讨论】:
我对吗? main 始终是起点,改变这一点的唯一可能性是编写自己的编译器? @KevinWallis C 标准将在“托管”环境中执行程序的起点定义为main()
函数。如果程序和主机符合标准,这就是在这样的环境中开始执行的地方。然而,事实上,一些现有的编译器/链接器确实具有指定不同入口点的选项。例如,GNU 工具链提供了这样一个替代方案。使用它会使程序在这方面不合格,但这并不意味着它不会工作。【参考方案2】:
main
不是程序的起点。程序的起点是程序的入口点,这在大多数情况下对 C 程序员来说是透明的。通常它用 _start 符号表示,并定义在用汇编编写的启动代码或预编译到 C 运行时初始化库中(如 crt0.o
)。它负责对您所接受的内容进行低级初始化,例如将未初始化的静态变量初始化为零。完成后,它会调用一个预定义的符号main
,也就是你知道的main
。
【讨论】:
谢谢。我想了解更多关于 crt0.o 的信息。您能否提供有关此主题的任何链接或有用的网站? 有一个链接,如果你仔细看 :) 你可能还想检查一个替代启动文件,如 here【参考方案3】:但是在我看来,全局变量分配了一些值并且是一个可执行语句
你的意见是错误的。
在全局上下文中,只能存在具有显式初始化的变量定义。所有可执行语句(即赋值)都必须驻留在函数中。
详细地说,在全球范围内,你不能有这样的陈述
int globalVar;
globalVar = 0; //error, assignement statement should be inside a function
然而,上面的内容在函数中是完全有效的,比如
int main()
int localVar;
localVar = 0; //assignment is valid here.
关于初始化,比如
int globalVar = 0;
初始化发生在main()
开始之前,所以这本身并不是执行的真正部分。
详细说明全局变量的初始化场景,引用C11
,第6.2章,
如果声明标识符的声明符或类型说明符 出现在任何块或参数列表之外,标识符具有文件范围,其中 在翻译单元的末尾终止。
对于飞行范围变量,
如果 对象标识符的声明具有文件范围且没有存储类说明符, 它的链接是外部的。
对于具有外部链接的对象,
一个对象,其标识符在没有存储类说明符的情况下声明
_Thread_local
,并且可以使用 外部或内部链接 或存储类 说明符static
,具有静态存储持续时间。它的生命周期是整个执行 程序及其存储的值仅在程序启动之前初始化一次。
【讨论】:
如果我有一个由int i = function();
之类的函数调用初始化的全局变量怎么办?
@CarloCe 这不会在 C 中编译,其中所有初始化程序都必须是常量表达式。
很遗憾投票者不发表评论,从而剥夺了其他人的一些学习机会。
那是初始化,不是执行。
@Vrajendrav.t。全局 int i = 0;
在 C 中不执行任何代码!初始化发生在编译时。【参考方案4】:
在一个理论上的、纯 C 标准的程序中,它是。
在实践中,它通常涉及更多。
在 Linux 上,AFAIK 内核将您的链接映像加载到保留的地址空间中,并首先调用可执行映像指定的动态链接器(除非可执行文件是静态编译的,在这种情况下没有动态链接部分)。
动态链接器可以加载依赖库,例如 C 库。
这些库可以注册自己的启动代码,你也可以(在gcc
上主要通过__attribute__((constructorr))
)。
(C++ 尤其需要用户提供的初始化代码,您需要在具有构造函数的 C++ 全局变量上运行一些启动代码。)
然后链接器调用图像的入口点,默认情况下为_start(如果您想深入挖掘,链接器允许您选择不同的名称)默认情况下由C
库提供。 _start
初始化 C 库并通过调用 main
继续。
在任何情况下,简单的全局初始化(例如 int x = 42;
)都应该被编译并链接到您的可执行文件中,然后由操作系统(而不是您的代码)一次性加载,作为加载进程映像的一部分,因此没有此类变量需要用户提供的初始化代码。
【讨论】:
【参考方案5】:如果您使用 turbo c watch,您会发现第一个全局被声明,然后在编译时数据段(为全局和静态变量提供内存)的 main 开始执行被初始化为 0。 所以虽然赋值是不可能的,但声明发生在编译时。
【讨论】:
你确定给全局和静态变量分配内存是在编译时?【参考方案6】:是的,当你声明一个变量时,内存是在编译时分配给它的,除非你不使用堆段(将内存分配给指针),即在运行时发生的动态分配。但是由于全局从 RAM 变量的数据段部分获取内存,因此在编译时分配了内存。 希望这会有所帮助。
【讨论】:
以上是关于C中的第一个可执行语句的主要内容,如果未能解决你的问题,请参考以下文章
什么是正确的 nginx 位置规则以仅将 URI 中的第一个路径元素与 CGI 可执行文件匹配?
我建立的sql列表出现'CREATE VIEW' 必须是查询批次中的第一个语句。