史上最全最详细最好的精品C语言万字长文(上)---看完直接手撕所有八股面试考研期末

定义与声明

定义变量的本质是在内存中开辟一块空间,用于存放数据

一个实例化的变量=空间+数据

定义任何变量都需要在内存中开辟空间,因为运行程序需要先加载到内存,然后才会定义变量

函数的定义也是要在内存中开辟空间的 定义是要开空间的,定义只有一次(只能初始化一次),声明可以多次,只是告知,声明不开空间

初始化和赋值是不一样的。初始化是和定义变量在一起完成,表现在汇编代码上是一条语句,在定义出变量的时候,与生俱来的就完成初始化。而赋值是另外一条汇编代码

什么是实例化?形参实例化,类实例化,相当于胖子与张三的关系,先笼统的描述,再具体为确切的某一个东西

类型

类型的本质就是偏移量,所有的虚拟地址都是资源,定义变量和创建线程都是一种抢占虚拟地址资源的行为,取地址都是只能得到一个地址,只能得到它们的起始地址加偏移量,然后连续读取多少个字节就可以把这个对象访问到。类型就是给编译器看的,然后就会让寄存器开多大来识别对象。拥有虚拟地址的资源就是拥有通过页表映射的权限,就拥有更大的物理内存

生命周期和作用域:

局部变量(临时变量)只在代码块中有效,代码块就是{}之间

函数调用→栈帧→临时变量在栈帧中开空间→临时变量具有临时性,是因为变量所在的栈帧空间具有临时性→函数调用结束,栈帧销毁→临时变量也就被释放 生命周期和作用域都和代码块强相关,但是侧重含义不同,生命周期是存活时间,作用域是影响范围

变量是在内存上开辟的一块空间,局部变量出了作用域就销毁了,是类型+变量名+初始化的值。因此,局部变量不做初始化是随机值

定义不同类型的变量意味着要开辟不同大小的空间

在另外一个文件中创建的变量在这个文件中是不能使用的,需要声明。所有变量声明时,不能设置初始值。如下图不能写=100

左值与右值

任何数据的存放都是要开辟空间的,因此,数据的属性有两个。例:int a=10

变量名在不同的应用场景中代表不同的含义

左值:数据的空间,变量属性 a = 1 a为左值,表示整型变量

右值:数据的内容,数据属性 int b = a a为右值,表示1

头文件

#pragma once防止头文件被重复包含

包含头文件是库提供的,使用<>

包含头文件是自己写的,使用""

头文件中已经包含了库中的头文件,在使用时,就不用再次包含,直接包含自己写的头文件即可

如下图,就是一个标准的头文件,包含库头文件和变量声明和函数声明

多文件之间需要进行数据交互,使用头文件便于维护

头文件默认会在当前路径或者指定路径下寻找,不需要在编译选项中带

文件操作

对文件的基本操作包括读写

操作文件需要先将文件打开,然后完成读写,然后关闭文件

每次打开一个文件,都会创建一个FILE结构体,这个结构体是与被打开文件紧密关联的,FILE结构体中包含着关于文件的所有信息。用这个结构体的指针FILE*来标定这个被打开文件的结构体的地址,从而方便做管理

FILE* pf = fopen("文件路径","打开方式")

fopen是打开文件进行文件操作的第一步,这个函数中包含两个参数,第一个参数是字符串,内容是要打开的文件的路径,可以是绝对路径,也可以是相对路径,如果不写路径,只写文件名,即默认在当前进程所在路径下打开。第二个参数是打开方式,常用的有"r"读,"w"写(每次打开都会先清空文件),"a"追加(在已有文件内容的后面追加内容)。如果文件存在于这个路径中,就会返回这个被打开文件的结构体的地址,而如果在这个路径下并不存在这个文件,就会返回一个空指针。

fclose(FILE*)关闭文件,参数是一个文件指针,用以标识关闭特定的文件

fputc将字符输出到文件中,使用如下

fputs向文件输出字符串

fgetc从文件中读取字符,读取到末尾返回EOF并设置结束标志,读取错误返回EOF

fgets从文件中读取字符串

stdin stdout stderr和所有的文件指针一样,都属于文件流(FILE* stream)

应用示例:

fread fwrite读写文件。ptr:指向存储读取数据的内存缓冲区的指针。size:要读取的每个元素的大小(以字节为单位)。count:要读取的元素的数量。stream:指向已打开的文件的指针,文件必须以二进制模式打开(使用 "rb" 或 "wb" 等标志)。

fprintf 和printf用法相同,只不过第一个参数是文件流指针

文件缓冲区

EOF

EOF(End-of-File)是C语言中表示文件结束的标志符号,用于指示已到达文件的末尾或输入流的末尾。它通常被定义为-1,用于判断是否读取到了文件结束符。在输入和文件操作中,可以使用EOF来检测文件结束的状态。

那么当fgetc返回了EOF,如何判断其是读取到末尾了还是读取错误了?ferror()为真表示读取错误

feof()为真表示读取到结尾

检测EOF应该在一边读取一边检测,不能等到读取结束后再检测

static

通过头文件的包含,我们可以实现跨多文件之间的函数和变量的使用。

static修饰某一个全局变量,可以使该变量只能在该文件内使用,不能在文件外部被直接访问。

static修饰函数,只能在本文件使用,不能在文件外部被直接访问。但是可以封装访问,将static函数封装进一个函数,然后跨文件访问该函数,即可实现对跨文件static函数的访问。这就是封装思想。只对外暴露使用接口,不展示内部实现细节。

static修饰全局变量,改变了全局变量的作用域,不改变生命周期。是一种安全机制,将它限制在本文件中,对外部只暴露接口。函数也是同理,先定义,后使用,要使用需要先声明。

static修饰局部变量,会延长该变量的生命周期,将局部变量变成全局变量的生命周期,出了代码块也不销毁。但是不影响作用域,依然是只能在代码块内被访问。

理解全局数据区(数据段):包含静态数据区,已初始化全局数据区,未初始化全局数据区。全局生命周期是指在整个进程运行的过程中都是有效的。

sizeof

sizeof(a) sizeof(int) sizeof a 都是可以的。sizeof不是函数,是操作符。sizeof不仅可以求内置类型的大小,还可以求自定义类型的大小。

ASCII

在计算机中,数据的存储是0101二进制,是不能直接将字符存在计算机的,因此,将(常用符号英文字母)和数字一一建立映射关系,从而将字符转成数字,实现字符以二进制的形式在计算机上能够存储。1个字节8个比特位,2^7是128,因为是以正数表示,所以最高位是0,一共可以表示128个数字,0~127。英文字母比较少,127个数字映射字母+符号已经足够了。但是中文是由汉字组成,汉字与数字建立映射需要更多的表示方法。

unicode--万国码

Linux中一般是以UTF-8的方式

UTF-8 是使用最广的一种 Unicode 的实现方式。 其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示)。UTF-8 是 Unicode 的实现方式之一

utf-8 兼容ASCII。常见的汉字用2个字节表示,不常用的汉字用3个字节表示

某些场景下,会使用UTF-16编码,C++库中的String是模板,需要使用UTF-16时,只需要更改模板参数即可

乱码:代码写的方式与字符编码方式不符,在编码表中不能找到对应的字符,就会出现乱码

GBK--国标库,国人专门将汉字做了编码表

数据的存储-->整形和字符 整形转补码,字符转ASCII

整型的字面值读出来是原码形式,在计算机内部存储或在cpu计算都是以补码形式

任何数据在计算机中的存储都是以二进制形式

整型在内存中存储的是补码

整型或char等等数字运算时,只有字面值转二进制表示的是原码,截断可以用原码截断,一般是用补码截断,截断也要保留符号位。要进行计算一律使用32比特位补码,要进行原码反码补码转换,一律使用32比特位补码,不够32位的,先整形提升到32位,再进行原反补转换

有符号数:

整数在内存中的存储是符号位(正数为0,负数为1)+数据位

正数的原码反码补码相同,负数的原码第一位是1,反码是原码的符号位不变,其他位按位取反。补码是反码+1

二进制转十六进制,四位二进制转一位十六进制数

补码→原码 先取反,再+1

无符号数:

没有符号位。原码=反码=补码

unsigned int b = -10 的意义 以%d来取,就是-10,以%u来取,就是巨大的数 。而内存中存着的数据一直就只有那一份补码 ,不同类型来取代表不同意义

sizet i = -1 的含义是最大值,因为内存中存的是补码,正数原反补相同,负数的补码是取反+1,因此-1的原码是1000000000000000000000000000001,取反+1就是全是1,sizet是无符号整型,所以32位1都是有效位,表示最大值

数据在存储的时候,以数据的二进制补码先存下来。之后在读取的时候,是根据类型读取的。也就是说,单纯的数是没有意义的,必须带上前面的类型才有意义,才能表示出这个数据的意义。

先把空间开辟出来,然后把数据转化完二进制,再放到空间中

存的时候,不关心变量类型,只是将数据转成二进制补码存储(只看数字),单纯的放到开辟的内存空间中。存字面数据必须先转成补码,在放入空间当中。所谓符号位,完全看数据本身是否携带+-号,和变量是否有符号无关!

取的时候,根据变量类型取值,有符号位,就取补码转反码转原码,没有符号位就是原反补相同。取数据一定要先看变量本身类型,然后才决定要不要看最高符号位。如果不需要,直接二进制转成十进制。如果需要,则需要转成原码,然后才能识别。(当然,最高符号位在哪里,又要明确大小端)

因此,同一份内存中的补码,在不同类型的取时,表现为不同的值

大小端

所有的数据都是在内存中存储的

大小端存储本质是按照字节为单位数据和空间的对应关系

在数据层面,数据按照字节为单位,有高权值位和低权值位之分。

在空间层面,每个字节都有地址,地址有高地址和低地址之分。

通常来说,都是小端。按照字节为单位,小权值位在低地址。大端相反,也就是小权值位在高地址。举例如下:

因此,数据在内存中的存储,不仅仅是受到原反补的影响,还受到大小端的影响(数据在内存中的存放顺序)。在存的时候,以补码 小端的形式存,在取的时候,以小端的形式取,还要看类型

数据的取值范围

一个比特位存放一个二进制数,比特是最小的存储单元,只能存0和1

32位机器有32个寄存器

一个char,大小为一个字节,8个比特位,取值范围为-128 - 127 。最小值11000000,发生截断,因为最高位是符号位,所以规定它是-128。最大值01111111,也就是127。00000000表示0

同理,short的取值范围是-2^15 - 2^15-1

int的取值范围是-2^32 - 2^32-1

字符的存储

\0不是字符串的内容,但是占用字符串的空间,标识结束,如果没有\0,字符串就不知道何时结束。strlen不包含,sizeof包含。

sizeof字符串是包含\0的空间大小(字节) strlen字符串是计算\0之前的字符长度,如果没有\0就会是随机值

ASCII编码表:值和符号,一一映射对应关系

一个字符一个字节,是8个比特位,也就是8个二进制数。这就决定了它的取值范围,字符的存储是转成ASCII值,然后转补码。

字符'\0'的ASCII值是0。char a ='\0',8个比特位是00000000,32个比特位也全部是0。a以%d打印,结果为0

字符'0'的ASCII值是48,char a = '0',以%d打印,结果是48

bool类型

c99中才支持bool类型,头文件stdbool.h,win和Linux都可以。bool类型只要两个取值:true和false。大小为1个字节。 0为假,非0为真。

bool和0比较推荐使用下图写法

浮点数比较相等

浮点数本身有精度损失,不能使用==来比较

应该将两个浮点数相减,然后和定义的精度或者系统提供的精度进行比较

过程如下:

定义精度

两浮点数相减结果在误差范围内(自定义精度),即可认为相等

abs是求绝对值的函数

也可以直接与系统提供的精度比较

如果是浮点数和0比较,可以直接将该浮点数和精度比较,即 if (fabs(x)< FLT_EPSILON),表示相等

强制类型转换

int double这种表示数字大小的可以强制类型转换,不同类型的指针之间可以强制类型转换(都是指针),int和指针之间可以强制类型转换。完全没有联系的类型之间不可以强制类型转换

0 '\0' NULL 以%d的形式打印,结果都是0。说明这三个在内存中是一样的数,但是它们三个在进行使用时分属于三个类型: int char 指针,同一个数却适用于不同类型,就是发生了强制类型转换

如图所示,对于实际在内存中是一个数,却因为对他的看法不同而发生使用场景的不同,就是强制类型转换。如果是改变了内存中的数,比如将数字123转变成了字符串"123",虽然看上去都是123,但是实际在内存存储的值发生了变化,这是真实的转变,是需要调用接口来完成的。强制类型转换没有发生真实的转变,只是因为类型的不同而发挥不同的意义

注意每一条语句最后的分号; 表示一条语句的结束

变量+运算符→表达式→语句→代码块→函数→一份C语言程序

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值