[大师C语言(第二篇)]C语言main函数背后的秘密

news/2024/5/20 7:05:43

C语言main函数背后的秘密(一)

main函数是每个C程序执行的入口点,但main函数是如何被调用的?它背后的技术原理是什么?本文将深入探讨main函数的调用过程及其背后的技术原理。

main函数的定义

在C语言中,main函数是程序执行的入口点。根据C语言标准,main函数可以定义为两种形式:

  1. int main(void):没有参数的main函数。
  2. int main(int argc, char *argv[]):带有参数的main函数,其中argc表示命令行参数的数量,argv是一个指向参数字符串数组的指针。

程序的启动过程

当我们在命令行中运行一个C程序时,例如./program arg1 arg2,操作系统的启动过程如下:

  1. 命令行解析:操作系统首先解析命令行,将命令行参数传递给程序。
  2. 程序加载:操作系统加载程序的可执行文件到内存中,并为程序分配资源。
  3. 环境初始化:操作系统初始化程序的环境,例如打开标准输入输出流等。
  4. 调用main函数:操作系统调用main函数,并将命令行参数传递给main函数。

示例代码

让我们通过一个简单的示例来演示main函数的调用过程。

#include <stdio.h>int main(int argc, char *argv[]) {printf("Hello, World!\n");for (int i = 0; i < argc; i++) {printf("Argument %d: %s\n", i, argv[i]);}return 0;
}

编译并运行该程序,可以得到以下输出:

$ ./example arg1 arg2
Hello, World!
Argument 0: ./example
Argument 1: arg1
Argument 2: arg2

从输出中可以看出,argc被设置为3,argv数组包含三个元素:程序的名称、arg1arg2

main函数的返回值

main函数的返回值用于表示程序的退出状态。通常情况下,返回0表示程序正常退出,非零值表示程序异常退出。在程序中,我们可以通过return语句来设置main函数的返回值。

return 0; // 程序正常退出

此外,我们也可以使用exit函数来终止程序,并设置退出状态。

#include <stdlib.h>exit(1); // 程序异常退出

总结

在本文的第一部分中,我们介绍了main函数的定义、程序的启动过程以及main函数的返回值。这些是理解main函数背后技术原理的基础知识。在下一部分中,我们将深入探讨操作系统中main函数的调用过程,包括程序加载、环境初始化等关键技术。

C语言main函数背后的秘密(二)

在第一部分中,我们探讨了main函数的基本定义和程序的启动过程。现在,让我们深入到操作系统的层面,了解main函数是如何被调用的,以及程序在执行main函数之前都经历了哪些步骤。

程序的加载和启动

当一个C程序被编译成可执行文件后,它需要被加载到内存中才能被执行。这个过程通常由操作系统的加载器(loader)完成,加载器负责将程序的可执行文件从磁盘加载到内存中,并准备好执行。

加载过程

  1. 读取可执行文件:加载器首先读取可执行文件的内容,这通常包括程序的代码段、数据段、堆栈等。

  2. 内存分配:加载器为程序分配内存空间,包括为代码段、数据段、堆栈等分配内存。

  3. 初始化静态变量:加载器负责初始化程序中的静态变量,将它们设置为指定的初始值。

  4. 设置执行环境:加载器设置程序执行所需的环境,例如初始化标准输入输出流、设置全局变量等。

  5. 跳转到入口点:最后,加载器将控制权转移到程序的入口点,通常是main函数的地址。

操作系统的角色

操作系统在程序启动过程中扮演着关键角色。它不仅负责加载程序,还负责管理程序的执行,包括处理程序的系统调用、信号、进程间通信等。

示例代码

为了更好地理解加载过程,让我们看一个简单的C程序,它包含了全局变量和main函数。

#include <stdio.h>int global_var = 42;int main() {printf("Global variable: %d\n", global_var);return 0;
}

当这个程序被编译和运行时,加载器会读取可执行文件,分配内存,初始化global_var,然后跳转到main函数开始执行。

环境初始化

在main函数执行之前,操作系统还会负责初始化程序的环境。这包括设置标准输入输出流(stdin、stdout、stderr)、环境变量、程序的工作目录等。

环境变量

环境变量是操作系统传递给程序的键值对信息,它们可以在程序中通过extern char **environ访问。例如,PATH环境变量定义了操作系统查找可执行文件的路径。

C运行时库

在main函数执行之前,C运行时库(CRT)也会被初始化。C运行时库提供了一系列的函数和服务,包括内存管理、I/O操作、字符串处理等。这些服务对于C程序的正常运行至关重要。

示例代码

以下代码展示了如何在C程序中访问环境变量:

#include <stdio.h>extern char **environ;int main() {for (char **env = environ; *env != NULL; env++) {printf("%s\n", *env);}return 0;
}

当这个程序运行时,它会打印出所有的环境变量。

总结

在本文的第二部分中,我们探讨了程序的加载和启动过程,包括加载器的角色、操作系统的参与以及环境初始化。这些步骤是main函数能够被执行的基础。在下一部分中,我们将讨论main函数执行之后的过程,包括程序的终止、资源清理和返回状态码的处理。

C语言main函数背后的秘密(三)

在前两部分中,我们探讨了main函数的定义、程序的加载和启动过程,以及环境初始化。现在,让我们来看看main函数执行完成后,程序会经历哪些步骤,以及如何正确地结束一个C程序的执行。

main函数的返回

当main函数执行完成后,它通常会返回一个整数值,这个值被用作程序的退出状态码。在C语言中,返回0通常表示程序成功执行,而非零值表示程序执行过程中出现了错误或异常情况。

int main() {// 程序代码return 0; // 表示程序成功执行
}

如果main函数中没有显式地返回一个值,C语言标准规定编译器应该隐式地添加一个返回0的语句,这意味着程序默认情况下会成功退出。

程序的终止

程序的终止并不仅仅是main函数返回那么简单。在程序终止时,还会发生一系列的清理工作,包括释放分配的资源、关闭打开的文件、执行注册的退出处理函数等。

清理工作

  1. 执行_atexit注册的函数:C语言提供了atexit函数,允许程序注册在程序正常终止时执行的函数。这些函数按照注册的反顺序被调用,用于执行清理任务,如释放资源、关闭文件等。
#include <stdlib.h>void cleanup() {// 清理代码
}int main() {atexit(cleanup);// 程序代码return 0;
}
  1. 清理静态局部变量:在程序终止时,所有静态局部变量的生命周期结束,它们所占用的内存将被自动释放。

  2. 关闭标准流:标准输入输出流(stdin、stdout、stderr)会被关闭,这通常是由C运行时库自动完成的。

  3. 资源释放:C运行时库会释放程序运行过程中分配的所有资源,包括堆内存、文件描述符等。

程序的退出状态码

程序的退出状态码是main函数返回值的一个整数,它传递给操作系统的父进程(通常是命令行解释器)。退出状态码可以被父进程用来判断子进程是否正常终止。

示例代码

以下代码展示了如何使用退出状态码来表示程序的不同退出情况:

#include <stdio.h>
#include <stdlib.h>int main() {// 程序代码if (/* 出现错误 */) {printf("Error occurred.\n");return 1; // 表示错误退出}printf("Program completed successfully.\n");return 0; // 表示成功退出
}

在这个例子中,如果程序中出现错误,main函数将返回1,否则返回0。

总结

在本文的第三部分中,我们探讨了main函数返回后程序经历的步骤,包括执行_atexit注册的函数、清理静态局部变量、关闭标准流和资源释放。我们还讨论了程序的退出状态码,它是一个重要的机制,用于传递程序执行结果给操作系统和父进程。

通过这三部分的探讨,我们现在对main函数背后的技术原理有了更深入的理解。从程序的加载和启动,到main函数的执行,再到程序的终止和资源清理,每一步都是确保C程序能够正常运行的关键。了解这些细节,对于编写健壮和高效的C程序至关重要。


http://www.mrgr.cn/p/06711064

相关文章

两个手机在一起ip地址一样吗?两个手机是不是两个ip地址

在数字时代的浩瀚海洋中&#xff0c;手机已经成为我们生活中不可或缺的一部分。随着移动互联网的飞速发展&#xff0c;IP地址成为了连接手机与互联网的桥梁。那么&#xff0c;两个手机在一起IP地址一样吗&#xff1f;两个手机是不是两个IP地址&#xff1f;本文将带您一探究竟&a…

【快速入门Linux】10_Linux命令—Vi编辑器

文章目录 一、vi 简介1.1 vi1.2 vim1.3查询软连接命令&#xff08;知道&#xff09; 二、打开和新建文件&#xff08;重点&#xff09;2.1 打开文件并且定位行2.2 异常处理 三、vi三种工作模式&#xff08;重点&#xff09;3.1 末行模式-命令 四、常用命令4.0 命令线路图4.1 移…

图像涂哪就动哪!Gen-2新功能“神笔马良”爆火,网友:急急急

AI搞视频生成&#xff0c;已经进化到这个程度了&#xff1f;&#xff01; 对着一张照片随手一刷&#xff0c;就能让被选中的目标动起来&#xff01; 明明是一辆静止的卡车&#xff0c;一刷就跑了起来&#xff0c;连光影都完美还原&#xff1a; 原本只是一张火灾照片&#xff0…

STM32快速入门(串口传输之USART)

STM32快速入门&#xff08;串口传输之USART&#xff09; 前言 USART串口传输能实现信息在设备之间的点对点传输&#xff0c;支持单工、半双工、全全双工&#xff0c;一般是有三个引脚&#xff1a;TX、RX、SW_RX&#xff08;共地&#xff09;。不需要一根线来同步时钟。最大优…

Hadoop3:集群搭建及常用命令与shell脚本整理(入门篇,从零开始搭建)

一、集群环境说明 1、用VMware安装3台Centos7.9虚拟机 2、虚拟机配置&#xff1a;2C&#xff0c;2G内存&#xff0c;50G存储 3、集群架构设计 从表格中&#xff0c;可以看出&#xff0c;Hadoop集群&#xff0c;主要有2个模块服务&#xff0c;一个是HDFS服务&#xff0c;一个是…

基于web的物流管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

特斯拉CEO马斯克访华,或加速FSD技术在中国的落地

特斯拉首席执行官埃隆马斯克于4月底进行了中国之旅&#xff0c;这一访问被业内人士认为可能加速特斯拉FSD&#xff08;Full Self-Drive&#xff0c;完全自动驾驶&#xff09;技术在中国的应用。业内专家指出&#xff0c;马斯克的此番到访可能会对中国自动驾驶市场产生深远影响&…

VMware如何将虚拟机的端口服务映射出去

我们有时候在VMware起了一个服务,想要局域网的朋友同事访问 这时候就需要i端口映射 选择NAT模式 VMnet8点击 NAT设置 然后点击添加然后映射传入端口对话框 红色部分是 你主机本机,也就是你在用的电脑的空闲端口(可以打开cmd 输入命令 : netstat -ano 查看已用端口都有哪些…

c++多线程2小时速成

简介 c多线程基础需要掌握这三个标准库的使用&#xff1a;std::thread,std::mutex, andstd::async。 1. Hello, world #include <iostream> #include <thread>void hello() { std::cout << "Hello Concurrent World!\n"; }int main() {std::th…

CSS 伪类、伪元素的应用实例:电池充电、高能进度条

一、目的 本文通过 CSS 伪类、伪元素&#xff0c;结合动画 animation 和 Vue 动态样式属性&#xff08;通过 CSS 变量&#xff09;的写法&#xff0c;来实现电池充电、高能进度条的效果&#xff0c;如下图所示。 二、基础知识 1、CSS 伪类、伪元素 简单概括成以下 4 点&#x…

亚马逊Amazon商品详情和关键词搜索API接口分享

一、亚马逊Amazon商品详情API接口 亚马逊商品详情API接口是亚马逊平台为开发者提供的一项重要服务&#xff0c;它允许开发者通过程序调用API来获取亚马逊商品的相关数据。这个接口为获取商品数据提供了便利的途径&#xff0c;有助于用户进行商品搜索、商品分类以及数据分析等操…

基于改进MFCC特征和卷积递归神经网络的心音分类

具体的软硬件实现点击http://mcu-ai.com/MCU-AI技术网页_MCU-AI人工智能 心音分类在心血管疾病的早期发现中起着至关重要的作用,特别是对于小型初级卫生保健诊所。尽管近年来心音分类取得了很大进展,但其中大多数都是基于传统的分段特征和基于浅层结构的分类器。这些传统的声…

2024最详细全面的发卡平台对比调研

最近在调研目前市面上的发卡平台&#xff0c;对一些主流的托管式发卡平台与github上开源的发卡项目做了横向对比&#xff0c;本文主要介绍各自特点以及需要注意避免的坑。 直接上表格&#xff0c;一目了然。 对比独角数卡***发卡/泛发卡平台iDataRiver发卡稳定性/跑路风险自己…

QT day2 作业

头文件 #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget> #include <QDebug> #include<QIcon> #include<QLabel> #include<QMovie> #include<QLineEdit> #include<QPushButton> QT_BEGIN_NAMESPACE namespace Ui { class …

ansible------inventory 主机清单

目录 inventory 中的变量 2&#xff09;组变量[webservers:vars] #表示为 webservers 组内所有主机定义变量&#xff0c;所有组内成 员都有效 ansible_userrootansible_passwordabc1234 3&#xff09; [all:vars…

Golang | Leetcode Golang题解之第71题简化路径

题目&#xff1a; 题解&#xff1a; func simplifyPath(path string) string {stack : []string{}for _, name : range strings.Split(path, "/") {if name ".." {if len(stack) > 0 {stack stack[:len(stack)-1]}} else if name ! "" &am…

Redis线程模型

文章目录 &#x1f496; Redis 单线程模型⭐ 单线程监听大量的客户端连接⭐ Redis 6.0 之前为什么不用多线程&#xff1f; &#x1f496; Redis多线程⭐ Redis 后台线程⭐ Redis 网络IO多线程 对于读写命令来说&#xff0c;Redis 一直是单线程模型。不过&#xff0c;在 Redis 4…

基于总线设备驱动模型的按键读取驱动程序

本次实验基于总线设备驱动模型实现按键驱动程序的编写,给上层应用程序提供检测按键是否按下的操作接口,上层应用根据按键是否按下控制led的亮灭。所以上层应用程序会同时使用led和按键的驱动接口,但是对于下层驱动而言,这二者是分离的,因此只需要专注于编写按键驱动程序就…

线~段~树

点击查看代码 #include<bits/stdc++.h> #define lson id<<1 #define rson id<<1|1 using namespace std; const int N=1e6+12000; struct node{int l,r,num;int ma,mi; }tr[N<<2]; int a[N]; int n,m; string str; int ans=0; int from,to; void build…

【功耗仪使用】

一&#xff0c;功耗仪使用 1.1&#xff0c;功耗仪外观及与手机&#xff0c;电脑连接方式 power monitor设备图 同时power monitor设备的后端有一个方形插孔通过数据线与电脑主机USB接口相连接&#xff0c;圆形插孔为电源插孔&#xff0c;用来给power monitor设备通电 pow…