导图社区 C语言知识图谱
C语言知识图谱的思维导图,内容有数据类型、合法数值常量、变量,常量、格式控制符、进制、转义字符、字符、字符串、printf函数、scanf函数、注释、选择语句、循环语句、函数、数组、操作符、关键字、宏、指针、结构体、联合(共用体)。
编辑于2023-09-17 22:01:07 河南C
数据类型
类型
整数
整型
short
短整型
int
整型
long
常整型
long long
更长的整型
小数
浮点型
float
单精度浮点数
double
双精度浮点数
字符
字符型
char
数据类型长度
整型
short
2字节
int
4字节
long
4字节
long long
8字节
浮点型
float
4字节
double
8字节
字符型
char
1字节
合法数值常量
实型常量必须用带小数点的数或带指数的数表示
小数形式
必须有小数点
指数形式
以“e”或“E”后跟一个整数来表示 以10为底数的幂数,且规定e或E之前 必须有数字,且e或E后面的指数必须为整数
数值常量中不能夹带空格
变量,常量
常量
不变的值
变量分类
字面常量
3.14
'A'
const修饰的常变量
const int a=10;
a本质上是变量
a不会被修改
#define 定义的标识符常量
#define MAX 100
枚举常量
enum
变量
变的值
变量分类
局部变量
{}内定义
全局变量
{}外定义
全局变量与局部变量名称可以相同, 且局部优先,但不建议写成一样
变量作用域
变量在哪里能起到作用, 哪里就是作用域
局部变量作用域
变量所在的局部范围
全局变量作用域
整个工程 (project)
声明来自外部的符号
extern
变量的生命周期
局部变量生命周期
进入作用域生命周期开始,出作用域生命周期结束
全局变量生命周期
整个程序的生命周期
格式控制符
int
%d
打印十进制数
%%打印输出字符%
%o
打印八进制数
%x
打印十六进制数
float
%f
double
%lf
short
%hd
字符
注:空格,回车符,制表符也算字符
%c
字符串
%s
地址
%p
sizeof返回值
%zu
跳过对应的输入数据
%*
用指数形式输入双精度浮点数
%le
进制
八进制
八进制的数码取值为0~7, 必须以0开头,作为八进制数的前缀
十进制
数码取值为0~9, 无前缀
十六进制
数码取值为0~9,A~F或a~f, 前缀为0X或0x
转义字符
\'
单引号字符
\"
双引号字符
\\
单斜杠字符
\t
tab键,空四格
\a
警告
\r
回车
\ddd
ddd表示1~3个八进制的数字
代表一个ASCII字符
\xdd
dd表示2个十六进制的数字
代表一个ASCII字符
字符、字符串
字符
字符需用' '(单引号)括起来
char ch = 'w';
char为字符类型
ch为字符变量
'w'为字符常量
字符串
C语言中无字符串类型
字符串需用“ ”(双引号)括起来
char a[10]={'a','b','c','\0'};
等价于char a[10]="abc";
字符串的结束标志是一个\0的转义字符,在计算字符串长度\0是结束标志,不算作字符串内容
比较两个字符串是否相等,不能使用==,应使用函数strcmp()
用==比较的是首地址
头文件 <string.h>
函数strlen()
求字符串长度的函数
头文件
<string.h>
printf函数
子主题
printf()函数的返回值
返回值为打印出来的字符个数
scanf函数
子主题
scanf()函数的格式控制串
可以使用其他非空白字符,但在输入时必须输入这些字符
在格式字符前,可以加入一个正整数指定输入数据所占据的宽度,但不可以对实数指定小数位的宽度
例
可%5d
不可%4.2f
使用格式符“%s”进行字符串输入时,空格和回车会被读入,并且函数scanf以它们作为分隔符停止一个字符串的输入
注释
形式
//
C++注释风格
/* 代码 */
C语言注释风格
不支持嵌套注释
选择语句
if else语句
switch语句
循环语句
while语句
for语句
for(exp1;exp2;exp3)
exp1
初始化
exp2
条件判断
exp3
调整
建议
不可在for循环体内修改循环变量,防止for循环失去控制
建议for语句的循环控制变量的取值采用“前闭后开区间”写法
for循环变种
判断部分省略意味着判断会恒成立
初始化、判断和调整部分都可以省略但不建议
do...while语句
至少循环一次
break执行
终止整个循环
continue执行
跳过本次循环
即跳过continue后的代码
函数
例
int add(int x ,int y) { int z = 0; z = x+ y; return z ; }
int
返回类型
add
函数名
int x , int y
函数参数
{}内的内容
函数体
分类
库函数
自定义函数
函数功能要足够单一,足够简单
一个工程中,可以有多个.c文件,但只能有一个main函数
函数参数
实参
真实传给函数的参数叫实参
实参可以是
常量
变量
表达式
函数等
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值, 以便把这些值传递给形参
形参
形参是指函数名后括号中的变量, 因为形参只有在函数被调用的过程中才实例化(分配内存单元), 所以叫形式参数
形参当函数调用完成之后就自动销毁了,因此形参只在函数中有效
形参和实参的名字可以相同也可以不同
传参
实参传递给形参时,对形参的修改不会影响实参
函数调用
传值调用
例:void fun(int x) …… fun(a);
函数的形参和实参分别占有不同内存块, 对形参的修改不会影响实参
传址调用
例:void fun(int *p) …… fun(&a);
传址调用是把函数外部创建变量的内存地址传递给函数参数的 一种调用函数的方式
这种传参方式可以让函数和函数外边的变量建立起真正的联系, 也就是函数内部可以直接操作函数外部的变量
函数的嵌套调用
函数可以嵌套调用,但不能嵌套定义
即一个函数里不能再去定义一个函数
函数的链式访问
一个函数的返回值作了另一个函数的参数
关于返回值
函数不写返回类型时,默认返回类型是int
函数有返回类型无返回值时,返回的是函数执行过程中最后一条指令执行的结果
int main(void) { return 0; }
明确说明main函数不需要参数,而本质上main函数是有参数的
函数声明
告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但具体是否存在,函数声明无法决定
函数声明一般出现在函数使用之前,要满足先声明后使用
函数的声明一般要放在头文件中
函数定义
指函数的具体实现,交代函数的功能实现
函数递归
程序调用自身的编程技巧叫递归(直接或间接调用自身)
递归的两个必要条件
存在限制条件,当满足这个限制条件时,递归便不再继续
每次递归调用之后越来越接近这个限制条件
递归与迭代
求n的阶乘
递归实现
int fac(int n) { if(n<=1) return 1; else return n*fac(n-1); }
迭代实现
int fac(int n) { int i = 0; int ret=1; for(i=1;i<=n;i++) { ret*=i; } return ret; }
数组
一组相同元素的集合
例
int a[10]={1,2,3};
int
数组元素类型
a
名
[10]
常量表达式,指定数组大小
下标从0开始
初始化
在创建数组的同时给数组内容一些合理的初始值
例1:int a[10]={1,2,,3};
不完全初始化,剩余元素默认初始化为0
例2: int a[]={1,2,3};
未指定大小则根据初始化内容确定大小
两种初始化的区别
char a1[5]={'a','b','c'};
数组元素为a,b,c,0,0
char a2[5]="abc";
数组元素为a,b,c,\0,0
计算数组元素个数
int sz=sizeof(a)/sizeof(a[0]);
在内存中的存储
数组在内存中是连续存放的
二维数组
创建
例:int a[3][4];
初始化
int a[3][4]={1,2,3,4,2,3,4,5,3,4};
1 2 3 4 2 3 4 5 3 4 0 0
int a[3][4]={{1,2},{3,4},{5,6}};
1 2 0 0 3 4 0 0 5 6 0 0
int a[][4]={{1,2,3,4},{2,3}};
1 2 3 4 2 3 0 0
可以省略行,但不能省略列
计算行数
sizeof(arr)/sizeof(arr[0]);
计算列数
sizeof(arr[0])/sizeof(arr[0][0]);
三维数组
三维数组只能省略第一维
在内存中的存储
二维数组在内存中的存储也是连续存储的
数组越界
设数组有n个元素
若数组下标小于0或大于n-1,就是数组越界访问了,并且编译器不一定会报错
二维数组行和列也可能存在越界
传参
数组传参时,传递的时数组首元素的地址
数组传参时有两种写法
数组写法
fun(int arr[])
指针写法
fun(int * arr)
数组名
数组名确实能表示首元素的地址,但有两种例外
sizeof(数组名) 这里的数组名表示整个数组,计算的是整个数组的大小,单位字节
&数组名 这里的数组名表示整个数组,取出的是整个数组的地址
二维数组的数组名理解
二维数组的数组名表示的地址为首行的地址
操作符
算术操作符
整数二进制表示
原码
32位二进制
反码
补码
整数在内存中存储的是补码
正整数原码、反码、补码均相同
负整数的原码、反码、补码需要计算
补码计算
原码符号位不变,其他位取反
反码+1
移位操作符
移位操作符
基于补码运算
<<
左边丢弃,右边补0
<<n相当于乘以2^n
负整数打印的是原码,需通过移位后的补码反推原码计算打印结果
>>
右边丢弃,左边补0
不要移动负数位,该标准未定义
位操作符
位操作符
基于补码对应的二进制位运算
&
有0则为0
两个都为1才为1
|
有1则为1
两个都为0才为0
^
相同为0
相异为1
赋值操作符
=
连续使用
运算顺序从右往左
+=
例:a+=3;
等价于
a=a+3;
*=
&=
^=
|=
>>=
<<=
单目操作符
即只有一个操作数的操作符
C中
0表示假
非0表示真
!
逻辑反
sizeof
操作数的类型长度
以字节为单位
++(--)
例
a++;(后置++)
先使用后++
int b=a++;
等价于: int b=a; a=a+1;
++a;(前置++)
先++后使用
int b=++a;
等价于:a=a+1; int b=a;
~
对一个数的二进制位取反
强制型转换
(类型)
例:int a=(int) 3.14;
3.14是字面浮点数,编译器默认理解为double类型
关系操作符
!=
不等于
==
相等
逻辑操作符
&&
且
||
或
逻辑操作符的运算
&&
左边为假,右边就不算了
||
左边为真,右边就不算了
条件操作符(三目操作符)
exp1?exp2:exp3
1为真则执行2
1为假则执行3
逗号表达式
逗号隔开的一段表达式
exp1,exp2,exp3,...,expN
从左往右依次计算,整个表达式的结果是最后一个表达式的结果
表达式求值
顺序由操作符的优先级和结合性决定
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
关键字
C语言本身内置的,不是自己创建的,也不能自己创建
循环语句
for
while
do
break
continue
分支语句
if
else
switch
case
default
goto
数据类型
char
short
int
long
float
double
常属性
const
自定义
enum
枚举
struct
结构体
union
联合体(共用体)
声明外部符号
extern
寄存器
register
例: register int num=3;
建议3存放在寄存器中
有符号的
signeed
无符号的
unsigned
计算大小
sizeof
静态的
static
修饰变量和函数
修饰局部变量
称为静态局部变量
局部变量出了作用域,不销毁
本质上,static修饰局部变量时,改变了变量的存储位置,影响了变量的生命周期,变长(同程序的生命周期)
修饰全局变量
称为静态全局变量
全局变量是具有外部链接属性的,static修饰全局变量时,其外部链接属性变为内部链接属性,其他源文件(.c)就不能再使用这个全局变量了
修饰函数
称为静态函数
一个函数本来具有外部链接属性的,但被static修饰时外部链接属性就变成了内部链接属性,其他源文件(.c)就无法使用
函数返回值
return
类型重命名
typedef
例:typedef int INT;
空
void
函数的返回类型,函数参数
注意
定义的变量名称不能是关键字
名字必须由字母、数字和下划线组成,不能有特殊字符,同时不能以数字开头
变量的命名要有意义
宏
#define定义宏(宏有参数)
例:#define add(x,y)((x)+(y))
add
宏名
x,y
宏的参数
参数是无类型的
((x)+(y))
宏体
指针
内存
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的
内存会划分为一个个的内存单元,每个内存单元都有一个编号
一个内存单元的大小为一个字节
地址
编号即为地址
地址也被称为指针
指针变量
存放指针(地址)的变量就是指针变量
口语中的指针,通常是指指针变量
例: int * p = &a;
* 说明p是指针变量
int 说明p指向的对象是int类型
操作符
&
取地址操作符
例:int a= 10; &a; printf("%p\n",&a);
打印的是a的地址
*
解引用操作符
例: int * p = &a;
通过p中存放的地址,找到p所指向的对象,* p就是p指向的对象
指针变量的大小
不论是什么类型的指针,都是在创建指针变量
指针变量是用来存放地址的,其大小取决于一个地址存放的时候所需要多大的空间
32位(x86)机器上的指针变量大小是4个字节
64位(x64)机器上的指针变量大小是8个字节
注意
int * p1,p2;
等价于
int * p1; int p2;
int *p1,*p2;
等价于
int *p1; int *p2;
指针类型的意义
指针类型决定了指针在被解引用的时候访问几个字节
例:int *解引用访问4个字节 char *解引用访问1个字节
指针类型决定了指针+-1操作时跳过几个字节
野指针
野指针指指针指向的位置是不可知的
随机
不正确
无明确限制的
int * p;
p没有初始化,就意味着没有明确的指向,一个局部变量不初始化的话,放的是随机值。
指针越界访问
当指针指向的范围超出数组范围时,p即为野指针
指针指向的空间释放
避免出现野指针
指针初始化
小心指针越界
指针指向空间释放即使置NULL
避免返回局部变量的地址
指针使用前检查有效性
指针运算
指针+-整数
例:int arr[]={1,2,3,4}; int *p=arr; p++;
p++为加一个单元格,即四个字节
指针-指针
结果为指针与指针之间元素的个数(绝对值)
不是所有的指针都能相减,指向同一块空间的两个指针才能相减
指针关系运算
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
指针和数组
打印数组元素
int * p=arr;
printf("%d",arr[i]);
printf("%d",*(p+i));
printf("%d",*(arr+i));
二级指针
存放一级指针变量的地址
例:int a=10; int *p=&a; int **pp=&p;
pp是一个二级指针变量
int * 说明pp指向的对象是int *类型
* 说明pp是指针
指针数组
存放指针的数组
例: int * p[];
数组指针
数组首元素地址的指针
例:int (*p)[]
p即为指向数组的指针
结构体
结构体是把一些单一类型组合在一起的做法
结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量
对复杂对象进行描述会用到结构体
结构体的成员可以是标量,数组,指针,其他结构体
例
struct peo{}p1,p2;
p1,p2是两个全局的结构体变量
struct peo p1={0};
结构体变量的创建
关键字
struct
操作符
.
结构体对象.成员名
->
结构体指针变量->成员名
ptr->name等价于(*ptr.age)
传参
结构体传参时,要传结构体的地址
联合(共用体)
一种特殊的自定义类型
这种类型定义的变量也包含一系列成员,特征是这些成员共用同一块空间
关键字
union
一个联合变量的大小,至少是最大成员的大小
当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍