底层视角看C语言

news/2024/11/13 17:56:16/

文章目录

  • main函数很普通
    • main函数之前调用了什么
    • main函数和自定义函数的对比
  • 变量名只为人而存在
  • goto是循环的本质
  • 指针变量
    • 指针是一个特殊的数字
    • 数组和指针
      • 数组越界问题
        • 低端地址越界
        • 高端地址越界
    • 引用就是指针

main函数很普通

main函数是第一个被调用的函数吗?在用户视角看来main函数的确程序的入口,但是在CPU视角下,main函数仅仅只是一个普通函数,和用户自定义的其他函数没有任何的区别。

main函数之前调用了什么

Linux环境:

_start->__libc_start_main->main

在这里插入图片描述

每一个Linux进程的入口函数都是_start,_start是一段直接由汇编语言编写的函数,它负责的工作就是把程序的命令行参数以及环境变量压入栈中,此时环境变量和参数一起存放在一个数组中

为了把环境变量单独提取出来,_start紧接着会调用__lib_start_main函数构建一张环境变量表,并进行一些全局变量的初始化工作,随后再进入main函数执行用户程序,再main函数退出时进行收尾操作例如全局变量的释放。这么一来,main函数似乎也只不过是一个被调用的函数,它只是默认被注册为用户代码的入口而已,也就是说,用户代码入口不一定非要是main函数

main函数和自定义函数的对比

一直以来我们编写C/C++程序时都是约定俗成地添加一个main函数来启动程序(因为不这么做往往会报错),这种情况一度让不少人认为main函数具有特殊的地位,能够得到CPU的青睐,其实不然,CPU眼中main函数啥也不是就是很普通的函数

int main(){return 0;
}
int func(){return 0;
}

通过汇编观察main和func的区别,会发现它们所对应的汇编指令居然完全一致

main:push rbpmov rbp, rspmov eax, 0pop rbpret
func:push rbpmov rbp, rspmov eax, 0pop rbpret

2个函数所做的操作都是一样的

  1. 建立函数栈帧 push rbp / mov rbp,rsp
  2. 将返回值拷入寄存器 mov eax,0
  3. 释放函数栈帧并返回 pop rbp / ret

gcc有一个命令可以改变用户代码的入口,使得用户指定其他函数作为程序起点

gcc -nostartfiles -efunc test.c

意思是编译test.c不使用系统的标准启动文件,将程序起点设置为func函数;一般不推荐这么做,因为使用标准启动文件代表着你需要自己为func瞻前顾后,这无疑是在自找麻烦

变量名只为人而存在

变量对程序员来说并不陌生,我们无时无刻都在使用变量帮助我们记忆,因为一个好的变量名可以大大提高源代码的可读性;尽管如此,对于可执行文件来说它并不需要存储所谓的变量名(release模式编译链接),CPU只需要知道一个逻辑地址就可进行读写操作,也就是说在发布模式编译链接时所有的变量名都会被转换称逻辑地址。

因此,我们可以给出关于变量的定义:=变量就是逻辑地址的一个别名,它向上以字符串形式以供人阅读记忆,向下被转成地址值供CPU访存

int a=0;
int main(){a=2;return 0;
}

所对应的汇编文件

main:push   rbpmov    rbp,rspmov    DWORD PTR [rip+0x0],0x2        # e <main+0xe>//将立即数0x2写入到rip值偏移量为0的位置,DWORD PTR标识4字节mov    eax,0x0pop    rbpret

可以看出a=2这条代码所对应的汇编mov DWORD PTR [rip+0x0],0x2,CPU只需要通过几个逻辑地址相对寻址就可以确定内存的哪个位置需要被赋值为2

goto是循环的本质

虽然说不鼓励在编写C/C++程序时随意的使用goto,但是不代表goto不值得探究,早期的循环其实都是通过goto语句来实现的,只不过随着程序越来越大,过多的goto语句打破了程序的结构性使得源码难以维护,进而衍生出了结构性更强的for、while、do语句,它们都是在底层实现上都继承的goto的机制

void test_for(){for(int i=0;i<10;++i){}
}
void test_while(){int i=0;

http://www.ppmy.cn/news/1546379.html

相关文章

nacos集群AP架构源码解析

1 心跳检测 核心类&#xff1a;ClientBeatCheckTask 核心方法&#xff1a;run public void run() {try {//1 集群状态下心跳处理if (!getDistroMapper().responsible(service.getName())) {return;}if (!getSwitchDomain().isHealthCheckEnabled()) {return;}List<Instance…

第十一天 线性代数基础

线性代数是数学的一个分支&#xff0c;广泛应用于计算机科学、物理学、工程学等领域。Python 提供了一些强大的库来进行线性代数计算&#xff0c;其中最著名的是 NumPy 和 SciPy。下面是一些线性代数的基础概念和如何在 Python 中使用这些库的示例。 线性代数是数学的一个分支…

05 SQL炼金术:深入探索与实战优化

文章目录 SQL炼金术&#xff1a;深入探索与实战优化一、SQL解析与执行计划1.1 获取执行计划1.2 解读执行计划 二、统计信息与执行上下文2.1 收集统计信息2.2 执行上下文 三、SQL优化工具与实战3.1 SQL Profile3.2 Hint3.3 Plan Baselines3.4 实战优化示例 SQL炼金术&#xff1a…

R和MATLAB及Python混合效应模型

R片段 使用 R 进行混合效应模型的分析是一种强有力的方法&#xff0c;尤其在研究带有嵌套或重复测量的复杂数据结构时。混合效应模型能够有效地检测和分析多层级数据中的固定效应和随机效应&#xff0c;这在研究选择性扰动效应时尤为有用。 R 中常用的软件包 lme4&#xff1…

Android 开发指南:初学者入门

Android 是全球最受欢迎的移动操作系统之一&#xff0c;为开发者提供了丰富的工具和资源来创建各种类型的应用程序。本文将为你提供一个全面的入门指南&#xff0c;帮助你从零开始学习 Android 开发。 目录 1. 了解 Android 平台[1]2. 设置开发环境[2]3. 学习基础知识[3]4. 创…

快速入门Zookeeper

Zookeeper ZooKeeper作为一个强大的开源分布式协调服务&#xff0c;扮演着分布式系统中至关重要的角色。它提供了一个中心化的服务&#xff0c;用于维护配置信息、命名、提供分布式同步以及提供组服务等。通过其高性能和可靠的特性&#xff0c;ZooKeeper能够确保在复杂的分布式…

标题:网络安全:数字时代的守护盾

标题&#xff1a;网络安全&#xff1a;数字时代的守护盾 在21世纪的今天&#xff0c;随着信息技术的飞速发展&#xff0c;网络已成为我们生活、工作和学习中不可或缺的一部分。它像一张无形的网&#xff0c;将世界各地的人们紧密相连&#xff0c;极大地促进了信息的交流与共享…

鸿蒙ArkTS和TS有什么区别?

Ark是强类型&#xff0c;打包编译的时候会保留类型。提高程序运行效率。 TS 支持类型校验&#xff0c;但是并不严格&#xff0c;支持any、unknown 这些不明确的类型。编译后其实jsd.ts,js文件不保留类型。 ArkTS基本禁用了动态类型 比如对象不能随意添加或者删除某些属性&am…