main函数执行以前

现在最重要的是要跟得上潮流,所以套用比较时髦的话,谁动了我的奶酪。谁调用了我的 main?不过作为计算机工作者,我劝大家还是不要赶时髦,今天Java热,明天 .net 流行,什么时髦就学什么。我的意思是先花几年把基本功学好,等你赶时髦的时候也好事半功倍。废话不多说了。
    
    我们都听说过一句话:“main是C语言的入口”。我至今不明白为什么这么说。就好像如果有人说:“挣钱是泡妞”,肯定无数砖头拍过来。这句话应该是“挣钱是泡妞的一个条件,只不过这个条件特别重要”。那么上面那句话应该是 “main是C语言中一个符号,只不过这个符号比较特别。”
    
    我们看下面的例子:
    
    /* file name test00.c */
    
    int main(int argc, char* argv)
    {
     return 0;
    }
    
    编译链接它:
    cc test00.c -o test.exe
    会生成 test.exe
    
    但是我们加上这个选项: -nostdlib (不链接标准库)
    cc test00.c -nostdlib -o test.exe
    链接器会报错:
    undefined symbol: __start
    
    也就是说:
    1. 编译器缺省是找 __start 符号,而不是 main
    2. __start 这个符号是程序的起始点
    3. main 是被标准库调用的一个符号
    
    再来思考一个问题:
    我们写程序,比如一个模块,通常要有 initialize 和 de-initialize,但是我们写 C 程序的时候为什么有些模块没有这两个过程么呢?比如我们程序从 main 开始就可以 malloc,free,但是我们在 main 里面却没有初始化堆。再比如在 main 里面可以直接 printf,可是我们并没有打开标准输出文件啊。(不知道什么是 stdin,stdout,stderr 以及 printf 和 stdout 关系的群众请先看看 C 语言中文件的概念)。
    
    有人说,这些东西不需要初始化。如果您真得这么想,请您不要再往下看了,我个人认为计算机软件不适合您。
    
    聪明的人民群众会想,一定是在 main 之前干了些什么。使这些函数可以直接调用而不用初始化。通常,我们会在编译器的环境中找到一个名字类似于 crt0.o 的文件,这个文件中包含了我们刚才所说的 __start 符号。(crt 大概是 C Runtime 的缩写,请大家帮助确认一下。)
    
    那么真正的 crt0.s 是什么样子呢?下面我们给出部分伪代码:
    
    ///////////////////////////////////////////////////////
    section .text:
    __start:
    
     :
     init stack;
     init heap;
     open stdin;
     open stdout;
     open stderr;
     :
     push argv;
     push argc;
     call _main; (调用 main)
     :
     destory heap;
     close stdin;
     close stdout;
     close stderr;
     :
     call __exit;
    ////////////////////////////////////////////////////
    
    实际上可能还有很多初始化工作,因为都是和操作系统相关的,笔者就不一一列出了。
    
    注意:
    1. 不同的编译器,不一定缺省得符号都是 __start。
    2. 汇编里面的 _main 就是 C 语言里面的 main,是因为汇编器和C编译器对符号的命名有差异(通常是差一个下划线'_')。
    3. 目前操作系统结构有两个主要的分支:微内核和宏内核。微内核的优点是,结构清晰,简单,内核组件较少,便于维护;缺点是,进程间通信较多,程序频繁进出内核,效率较低。宏内核正好相反。我说这个是什么目的是:没办法保证每个组件都在用户空间(标准库函数)中初始化,有些组件确实可能不要初始化,操作系统在创建进程的时候在内核空间做的。这依赖于操作系统的具体实现,比如堆,宏内核结构可能在内核初始化,微内核结构在用户空间;即使同样是微内核,这个东东也可能会被拿到内核空间初始化。
    
    随着 CPU 技术的发展,存储量的迅速扩展,代码复杂程度的增加,微内核被越来越多的采用。你会为了 10% 的效率使代码复杂度增加么?要知道每隔 18 个月 CPU 的速度就会翻一番。所以我对程序员的要求是,我首先不要你的代码效率高,我首先要你的代码能让 80% 的人迅速看懂并可以维护。

总结:

main函数执行之前,主要就是初始化系统相关资源:

1.设置栈指针

2.初始化static静态和global全局变量,即data段的内容

3.将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容

4.运行全局构造器,估计是C++中构造函数之类的吧

5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数
<think>好的,我需要回答用户提出的五个问题,每个问题都需要仔细分析和组织语言,确保内容准确且符合中文表达习惯。现在逐个来看: 第一个问题:main函数执行前会执行什么代码?我记得在C/C++中,程序启动时,操作系统会先初始化环境,比如设置堆栈,加载全局变量和静态变量,然后运行全局对象的构造函数,最后才调用main函数。可能还需要提到运行时库的初始化,比如CRT(C运行时库)的处理,比如参数准备、环境变量获取等。 第二个问题:内存分配方式及其区别。这里应该包括四种分配方式:静态存储区、栈、堆和内存映射区域。需要解释每种方式的特点,比如静态存储区用于全局变量和静态变量,栈由编译器自动分配释放,堆需要手动申请和释放,内存映射用于文件映射等。还要比较它们的分配效率、生存期、大小限制等区别。 第三个问题:BOOL、int、float、指针变量与零的比较语句。这里要注意不同数据类型的比较方式,比如BOOL类型可能用FALSE或0,但有些平台可能用true/false关键字。int直接比较是否等于0,float由于精度问题需要用范围比较,指针则与NULL或nullptr比较。需要确认不同语言的情况,但用户可能指的是C/C++。 第四个问题:const与#define的优点。应该提到类型检查、作用域限制、调试友好性、避免宏副作用等。比如#define只是文本替换,而const有类型,且编译器会进行类型检查,还能避免重复计算。 第五个问题:数组与指针的区别。需要说明数组是连续内存空间,大小固定,而指针是变量,存储地址。sizeof的结果不同,数组名为首地址但不可修改,而指针可以指向其他地址。此外,函数传参时数组退化为指针等。 现在需要逐一整理这些点,确保每个回答结构清晰,符合用户要求的分点回答,并且语法正确,使用中文。同时注意数学公式和代码部分用正确的格式,比如行内公式用$...$,独立公式用$$...$$。不过当前问题中可能涉及代码的部分比如变量比较,需要用代码块格式。但用户提供的系统级指令中提到的是数学表达式用LaTeX格式,其他可能需要正常代码标记。但用户的问题中没有数学公式,主要是代码示例,所以可能需要在回答中使用代码块,但用户没有明确允许,可能需要按用户给的格式来处理。比如变量比较的代码可能需要用反引号或代码块。但用户的问题中的例子用了类似$(a+b)^2$的数学表达式,但实际代码比如比较语句可能属于代码部分,可能需要使用不同的格式。不过根据用户的系统指令,行内数学表达式用$...$,而代码部分是否适用此规则呢?可能用户希望代码部分用代码块,但这里可能需要根据问题调整。可能用户的问题中的代码部分需要用行内代码,用反引号,但系统指令中没有明确说明。但根据用户给出的例子,比如变量名可能用$符号包裹,但代码语句可能不需要。这里可能存在歧义,但根据用户给出的例子,如变量a的比较可能需要用代码格式,比如: 对于BOOL类型,与零比较的语句可能是`if (a == FALSE)`,但根据系统指令,可能需要用行内数学表达式格式,但代码可能更适合用代码块。不过用户提供的系统指令主要针对数学表达式,而代码可能属于普通文本。因此,可能需要将代码用反引号包裹,或者直接写出。需要判断用户的要求,但根据系统指令,可能不需要特别处理代码,但用户的问题中要求写出比较语句,所以这部分需要正确展示代码。 现在开始组织每个问题的回答,确保每个问题分点清晰,结构合理,符合用户的要求。</think>以下是对您问题的分步解答: 一、main函数执行执行的代码 $$程序启动流程示意图:操作系统加载→运行时初始化→全局变量初始化→main()$$ 1. 运行时环境初始化:由C Runtime Library负责 - 堆栈指针初始化 - 静态存储区初始化 - 参数和环境变量准备 2. 全局/静态对象构造 - 全局变量内存分配 - 调用静态对象的构造函数(C++特性) 3. 平台相关初始化 - Windows系统的`WinMainCRTStartup` - Linux的`_start`函数 二、内存分配方式对比表 | 分配方式 | 分配位置 | 生命周期 | 管理方式 | 容量限制 | |--------------|----------------|----------------|----------------|----------------| | 静态存储区 | 数据段 | 程序整个周期 | 编译器管理 | 编译时确定 | | 栈内存 | 函数栈帧 | 作用域内 | 自动分配释放 | 系统栈大小限制 | | 堆内存 | 动态内存区 | 手动控制 | 程序员管理 | 系统可用内存 | | 内存映射区 | 虚拟地址空间 | 文件映射周期 | 系统管理 | 磁盘文件大小 | 三、变量与零值的比较语句 ```c // BOOL类型(需包含<stdbool.h>) if(a == false) // int类型 if(a == 0) // float类型(考虑浮点精度) if(fabs(a) <= FLT_EPSILON) // 指针类型(C++11推荐用法) if(a == nullptr) ``` 四、const相比#define的优势 $$类型安全优势:const T vs #define T value$$ 1. 类型检查机制 - 编译器会对const常量进行类型检查 - #define只是文本替换,无类型安全 2. 作用域控制 - const常量遵循作用域规则 - #define无作用域概念,容易污染命名空间 3. 调试支持 - const变量有符号信息,可调试 - #define替换后丢失符号信息 4. 内存效率 - const常量可能进入符号表(不分配存储空间) - #define产生多个字面量副本 五、数组与指针的核心差异 $$内存关系示意图:数组→连续存储块 vs 指针→地址变量$$ 1. 本质属性 - 数组:编译器分配的连续内存块 - 指针:存储地址的变量 2. 空间特性 - `sizeof(数组)`返回总字节数 - `sizeof(指针)`返回地址长度(通常4/8字节) 3. 可修改性 - 数组名是常量指针,不可重新指向 - 指针变量可修改指向地址 4. 函数传参 - 数组参数退化为指针 - 指针参数保持地址传递特性 5. 初始化方式 ```c int arr[] = {1,2,3}; // 合法数组初始化 int *ptr = arr; // 合法指针赋值 ``` 以上内容均基于C/C++语言规范,不同编程语言实现细节可能有所差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值