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

switch语句

case完成判定功能,break表示分支功能,default处理默认情况

case后面的,只能是常量,或者定义的宏。不能是const修饰的变量和普通变量

switch( *) 用括号内的值去匹配下面的case后面的数字,从上到下依次匹配

case1: 如果1不匹配,就向下匹配2 如果匹配1,就执行1中的语句

     break;      break是分支功能,如果匹配这一条,就会在执行完之后中断,不再继续向下匹配

在switch语句的最后,如果从上到下没有一个匹配,就会执行最后一条语句,推荐写default语句

循环

1 循环条件初始化

2 循环条件更新

3 循环条件判定

do{} while(); 至少执行一遍 执行流程图如下

for循环执行流程图如下

标准输入输出

键盘输入的内容输出的内容,或者往显示器中打印的内容,全部都是“字符”。键盘,显示器都是字符设备

printf和scanf都是格式化输入输出,键盘显示器上的都是字符,然后被格式化控制成数据来使用。即: 格式化输出:数据→printf→字符→显示器 格式化输入: 键盘→字符→scanf→数据

以整型为例如图,整型变量a中存放的是整型1234,是数字,printf打印变量a,显示1234,但是这里显示器上的1234是字符,其实是打印字符1 2 3 4 \n一共5个字符到显示器上。用ret接收表示输出了5个字符,由此可证

getchar() 返回值是int 从文件中获取字符。 从键盘中获标准输入字符,代码如下

putchar将字符标准输出到显示器上

break和continue

while循环和do while循环中,continue跳转到条件判定处 i<10

for循环中,continue跳转到条件更新处 i++

void

空类型,不允许定义变量,大小在不同编译器中不同,在win中是0,在Linux中是1

为什么?因为定义变量的本质是开辟空间,而void是空类型,是不开辟空间的,即使开空间也会被当成占位符来对待

void的作用

void* p是可以的,因为指针变量是有固定大小的(4or8),*p解引用void指针是不可以的,因为不存在

void*指针可以接收任意类型的指针

NULL 数字0 字符'\0' 在数字层面都是0,含义不同。NULL(是将0强制转换成了void*类型)是对指针变量做初始化,数字0是对整型变量做初始化,字符'\0'是字符串的结尾

return

C语言中有字符串,但是没有字符串类型

在代码块内函数内定义的是临时变量,是在栈上开辟空间的。栈帧销毁之后,就会将其中的数据标记为无效,表示可以让后续的函数调用覆盖这一块栈帧空间

计算机清空数据,只需要标记该数据无效即可

const

const可以修饰变量变成不可修改的常量 ,但是可以通过指针解引用的方式修改

const必须在定义变量时进行初始化,不能二次赋值

const所在的位置不同,修饰的内容不同。如下图,在的前面是修饰的,表示指针p指向的那个变量的内容不可修改。在p的前面是修饰p的,表示指针p的内容是不可修改的,即p指向哪块空间不可修改

权限只能缩小,不能放大。如下图,第一种const修饰的指针变量的内容赋值给没有const修饰的指针变量,会有警告。第二种反过来则不会

在C中,所有的函数传参,都会形成临时变量,无论参数是什么变量类型

const 还可以修饰函数参数和函数返回值

在C++中,类中定义的成员函数,如果不想让它修改类中的成员变量,在成员函数定义处的形参列表的后面 加一个 const

指针

不同的类型的指针都是4或者8字节,那它们有什么区别呢?其实就是根据类型来确定要识别多大的一块连续的空间

指针就是地址,指针是数据,是一个十六进制数字,指针变量是一种变量,是存放地址的

指针就相当于是房间的门牌号,用来标识空间,表示地址,从而让别人能够找到

内存寻址是以字节为基本单位

在C语言中,任何变量取地址都是最低地址开始。只要定义变量就会开空间,开空间就会有地址,对变量(这里的变量是一个统称,包含数组,结构体,枚举,联合,位段等)进行取地址,就会得到这一块空间的所有地址中最低的地址

对指针解引用代表指针指向的目标

volatile

保持内存可见性,不要让变量被编译器优化(被放入寄存器中),保持CPU持续从内存中读取数据

函数指针

如上图所示,将函数名取地址即可得到函数指针,函数指针的类型写法是,保留函数返回类型,保留函数的参数类型,去除参数名,将函数名改为指针名

函数指针的函数调用方式,函数名就是函数指针,所以如上图的2和3是相同的,对函数指针解引用也是可以的

回调函数

让函数指针作为另一个函数的参数,调用这个函数从而调用到参数的这个函数。回调函数的使用如下:

extern

声明变量和函数,在头文件中声明时使用

extern表示函数声明,没有函数体,参数列表后面是;可以省略extern。示例:extern int add(int a,int b);

extern表示变量声明时,不能省略extern

源文件包含这个头文件之后,对这些变量和函数进行定义,就可以将定义和声明进行关联

别的源文件包含这个头文件之后,就可以找到这些变量和函数,从而实现变量和函数的跨文件访问

结构体

struct stu{

};

定义结构体,将不同类型的元素组合起来,制作一个新的类型。区别于数组,数组是相同类型的元素的集合

结构体的初始化如下,对结构体中的每一个元素进行初始化

结构体不能进行整体的二次赋值,初始化后如果想修改元素,只能一个一个的修改

改变字符数组的内容,使用strcpy

访问结构体元素的两种方法:1  结构体对象名. 结构体元素         2  结构体指针→结构体元素  

typedef

typedef类型重命名 typedef A B,将类型A重命名为B,以后直接使用B就可以代替类型A

只推荐对结构体名进行重命名,其他情况一般不使用

宏和typedef的区别

宏是完全的文本替换,在编译的预处理阶段就会进行全部替换,#define 1 2 其中1和2是完全相同的

typedef是创建与目标相似的独立的一种类型,并不是文本替换,是与原类型不同的

\ 转义和续行

在一行语句后面加\,然后换行表示连续下一行,\的前面可以有空格,\的后面不可以有空格

单引号和双引号

单引号→字符

双引号→字符串

特殊:单引号字符是4个字节,例如,sizeof('1')

短路

A && B 当A为假时,B就不会执行

A || B 当A为真时,B就不会执行

对于上述的表达式只执行一半的情况称为短路

按位或 按位与 按位取反 按位异或

是二进制之间的计算的概念

按位或 | 表示有一位为1,结果为1

按位与  &  表示两位都是1,结果才是1

按位取反~

按位异或^ 表示两位相同为0,不相同为1 1^1=0 0^0=0

任何数与0异或都等于本身,任何数自己与自己异或等于0

异或运算可以用来求一个数组中出现奇数次的数。方法是使数组每个元素都异或起来,最后数值即是解。因为异或运算满足交换律和结合律,所以出现偶数次的数经过异或结果为0,出现奇数次的数异或结果为其本身。

异或运算支持交换律和结合律

整型提升

cpu在运算的时候,只能按照整形的字节长度进行计算,所以短整形和char类型在数字计算的时候,需要先将8个比特位提升为32个比特位。比如,char a = 2,原码是 00000010,在8个比特位中,第一个比特位也是表示符号位,0为正数,1为负数。char a 和char b相加,先对它们进行整型提升,(unsigned char高位补0,char是负数高位补1,是正数高位补0。short同理)然后再对补码进行计算,计算后再进行截断,取最后8个比特位,即计算结果的补码。然后对8个比特位进行整形提升,补到32个比特位,才能转换为原码,即字面值

左移和右移

对数据进行计算,都需要先将数据从内存中移动到CPU的寄存器中

<<左移 最高位舍弃,最低位补0。右移 两种 1有符号数 最低位舍弃,最高位补符号位 2无符号数 最低位舍弃,最高位补0

前置++与后置++

a++先使用再++自增

++a先++自增再使用

int a=0 int b=a 将a的值赋值给b,是需要先将a的值放到寄存器中,再将寄存器内的值写到变量b的内存空间中

如果没有变量使用这个值,只是单纯的后置和前置++,是没有区别的,只是完成自增

字符串

字符串比较是否相等是strcmp. 字符比较是否相等是可以想整型一样==进行比较

C语言中的字符串,有两种定义方式:

1.将字符串的首字符地址传给一个指针变量保存

2.定义一个字符数组 char arr[20]

表现方式也有两种:

1 双引号,以\0结尾,标识字符串结束,占用一个字节的空间,不算strlen

2 以花括号的形式将每一个字符列举,结尾不包含\0 {'a','b','c'}

宏定义数字常量 字符串常量 #define A 10 #define B "字符串"

这样就可以提升代码的可维护性,在代码中会大量使用的这种字符或数字常量,推荐使用宏进行文本替换,之后如果想更改常量,直接在宏定义更改一次即可

如果想打印的字符

宏可以定义表达式和函数

宏虽然是简单的文本替换,但是如果宏定义的表达式在双引号中打印,就会以字符串的形式打印,而不会被替换

使用下图代码,可以在宏中插入多条语句

宏可以在代码的任何地方定义,与是否在函数内无关

宏的作用范围是自宏之后都是有效的,之前的不行

条件编译

条件编译是为了在预处理阶段对代码进行裁剪。ifdef 检验宏是否被定义 满足条件就执行代码,不满足条件就不执行下列代码(if和endif之间)。判断结束时 #endif   ifndef 如果没定义宏,就执行代码

最基本的条件编译如下图

数组

数组是一类元素的集合

普通的定义变量,因为栈区是向下增长的,所以先定义的变量,也就是先在栈区上开辟空间的变量地址高,后定义的变量的地址低。而数组中的变量是与之相反的,随着元素个数的增加,地址依次增大,因为数组开辟空间时是开辟完整的一块空间。

将数组看成是一种对象,是整体开辟空间的,数组开辟的空间是连续的。栈帧销毁后整体释放。地址最低的是0号元素,线性连续递增

指针+1,是将地址越过一个指针自身所指向的类型的大小。如果是int*,地址是0x00000001,对这个指针变量+1之后地址就变成了0x00000005,地址增加了4个字节

因此,通过对指针+1,就可以遍历数组内的元素。数组是连续的开辟的一整块空间,地址是连续的。以字符数组为例,找到第一个字符的地址,然后对其+1就会让指针跨越1个字节,从而指向下一个字符,解引用就可以访问到该元素

只有以下两种情况,数组名代表整个数组

虽然地址值相同,但是对地址+1,跨越的步长不同,前者是跨越一个元素,后者是跨越一个数组

数组名在做右值的时候,表示数组首元素的地址

遍历数组的两种方式,解引用与[ ],使用指针和数组名都可以使用这两种方式

如图,图示指针指向数组的首元素,指针[ i]可以访问数组中的第i+1个元素,等同于(指针+i)解引用

&arr+1 取地址数组名+1表示地址跨越一个数组的大小

arr+1 是首元素地址+1,表示地址跨越一个数组元素的大小

A是指针数组 B是数组指针 [ ]比的优先级比高,先和**结合,就是指针,先和[ ]结合,就是数组

二维数组和多维数组

数组中也可以保存数组,数组也是一种类型

二维数组和多维数组也可以看作是一维数组,都是线性连续递增的

函数指针

函数也是代码的一部分,所以函数也有地址,保存函数的地址,方便后续函数调用,以便后续CPU寻址访问,执行指令

函数名就是函数地址,&函数名也是函数地址,两个是相同的

如图,就是一个函数指针变量p,void (p) () 前面是函数返回类型,(p)表示变量先和*结合,是指针变量,后面的()表示是函数的指针

注意: 函数指针和数组指针一样,都需要将接收地址的指针变量里面的每一块内容都与之相符。

如下图,数组指针,数组定义处和地址接收处相同,都是int类型的数组,而且数组中的元素都是3

assert

断言,如果assert中的条件不满足,就中断运行

只在debug模式下有效

不推荐,在指针作为函数参数传参时,要确保传的参数不是空。推荐下列写法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值