C语言学习笔记
C语言知识点
空格
打空格!!!
逗号后面要打空格
1 |
|
运算符前后要打空格
1 |
|
for循环里的分号后面要打空格
1 |
|
小括号和花括号之间要打空格
1 |
|
双斜杠注释后面打空格
1 | // 双斜杠和这句话中间有空格 |
其他还有什么地方要打空格自己意会
基础
C主框架
1 | int main() |
main
函数,又称主函数,是程序执行的起点。在执行程序时,由系统调用主函数,最后返回,结束程序。主要代码要写在主函数里。
主函数的类型一般是int
,最后由return
返回0
来结束运行。
1 | void main() |
主函数也可由其他类型定义,如void
、signed
等,但一般不常见且在一些情况下可能报错。以上为以void
做主函数头(无需返回值)。
头文件
C语言标准库
stdio.h
是C语言标准库,提供了C语言最基本的语法以及一些函数。
1 |
|
数学函数库
math.h
里有大量关于数学操作的函数,可以用来更方便的解决问题。常用的有:
abs()
对整形数据取绝对值fabs()
对浮点型数据取绝对值sqrt()
对数据取平方根(double
型)pow(x, y)
求x的y次幂(double
型)
1 |
|
输出如下:
1 | 9 3 12.5 5.123 |
string库
string.h
中的函数主要用于对字符串进行操作,常用的函数有:strlen()
返回字符串的长度。strcmp(x, y)
比较字符串x
和y
,当x < y
,返回值小于0
;当x = y
,返回值等于0
;当x > y
,返回值大于0
。strcpy(x, y)
将y
指向的字符复制到x
中strcat(x, y)
将字符串y
连接到x
的尾部
1 |
|
输出如下:
1 | 5 |
输出
1 |
|
注释
//
单行注释/**/
整段注释
1 |
|
#define定义标识符
C语言中可以使用#define
来定义一个标识符来表示一个常量,或定义一些宏,定义的标识符,并不占用程序内存,在预编译阶段对程序代码进行文本替换。定义标识符的操作在主函数外面。
最常见的用法就是#define
来定义一些常量:
1 |
|
typedef 关键字定义
C语言允许用户使用 typedef
关键字来定义自己习惯的数据类型名称,typedef
的真正含义是给一个已经存在的类型名称起一个别名,注意是已经存在的数据类型,而非变量,例如:
1 |
|
数据
定义和赋值
定义:数据类型
数据名
;
定义时赋初值:数据类型
数据名
赋值符号(=)
初值
;
同时定义多个:数据类型
数据名
,
数据名
;
注意! 定义时没有赋初值的话这个数据的值就是随机的,有需要时千万别忘了赋初值,没有需要时也赋初值也是一个很好的习惯。
1 |
|
输出如下:
1 | 0 5 3.14 2.2 |
变量和常量
在程序运行的过程中,可以改变值的变量称为变量。
程序运行过程中,不可以发生改变的量叫做常量。
一般定义的数据默认为变量,可以用define
定义常量,也可以在定义时的数据类型前加上const
使之成为常量。
1 | const int a = 2; |
数据类型
整形
类型 | 存储大小 | 值范围 | |
---|---|---|---|
char | 字符型 | 1字节 | -128 到 127 |
short | 短整型 | 2字节 | -32,768 到 32,767 |
int | 整型 | 4字节 | -2,147,483,648 到 2,147,483,647 |
long | 长整型 | 4字节 | -2,147,483,648 到 2,147,483,647 |
long long | 长长整形 | 8字节 |
浮点型(实型)
类型 | 存储大小 | 值范围 | 精度 | |
---|---|---|---|---|
float | 单精度浮点数 | 4字节 | 1.2E-38 到 3.4E+38 | 6 位有效位 |
double | 双精度浮点数 | 8字节 | 2.3E-308 到 1.7E+308 | 15 位有效位 |
long double | 长双精度浮点数 | 16字节 | 3.4E-4932 到 1.1E+4932 | 19位有效位 |
unsigned
整型变量的值的范围包括负数到正数。 但是在实际应用中,有的数据的范围常常只有正值(如学号、年龄等),为了充分利用变量的值的范围,可以将变量定义为“无符号”类型。可以在类型符号前面加上修饰符 unsigned
,表示指定该变量是“无符号整数”类型。如果加上修饰符 signed
或什么都不加,则是“有符号”类型。
有符号整型数据存储单元中最高位代表数值的符号,如果指定为无符号型,不能存放负数,如 -123 等。由于无符号整型变量不用符号位,所以可表示数值的范围是一般整型变量中的两倍。
内存分配
类型转换
在进行运算时,不同类型的数据要转换成同一类型。
自动类型转换(隐式类型转换)
float
型数据自动转换成double
型;char
与short
型数据自动转换成int
型;int
型与double
型数据运算,直接将int
型转换成double
型int
型与unsigned
型数据、直接将int
型转换成unsigned
型;int
型与long
型数据,直接将int
型转换成long
型。如此等等,总之是由低级向高级型转换。另外不要错误地理解为先将
char
型或short
型转换成int
型,再转换成unsigned
型,再转换成long
型,直至double
型。
强制类型转换
强制类型转换的一般形式为:(类型名)
(表达式)
1 | int a = 7, b = 2; |
ASCII码
每个字符都对应着一个ASCII码:ASCII码表
常用:
0
-> 48
9
-> 57
A
-> 65
Z
-> 90
a
-> 97
z
-> 122
进制转换
**十进制:**默认数制
**二进制:**以0B
或0b
前缀表示,如0b0101
**八进制:**以0
前缀表示,如0123
**十六进制:**以0X
或0x
前缀表示,如0x1A
vc6.0中整形后加l
或L
表示是long
型,加u
表示是unsigned
型
顺序结构
在C语言中,程序的执行分为三种结构:顺序结构、选择结构(分支结构)和循环结构。
顺序结构:代码从上到下顺序执行,中间没有任何判断和跳转。
变量输入输出
在C语言中,输入和输出是通过库函数stdio.h
中的scanf()
和printf()
函数来实现的。在输入与输出时,printf()
函数与scanf()
函数的格式字符串用于指定输入输出的格式。格式字符串中的格式说明符(如%d
表示整数,%f
表示浮点数)必须与后面参数的类型和数量相匹配。如果格式字符串与参数不匹配,可能会导致未定义的行为或输出错误。
1 |
|
输入:
1 | a 25 3.456 12.3456 111 hello |
输出如下
1 | 25 250 2500 |
表达式
C语言中的表达式主要由运算符和操作数构成。
运算符
- 算术运算符:
+
-
*
/
%
- 赋值运算符:C语言中的赋值运算符
=
用于将一个表达式的值赋给变量。此外,C语言还支持复合赋值运算符,如+=
、-=
、*=
、/=
、%=
等,这些运算符可以简化赋值和算术运算的组合。 - 自增自减运算符:C语言中的自增
++
和自减--
运算符用于将变量的值增加或减少1。这些运算符只能用于变量,不能用于常量或表达式。 - 位运算符:
<<
、>>
、^
、&
、|
优先级
在C语言中,运算符的优先级决定了表达式中各个运算对象之间的计算顺序,即哪个部分先计算,哪个部分后计算。下面是C语言中常用的运算符优先级列表,从高到低排列:
- 括号 ()
- 一元运算符:++ - - !
- 算术运算符:* / %
- 算术运算符:+ -
- 关系运算符:< > <= >=
- 等价运算符:== !=
- 位运算符:<< >>
- 位运算符:&
- 位运算符:^
- 位运算符:|
- 条件运算符 ?:
- 赋值运算符:= += -= *= /= %= >>= <<= &= ^= |=
- 逗号运算符: ,
需要注意的是,同一优先级的运算符按照结合性进行计算,大部分运算符遵循从左至右的结合性,只有单目运算符、条件运算符、赋值运算符遵循从右至左的结合性。
语句
C语言中的语句可以分为以下几类:
- 表达式语句:由表达式加上分号
;
组成,用于计算表达式的值并执行副作用。 - 函数调用语句:由函数名、实际参数加上分号
;
组成,用于调用函数。 - 控制语句:用于控制程序的执行流程,包括条件判断、循环执行、转向等。
- 复合语句:用花括号
{}
括起来的一条或多条语句,也称为块。 - 空语句:只有分号
;
组成的语句,不执行任何操作的语句。
选择结构
if-else if- else判断结构
大括号
1 | if(表达式1){ |
这是一个if语句,如果表示条件的逻辑表达式的结果不是0,那么就执行后面跟着的这对大括号内的语句;否则就跳过不执行,继续下面的其他语句。
但是if语句还有一种形式可以不用{}。
1 | if(a > b) |
if语句这一行结束的时候并没有表示语句结束的”;”,而后面的赋值语句写在if的下一行,而且缩进了,在这一行结束的时候有一个分号。
表明这条赋值语句是if语句的一部分,if语句拥有和控制这条赋值语句,决定其是否被执行。
简单地说就是if(逻辑表达式)后要么跟上”{“,要么跟上语句,不能直接写分号。
**总结:**有大括号的时候 条件满足的情况执行所有括号内语句,无大括号的时候 条件满足执行最近邻语句。
关系运算符
<
>=
<
<=
!=
用于测试“不相等”
==
用于测试“相等”
逻辑运算符
&&
逻辑与 理解为: 即 怎么 又 怎么 一假全假,全真为真
||
逻辑或 理解为: 要么 怎么 要么 怎么 一真为真, 全假为假
!
逻辑非 真取假 假取真
1 |
|
switch-case选择结构
switch
语句也是一种分支语句。 常常用于多分支的情况。else if
语句也能实现多分支情况,但在某些情况下使用else if
来实现,会使代码过于复杂。
1 | switch(整型表达式) |
switch-case
语句一般搭配break
和default
使用
中断语句break
是C语言中的关键字,用于跳出循环或switch
语句的执行。break
语句通常用于在满足某个条件时提前终止循环,或在switch
语句中匹配到某个case
后跳出。
当 switch
表达式的值并不匹配所有 case
标签的值时,这个 default
子句后面的语句就会执行,switch
语句可以有一个可选的 default
case
,出现在 switch
的结尾。default
case
可用于在上面所有 case
都不为真时执行一个任务。default
case
中的 break
语句不是必需的。
1 |
|
注:C语言中的switch
语句具有“穿透”性,这意味着如果在switch case
中没有使用break
语句,那么匹配的case
之后的所有case
都将被执行。
三目运算符
格式:
1 | a ? b : c; |
意为若a成立,则执行b,否则执行c。相当于if-else中的:
1 | if (a) |
三目运算符有很多用法,如判断赋值:
1 | int a = 1, b = 2; |
如直接返回:
1 | int cmp (int a, int b) |
布尔变量
布尔类型是一种包含两种值的数据类型,即0
和1
。基本上,bool
类型的值表示两种行为,即true
或false
。在这里,’0'
表示false
值,而’1
‘表示true
值。
在C中,'0'
以0
的形式存储,而其他整数以1
的形式存储,即“非零即true”
C语言标准库不自带bool类型,需要引用stdbool.h
头文件或用typedef
手动定义:
1 |
或
1 | typedef enum {false, true} bool; |
应用示例:
1 |
|
1 | x的值为假 |
循环结构
循环语句具有在某些条件满足的情况下,反复执行特定代码的功能。
while
语法形式:
1 | while (表达式) |
do-while
语法形式:
1 | do |
do while
循环是先直接进⼊循环体,执⾏循环语句,然后再执⾏ while
后的判断表达式,表达式为真,就会进⾏下⼀次,表达式为假,则不再继续循环。
for
语法形式:
1 | for (表达式1; 表达式2; 表达式3) |
表达式1 ⽤于循环变量的初始化 ,表达式2 ⽤于循环结束条件的判断 ,表达式3 ⽤于循环变量的调整,三种表达式都可以省略,但分号不能省略。
for
循环和while
循环可以相互转换。二者没有性能上的差别。实际开发中,根据具体结构的情况,选择哪个格式更合适、美观。
break、continue
在循环执行的过程中,如果某些状况发⽣的时候,需要提前终止循环,这是非常常见的现象。C语言中提供了 break
和 continue
两个关键字,就是应⽤到循环中的。
break
的作用是用于永久的终止循环,只要 break
被执行,直接就会跳出循环,继续往后执行。
continue
的作用是跳过本次循环 continue
后边的代码,在 for
循环和 while
循环中有所差异的。
嵌套
分支结构和循环结构在使用中都可以嵌套,像这样:
1 | void Func(void) |
函数
函数是指将一组能完成一个功能或多个功能的语句放在一起的代码结构。在C语言程序中,至少会包含一个函数,即主函数main()
。
分类
库函数
库函数就是存放在函数库中的函数,具有明确的功能、入口调用参数和返回值。
库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。
自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
例:写一个函数可以找出两个整数中的最大值。
1 | int cmp(int x, int y) |
定义和使用
一个在两个数中返回最大值的函数:
1 |
|
输入:
1 | 10 20 |
输出如下:
1 | max = 20 |
函数可以先声明后定义
1 |
|
在以上的代码中,int cmp(int x, int y);
先将函数声明,这样的语句叫做函数原型。
声明即告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
创建 C 函数时,会定义函数做什么,然后通过调用函数来完成已定义的任务。
调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。
返回类型、函数名、形参、实参、返回
C 语言中的函数定义的一般形式如下:
1 | return_type function_name( parameter list ) |
- **返回类型:**一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
- **函数名称:**这是函数的实际名称。函数名和参数列表一起构成了函数签名。
- 参数:当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
- **函数主体:**函数主体包含一组定义函数执行任务的语句。
局部变量与全局变量
任何一种编程中,作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。C 语言中有三个地方可以声明变量:
在函数或块内部的局部变量
在所有函数外部的全局变量
在形式参数的函数参数定义中
在某个函数或块的内部声明的变量称为局部变量。它们只能被该函数或该代码块内部的语句使用。局部变量在函数外部是不可知的。
1 |
|
全局变量是定义在主函数外部,通常是在程序的顶部。全局变量在整个程序生命周期内都是有效的,在任意的函数内部能访问全局变量。
全局变量可以被任何函数或语句访问。也就是说,全局变量在声明后整个程序中都是可用的。
1 |
|
全局变量在定义时默认初值为0
递归
数组
数组是一种数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组中的元素可以通过索引访问,索引通常从0开始。
声明与初始化
1 | type arrayName [ arraySize ]; |
这叫做一维数组。arraySize
必须是一个大于零的整数常量,type
可以是任意有效的 C 数据类型。
1 | int arr[5]; // 声明一个整型数组,其中包含5个元素,未初始化 |
访问
数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。例如:
1 | int a = arr[3]; // a = 4 |
遍历
可以使用循环语句对数组进行遍历:
1 |
|
输出如下:
1 | n[0] = 100 |
获取数组长度
数组长度可以使用 sizeof
运算符来获取数组的长度,例如:
1 |
|
输出如下:
1 | 数组长度为: 5 |
多维数组
C 语言支持多维数组。多维数组声明的一般形式如下:
1 | type name[size1][size2]...[sizeN]; |
多维数组最简单的形式是二维数组。一个二维数组,在本质上,是一个一维数组的列表。下面是一个二维数组,包含 3 行和 4 列:
1 | int x[3][4]; |
初始化二维数组
多维数组可以通过在括号内为每行指定值来进行初始化:
1 | int a[3][4] = { |
这样也是一样的:
1 | int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; |
二维数组可以用嵌套的两个循环来遍历:
1 |
|
输出如下:
1 | a[0][0] = 0 |
字符串(字符数组)
字符串实际上是使用空字符 \0
结尾的一维字符数组。因此,字符串的实际长度总要多一位,\0
是用于标记字符串的结束。在定义一个字符串时不需要把 \0
字符放在字符串常量的末尾。C 编译器会在初始化数组时,自动把 \0
放在字符串的末尾。
转义字符
在C语言中,转义字符是以反斜杠\
开头,后跟一个字符。它用来表示非打印字符,比如换行\n
以及其他一些特殊的字符。
以下是C语言中常用的转义字符的完整列表:
\\
:反斜杠\'
:单引号\"
:双引号\?
:问号\a
:警报(响铃)\b
:退格\f
:换页\n
:换行\r
:回车\t
:制表符(水平制表)\v
:垂直制表\0
:空字符\ooo
:八进制表示的字符(其中 ooo 是一个八进制数,范围为 0-377)\xhh
:十六进制表示的字符(其中 hh 是一个十六进制数,范围为 00-FF)
定义与赋值
1 | char site[7] = {'R', 'U', 'N', 'O', 'O', 'B'}; |
实际上,字符串就是char类型的数组,各种操作都与数组大同小异。
输入输出
字符串用%s
输入输出,且输入时不用加取地址符&
:
1 |
|
输出如下:
1 | Char Array Value is: javatpoint |
注
getchar()
读取一个字符,包括任何字符。
1 |
|
输入:
1 | abcdefg |
输出如下:
1 | b |
gets()
读取整行输入,直至遇到换行符,然后把换行符,储存其余字符,并在这些字符的末尾添加一个空字符使其成为一个 C 字符串。
1.gets()
函数不安全。
2.C11标准委员会已经将其废除,建议能不用尽量不用。
1 |
|
输入:
1 | Hello World! |
输出如下:
1 | Hello World! |
%[^\n]
%[^\n]
是一种输入方法,用到了正则表达式相关用法。它代表输入至换行符时停止。同样的,%[^1]
表示输入到1停止,以此类推。
1 |
|
输入:
1 | xxxxxxxxxxxxxxxxxxx1 |
输出如下:
1 | xxxxxxxxxxxxxxxxxxx |
指针
(能理解多少就理解多少,知道地址与变量的关系就行)
取地址符
每一个变量都有一个内存位置,每一个内存位置都定义了可使用 &
运算符访问的地址,它表示了在内存中的一个地址。
下面一个例子输出变量的地址:
1 |
|
输出如下:
1 | a 变量的地址: 0x7ffeeaae08d8 |
取内容符
指针变量定义格式:存储类型 数据类型 指针变量名;
int *p
: 定义了一个指针变量p,指向的数据是int类型的:
1 | int a = 5; |
访问指针所指向空间的内容用取内容运算符*
那么p变量存放的就是a的地址,q变量存放的是c的地址。
符号*
可以访问地址里面的内容。
指针与数组
在 C 语言中,数组名表示数组的地址,即数组首元素的地址。当我们在声明和定义一个数组时,该数组名就代表着该数组的地址。
1 | int myArray[5] = {10, 20, 30, 40, 50}; |
在这里,myArray
是数组名,它表示整数类型的数组,包含 5 个元素。myArray
也代表着数组的地址,即第一个元素的地址。
数组名本身是一个常量指针,意味着它的值是不能被改变的,一旦确定,就不能再指向其他地方。
我们可以使用&运算符来获取数组的地址:
1 | int myArray[5] = {10, 20, 30, 40, 50}; |
在上面的例子中,ptr
指针变量被初始化为 myArray
的地址,即数组的第一个元素的地址。
需要注意的是,虽然数组名表示数组的地址,但在大多数情况下,数组名会自动转换为指向数组首元素的指针。这意味着我们可以直接将数组名用于指针运算,例如在函数传递参数或遍历数组时:
1 | void printArray(int arr[], int size) { |
在上述代码中,printArray
函数接受一个整数数组和数组大小作为参数,我们将 myArray
数组名传递给函数,函数内部可以像使用指针一样使用 arr
数组名。
指针与函数
传递指针给函数
C 语言允许传递指针给函数,只需要简单地 声明函数参数为指针类型 即可。
下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:
1 |
|
输出如下:
1 | Number of seconds :1294450468 |
结构体
结构体(struct
)是一种构造类型,它可以将不同的数据类型组合在一起形成一个新的数据类型,这种新的数据类型就是结构体。
声明
定义和声明结构体的同时创建变量:
1 | struct Student { |
先定义结构体,然后声明变量:
1 | struct Student { |
使用typedef定义别名,然后创建变量:
1 | typedef struct Student { |
在结构体内部不初始化成员,而是在创建结构体变量后初始化:
1 | struct Student { |
访问
使用指针访问结构体成员:
1 | struct Student { |
在结构体数组中存储数据:
1 | struct Student { |
使用结构体指针访问结构体数组:
1 | struct Student { |
在结构体中使用结构体类型成员:
1 | struct Date { |
链表-结构体指针
—end—