C语言

本文最后更新于:2022年5月18日 晚上

关键字

  • 数据类型关键字(12个)
关键字 含义 关键字 含义
char 声明字符型变量或函数 signed 声明有符号类型变量或函数
float 声明浮点型变量或函数 unsigned 声明无符号类型变量或函数
double 声明双精度变量或函数 void 声明函数无返回值或无参数,声明无类型指针
int 声明整型变量或函数 struct 声明结构体变量或函数
short 声明短整型变量或函数 union 声明共用体(联合)数据类型
long 声明长整型变量或函数 enum 声明枚举类型
  • 控制语句关键字(12个)

(1)循环语句(5个)

关键字 含义
for 循环语句
do 循环语句的循环体
while 循环语句的循环条件
break 跳出当前循环
continue 结束当前循环,开始下一轮循环

(2)条件语句(6个)

关键字 含义
if 条件语句
else 条件语句否定分支 (与if连用)
goto 无条件跳转语句
switch 用于开关语句
case 开关语句分支
default 开关语句中的 “其他” 分支

(3)返回语句(1个)

关键字 含义
return 子程序返回语句(可以带参数,也看不带参数)
  • 存储类型关键字(4个)
关键字 含义
auto 声明自动变量
extern 声明变量是在其他文件中声明(引用变量)
register 声明寄存器变量
static 声明静态变量
  • 其他关键字(4个)
关键字 含义
const 声明自动变量
sizeof 计算数据类型长度
typedef 用以给数据类型取别名
volatile 说明变量在程序执行中可被隐含地改变

命名规则

  • 必须以字母、下划线开头
  • 只能包含字母、数字、下划线
  • 区分大小写
  • 最打长度为32个字符
  • 自定义标识符不可用关键字及预定义标识符
  • 自定义标识符最好见明知义

数据类型分类

常量

常量的分类

  • 整型常量: 用十进制、八进制、十六进制表示
  • 实型常量: 用十进制小数形式,指数形式表示
  • 字符常量: 用单引号括起来的单一字符
  • 字符串常量: 用双引号括起来的一串字符
  • 符号常量: #define 符号常量 常量

定义符号常量的时候注意格式:(后面没有分号)

#define PI 3.14 => #define只是简单的代换,这就有可能产生意想不到的错误

const和#define两者的区别

  • 一:区别

(1)就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。

(2)就起作用的方式而言: #define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。

(3)就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。

(4)从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了。

  • 二:const优点

(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

(3)const可节省空间,避免不必要的内存分配,提高效率

常量符号化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
coust int red = 0;
coust int yellow = 1;
coust int green = 2;

int main() {
int color = -1;
char *colorName = NULL;
printf("请输入你喜欢的颜色的代码:");
scanf("%d", &color);
switch(color) {
case red: colorName = "red"; break;
case yellow: colorName = "yellow"; break;
case green: colorName = "green"; break;
default: colorName = "unknown"; break;
}
printf("你喜欢的颜色是%s\n", colorName);
return 0;
}

整型数据

  • 整型数据分类
类型名称 类型说明符 字节数 取值范围
短整型 short[int] 2 -32728~+32767
基本整型 int 4 -2147483648~+2147483647
长整型 long [int] 4 -2147483648~+2147483647
无符号短整型 unsigned short 2 0~65535
无符号整型 unsigned int 4 同无符号短整型(或长整型)
无符号长整型 unsigned long 4 0~4294967295

1字(word)= 2字节(byte)

1字节(byte) = 8位(bit)

1.短整型:含有符号位 所以大小2^15

2.无符号短整型:没有符号位 所以大小2^16

八进制:在数字前加前缀0(数字0),由数字0~7组成。

十六进制:在数字前加前缀0X或0x,由数字0~9 ,字母a~f或 A~F组成。

有符号位十进制整型数:

  1. 有符号位的整型数据在内存中以补码的形式存储
  2. 正数的补码时其本身,负数的补码为 “原码取反再加1,符号位不变”。
  3. 求得十进制 “11” 的二进制值:1011
  4. 最高位为符号位,负数的符号位为 “1” ,正数的符号位为 “0”
  • 整型数据的溢出
1
2
3
4
5
6
7
#include <stdio.h>
int main() {
short a,b;
a=32767;
b=a+1;
printf("%d, %d",a, b);
}

32767, -32768

C语言是没有布尔类型的,但是有一个头文件使用后就有了像布尔类型的变量了,#include ;

浮点型数据

类型名称 有效位数 值域
单精度 float 7位 约±3.4E±38
双精度 double 16位 约±1.7E±308
长精度 long double 至少16位

整数除以整数结果为整数

十进制小数:由数字和小数点组成

指数:以字母E或e来表示以10为底的指数。注意:E或e的两边必须都有数,右边必须时整数

字符型

字符型数据:用 ‘ ‘ 括起来,单个字符或转义字符,占用一个字节。

转义字符:“ \ ” 反斜线开头,后面跟一个字符或一个代码值表示,接在 “ \ ” 后的字符不再表示字面意思,而是另有其意。

转义字符 含义
\0 空字符,表示字符串结束
\a 响铃,发出系统警告声音
\b 退格,将当前光标位置移动到前一列
\t 水平制表符,使屏幕光标跳过8个光标。
\n 换行符,使屏幕光标移到屏幕下一行开头
\v 重直制表(跳到下一个Home位置)
\f 换页,将当前光标移到下一页的开头
\r 回车,将当前光标移到本行的开头
\“ 双引号字符
\‘ 单引号字符
\\ 反斜杠字符
\ddd 1~3位八进制数表示的字符
\xhh 1~2位十六进制数表示的字符
1
2
3
4
5
6
#include <stdio.h>
int main() {
printf("12345678\t0\rabc\n");
printf("h\ti\b\bj k");
return 0;
}

abc456789 0

h j k

字符串:用双引号( “ “ )括起来的字符数列。

储存:每个字符串尾自动加一个 ‘\0’ 作为字符串结束标志。

字符与字符串的比较

类型 字符 字符串
引用方式 ‘ ‘ “ “
长度 1 >=1
结束标志 无结束标志 \0
是否可直接赋值 可以 不可以

字符:是用单引号引用起来的一个字符或转义字符,长度为1。

字符串:是用双引号引用起来的一系列字符,长度大于等于1,以\0结束。

ASCII码

标准ASCII,值为0~127,表示所有的大、小写字母,数字0到9,标点符号,以及在美式英语中使用的特殊控制字符

对应的字符 ASCII值
0~9 48~57
A~Z 65~90
a~z 97~122
回车 13

数据操作

自增自减

1.前缀形式:++变量,—变量

作用:先将变量自增(或自减),然后将自加(或自减)后的结果赋给表达式。

a=++i; ==> i=i+1; a=i;

2后缀形式:变量++,变量—
作用:先给表达式赋值,然后变量自增(或自减)。

b=i—; ==> b=i; i=i-1;

逗号表达式

用逗号运算符把多个表达式连接起来的式子,称为逗号表达式

  1. 各表达式之间用逗号隔开

  2. 从左至右每个表达式的值

  3. 最后一个表达式的值为整个表达式的值
1
2
3
4
5
6
7
#include <stdto.h>
int main() {
int a,b;
b=(a=3*5, a*4, a+5);
printf("a=%d, b=%d", a, b);
return 0;
}

a=15, b=20

运算符

1.算术运算符: 优先级 +、- < *、\、%

2.关系运算符: 优先级 >,>=,<,<= < ==,!=

:关系运算符优先级别低于算术运算,高于赋值运算符

c > a+b ==> c > (a+b)

a > b == c ==> (a+b) == c

a = b > c ==> a = (b>c)

用关系运算符将两个表达式(算术、关系、逻辑、赋值表达式等)连接起来所构成的表达式,称为关系表达式

关系表达式的值只有两个,1和0

3.逻辑运算符: 优先级 !高于算术运算;&&高于||,&&和||都低于关系运算符,高于赋值运算符

用逻辑运算符把表达式连接起来的式子,称为逻辑表达式

逻辑表达式的值也只有两个,1和0

所以赋值运算符时优先级最低

逻辑表达式的值

A B A&&B A\ \ B !A
0 0 0 0 1
0 非0 0 1 1
非0 0 0 1 0
非0 非0 1 1 0

A&&B:当AB两个表达式同时为真时,整个表达式为真,否则为假。
AlIB:当AB两个表达式有一个为真时,整个表达式为真,其他为假。
!A:当表达式A为真时则为假,当A为假时则为真。

(非0就是真。只有0才为假)

a=3, b=4, c=5 优先级:!、*、-,+、&&、||、=

x=a||b*c&&b-c+!a

x=3||20&&(-1)+0

x=3||1 63, 103, 227

三目运算符

格式: 表达式1 ? 表达式2: 表达式3

表达式1为真时取表达式2的值;表达式1为假时取表达式3的值

语句

UmyKv6.png

语句:组成程序的基本元素,以 “ ; “ 作为结尾。

  1. 表达式语句:表达式 + “ ; “

  2. 函数调用语句:函数名(参数表) + “ ; “

  3. 控制语句:控制各语句执行的顺序及次数

  4. 复合语句:以一对大括号括起来的0条或多条语句

作用:在程序的某些地方,语法上只允许出现一条语句,而程序员可能需要多条语句来完成程序功能,这时就可用复合语句

  1. 空语句:一个分号构成的语句

作用:在程序某些地方,语法上要求必须有语句出现,而程序员可能没有代码要写,或者留待以后扩充,就可以写一条空语句

算法是指解决问题的一种方法或一个过程。

算法特征:有穷性、确切性、≥0个输入、有输出、可行性

程序是算法用某种程序设计语言的具体实现,程序可以不满足算法的性质。

程序是对解题方案的准确而完整的描述,是解决问题的一系列指令

流程图

UmgmCj.png

Umgax1.png

以特定的图形符号加上说明,表示算法的图,称为流程图或框图。

没有种类,有并行的图,比如关系图,类图等

输入输出函数

输出函数

C语言的输入和输出操作是通过函数来实现的,在使用前必须在程序的前面使用命令:#include

printf(“格式控制字符串”, 输出列表(变量、常量、表达式));

说明

(1)格式控制字符串,包括普通字符和格式说明两部分,格式说明由%开头,后跟格式字符及修饰等组成;

(2)格式控制字符串中的普通字符按原样输出;

(3)格式说明与输出表列输出项的个数要一致,格式说明的作用是使对应的输出项按指定的格式输出;

(4)输出表列由输出项组成,两个输出项之间用逗号分隔;

格式字符及其作用

格式字符 作用
d或i 按有符号十进制基本整型数据形式输出
ld 按有符号十进制长整型数据形式输出
c 按字符形式输出
s 按字符串形式输出
f 按小数形式输出单精度实数
o 按无符号八进制整型数据形式输出
u 按无符号十进制整型数据形式输出
x或X 按无符号十六进制整型数据形式输出
e或E 按指数形式输出单精度实数
% 输出%本身

可以在格式字符中添加数字来达到不同的输出需求

%3.2f %1.2f %0.6f 等等

字符输出函数: putchar(ch); 向屏幕输出一个字符

说明
(1)函数参数ch,可以是字符变量整型变量字符常量
(2)函数的返回值是输出的字符。

输入函数

scanf(“格式控制字符串”, 地址表列);

输入项组成,两个输入项之间用逗号分隔,输入项由取地址运算符&和变量名组成,即:&变量名;

输入数据流的分割方法
① 根据格式说明规定的数据类型从数据流中取得数据,即当数据流的数据类型与格式说明的类型不一致时,就认为这一数据项结束;

② 根据格式说明中指定的域宽从数据流中分割数据;

③ 通过在格式字符串指定分割符来分割数据,分割符可以是一切非格式字符;

④ 格式字符串中没有指定分割符时,常使用空格、Tab键、回车键来分割数据。

说明:如果输入的数据多于变量的个数时,余下的数据可为下一个 scanf() 函数使用。

getchar( ) 从键盘读入一个字符

说明:
(1)该函数没有参数,函数的返回值是从输入设备得到的字符;
(2)从键盘输入的数据通过回车键确认结束;
(3)该函数得到的字符可以赋给一个字符变量或整 型变量,也可以不赋给任何变量,而作为表达式的一部分;

(4)常用 if ( (ch=getchar()) == ‘Y’ ) 确定用户输入的字符是否是所需的字符。
(5)该函数常与putchar()配合使用,将读入的字符输出到终端。

1
2
3
4
5
6
#include <stdio.h>
int main() {
int a,b;
scanf("%d%*d%d", &a, &b);
printf("%d,%d\n", a, b);
}

123 45 678

123,678

%*d:抑制字符,表示虚读

回车符是字符,所以在应用的时候有要注意的地方

在上一个scanf()函数中的回车符,如果下一行是接受字符的scanf()函数,那么回车符会被捕获,从而出现意想不到的错误。

处理方法,在两个scanf()函数之间加一个getchar()函数,getchar()函数来捕获回车符,这样就没有错误了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main() {
int num,age;
char sex;
printf("请输入你的两位数字号:");
scanf("%d", &num);
printf("请输入性别(F/M):");
getchar();
scanf("%c", &sex);
printf("请输入你的年龄:");
scanf("%d", &age);
printf("%d号性别是%c,年龄是%d岁。\n", num, sex, age);
return 0;
}

请输入你的两位数字号:20
请输入性别(F/M):F
请输入你的年龄:12
20号性别是F,年龄是12岁。

scanf( ) 函数在读取数据时,会将输入的数据流存储在缓冲区中,根据指定的数据类型从输入流中匹配,当输入的数据与指定类型不匹配时,就会结束输入;当下一个输入为字符型时,需要清空缓冲区中的回车符,可以用getchar( ) 函数接收掉字符。

文件包含

UumI4s.png

头文件:用于保存程序的声明,包括:功能函数原型、数据类型的声明、数据接口声明等。

文件包含是指一个源文件可以将另一个源文件的全部内容包含进来。

头文件括起来的区别:

  1. 尖括号:到编译系统指定的标准目录(\include)下面查找该头文件,若没有找到就报错。多用于包含标准头文件
  2. 双引号:首先到当前工作目录中查找头文件;若没找到,再到编译系统指定的标准目录中查找。多用于包含用户自定义的头文件

文件包含预处理时将查找指定的被包含文件,并将其复制到#include命令位置.上。

常见的头文件

头文件名称 作用 头文件名称 作用
stdio.h 输入输出 local.h 地区化
string.h 字符串处理 windows.h 系统函数
math.h 数学函数 errno.h 错误处理
stdlib.h 数值转换、内存分配等问题 ctype.h 字符处理
time.h 时间函数 signal.h 信号处理

程序设计方法

  1. 顺序结构程序设计:一般程序都是顺序结构
  2. 选择结构程序设计:if 语句,weitch case 语句
  3. 循环结构程序设计:while 循环语句,do while 循环语句,for 循环语句

循环语句

switch . . . case循环

说明:

  1. 表达式必须是整型字符型表达式,每个常量表达式必须是相应的整数或字符,且两个常量值不能相同;
  2. 该结构从某-一个case进入后,将执行该入口中的语句组及后面所有语句组,如果只需执行一个语句组的操作,应在该语句组中加break语句跳出switch结构。

注意

如果case语句中要重新定义新的变量,那么必须用花括号把case内的语句括起来,不然要报错。

for循环

for ( 表达式1; 表达式2; 表达式3 )

{ 循环体; }

注意事项:

  1. for语句中三个表达式都可以省略,但是 “ ; ” 不能省略
  2. 如果省略表达式1,则应该在for语句前给循环变量赋初值
  3. 如果省略表达式2,循环陷入死循环,应该在其它位置安排检测及退出循环机制
  4. 如果省略表达式3,则应该其它位置安排使循环趋向于结束的工作

while循环

while(表达式) {

​ 语句顺序;

}

执行过程:当表达式为真时,执行语句序列,执行完后再跳到while语句,表达式为假时执行循环体的下一一个语句。

注意

  1. while循环先判断后循环
  2. 循环体应该写成复合语句的形式
  3. 避免“死循环”

do . . . while循环

do {

​ 语句序列;

}while(表达式);

执行过程:先执行语句序列,再判断循环条件,
如果表达式为真,再跳到do语句,表达式为假时执行循环体的下一个语句。

注意

  1. do . . . while先执行循环体再判断条件
  2. 循环体应该写成复合语句的形式
  3. 避免“死循环“

嵌套循环是指-一个循环语句的循环体内包含另一个完整的循环结构。

终止语句

breaak语句

  1. 格式:break;
  2. 功能:强行结束循环switch选择分支,转向执行循环语句或选择结构的下一条语句。

continue语句

  1. 格式:continue;
  2. 功能:跳过循环体其余语句,转向计算“循环变量增值”表达式(for循环),或者转向“循环条件”的判定(while和do-while循环)。

两者对不同循环的区别

UQYFF1.png

UQYVSK.png

UQt1HJ.png

函数

函数:完成特定工作的独立程序模块

作用

  1. 提高程序开发的效率,使程序易于管理降低程序的复杂性。
  2. 函数可以被重复使用

函数:包含标准库函数和自定义函数;也可以分为有参函数和无参函数。

1
2
3
4
[函数类型]函数名(函数类型 参数[, 数据类型 参数2 . . . .]) {
说明语句部分;
可执行语句部分;
}

有参数时称为有参函数,没参数时称为无参函数,无参函数在括号中用void。

U1hFQf.png

函数说明的两种方法

谁调用谁说明:函数说明语句放在调用函数的函数体中。

1
2
3
4
void main(void) {
int maxnum(int x, int y);
. . .
}

预先统一说明:函数说明通常放在所有函数定义体之前。在这种方式下,所有调用函数都无需再对自定义函数进行说明。
如,可以将对maxnum()函数的说明放在程序文.件开始处:

1
2
3
4
int maxnum(int x, int y);
void main(void) {
. . .
}

显然,对于被多个函数调用的自定义函数而言,采用预先统一说明方式进行说明,可以有效地减少函数说明次数。
注意:如果被调用函数的定义体,出现在调用函数之前,可以缺省说明。

函数的嵌套

函数的嵌套调用:在调用-个函数的过程中又调用另一个函数。

函数的递归调用

函数的递归调用即在调用函数时直接或间接的调用函数自身

函数递归调用时一定要存在可使递归调用终止的条件,否则导致出现无限递归

内部函数

内部函数(又称静态函数):只能被本文件中其他函数所调用的函数。

1
2
3
static 类型标识符 函数名(形参表)

例如:static int max(int a, int b)

因为内部函数只在本文件内部起作用,所以在不同的文件中可以有相同的函数名,互不影响。

外部函数

外部函数:能够被其他文件调用的函数。

1
2
[extern] 类型标识符函数名(形参表)
例如:extern int max(int a,int b以}

调用外部函数时,要将外部函数写在头文件中,在需要调用外部函数的文件中使用include命令将头文件包含进来,然后可直接调用头文件中的外部函数。

头文件的创建与应用

头文件分类

头文件用来保存程序的声明,包括:功能函数原型、数据类型的声明、数据接口声明等。

头文件包含:系统自带头文件和用户自定义头文件

提醒:自定义头文件只存在本机,所以只能在本机用,如果要在其他机器上使用需要将自定义头文件复制到与需要使用的源文件同-路径,或者include文件夹下。

自定义只需要把你所需要的函数保存在一个.h的文件中,该头文件是可以添加好多的函数,但是要注意那些函数是不能用static来修饰的,并且头文件放的位置也有要求。

数组

一维数组

1
2
3
类型标识符 数组名[整型常量表达式]

数据类型 数组名[常量表达式] = {初值表}

一维数组可在定义时初始化即给数组元素赋初值

1
2
3
int arr[6] = {1,2,3,4,5,6};		//给数组中的每一个元素赋值
int arr[6] = {1,2,3}; //给数组中前三个元素赋值
int arr[6] = {0}; //给数组中所有数据元素赋值为0

注意

  1. 对数组所有元素赋初值,此时数组定义中数组长度可以省略
  2. 对数组部分元素赋初值,此时数组长度不能省略
  3. 对数组的所有元素赋初值0

一维数组的引用

一维数组元素的引用形式:数组名[下标] 数组的下标从0开始

二维数组

1
类型标识符 数组名[整型常量表达式1][整型常量表达式2]

二维数组可在定义时初始化即给数组元素赋初值

1
2
3
4
int arr[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6}};//分行给二维数组中所有元素赋值
int arr[3][4] = {1,2,3,4,2,3,4,5,3,4,5,6};//不分行给二维数组中元素赋值
int arr[][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6}};//二维数组所有元素赋初值,第一维的长度可以省略
int arr[3][4] = {{1,2,3}.{2,3},{3,4,5,6}};//对每行部分元素赋初值

二维数组的引用

二维数组元素的引用形式:数组名[下标1] [下标2] 数组的下标从0开始

总结

  1. 二维数组中每个数据元素的数据类型相同。
  2. 二维数组初始化时,可以分行给赋值,也可以不分行赋值;对二维数组所有元素赋初值,二维数组第一维的长度可以省略。
  3. 二维数组通过下标引用数组元素,行下标与列下标都是从0开始。

字符串

字符数组

字符数组也是数组,只是数组元素的类型为字符型。所以字符数组的定义、初始化、字符数组元素的引用与一般的数组类似。

1
char 数组名[整型常量表达式]

字符数组初始化

1
2
char str[100] = {'a','b','c','d'};//以字符常量的形式对字符数组初始化
char str[100] = "abcd";//以字符串的形式对字符数组初始化。系统会自动在最后一个字符后加'\0'

字符数组的输入与输出

  1. 逐个字符输入/输出:采用”%c”格式说明和循环,像处理数组元素一样输入输出。
  2. 整串输入/输出:采用“%S”格式符来实现。

U8mjpD.png

总结

  1. 字符数组与一维数组类似,定义与引用方法相同。
  2. 字符数组可以逐个字符操作,也可以作为字符串操作。
  3. 字符串的结束标志为’\0’,所以在定义字符数组长度时至少要是字符串的长度加1。

字符串处理函数

标准库函数是由系统建立的具有一定功能的函数。在使用库函数时要将库函数所在文件名用#include<>包含进来。

库函数优点

  1. 准确性
  2. 高效性
  3. 可移植性

函数

1.字符串输入gets(str)

功能:从键盘输入一个字符串(可包含空格,scanf不可以),直到遇到回车符,并将字符串存放到由str指定的字符数组(或内存区域)中。

2.字符串输出puts(str)

功能:从str指定的地址开始,依此将存储单元中的字符输出到显示器,直到遇到“字符串”结束标志

3.字符串长度strlen(str)

功能:统计str为 起始地址的字符串的长度(不包括“字符串结束标志”),并将其作为函数值返回。

4.字符串连接函数strcat(str1, str2)

功能:将str2为 首地址的字符串连接到str1字符串的后面。从str1 原来的’\0’(字符串结束标志)处开始连接。

5.字符串复制函数strcpy(str1, str2)

功能:将str2为首地址的字符串复制到str1为首地址的字符数组中。

6.字符串比较函数strcmp(strl, str2)

功能:将str1,str2为首地址的两个字符串进行比较,比较的结果由返回值表示。

比较大小:

  1. 当str1=str2,函数的返回值为:0;
  2. 当str1<str2,函数的返回值为:-1;
  3. 当str1>str2,函数的返回值为:1;

指针

变量的存储地址

变量存储空间的第一个 字节对应的内存地址可看作是存储地址

根据内存地址就可找到相应的存储单元,所以通常也把地址称为指针

指针变量:C语言允许用指针变量来存放地址
指针变量的值就是数据的内存存储地址

指针占4个字节,不管指向什么类型都是4个字节

指针变量的定义

类型标识符 *指针变量名 ;

说明:

  1. “指针变量名” 前面的 “*” 表示该变量是指针变量。
  2. “类型标识符” 表示该指针变量所指向的变量的数据类型。
  3. 一个指针变量只能指向一个同类型的变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
int *p;      //定义int型指针变量,变量名为p
float *pf; //定义float型指针变量,变量名为pf
char *pc; //定义char型指针变量,变量名为pc

int n;
int *p; ==> int *p=&n;
p = &n;

char str[20]; //定义char型变量名为str的一维字符数组
char *ps;
ps = str;

gets(ps); <==> gets(str);

注意

指针变量也可以被初始化为NULL,它的值为0。当指针值为零时,指针不指向任何有效数据,也称为空指针

*指针变量名

说明:

​ “*” 为指针运算符,表示指针变量所指向内存单位地址的数据内容

地址可以用指针表示

“&” :取地址运算符

“*” :指针运算符

1
2
3
4
char ch1[10], *ch2=ch1;
ch1++; //错误:lvalue required as increment operand

ch2++; //正确只能是这个

只是改变p的地址值,对ch1的值没有任何改变

指针与一维数组

int a[5];

int *p; ==> p = &a[0];

p = a; //将数组a的首地址赋值给指针p

a+1是a[1]的地址,a+i就是a[i]的地址

等价关系

  1. 数组元素地址:&(a[i]) <=\=> a+i <\==> p+i;
  2. 数组元素值:a[i] <=\=> (a+i) <\=\=> (p+i)

指针与二维数组

说明

  1. a[0]、a[1]、a[2]分别代表二维数组各行的首地址
  2. a[0]代表第0行中第0列元素的地址 —> &a[0][0]; a[0]+1即代表第0行第1列元素的地址 —> &a[0][1];
  3. 可用指针形式代表各行元素的首地址。a[0]等价于*(a+0) ,a[1]等价于*(a+1)。则*(a+i)+j,即代表第i行第j列元素的地址

a[i][j]的地址表示方法:

  1. &a[i][j]
  2. a[i]+j
  3. *(a+i)+j

a[i][j]的表示方式:

  1. a[i][j]
  2. *(a[i]+j)
  3. *(*(a+i)+j)
  4. (*(a+i))[j]

指针与函数

指针作函数参数
● 形参指针指向实参数组的首个元素的地址
● 指针作形参,可以改变指针所指向的实参变量的值

类型标识符 *函数名([形参列表]);

1
2
3
int *f(int *x, int *y);
f是函数名,x、y是形参
函数返回值为整型指针类型,也就是地址类型

注意

  1. 函数定义时,在函数名前加 “ “ 指明函数返回值为地址值*。
  2. 函数返回值语句return中指明函数返回的是地址值
  3. 主调函数中返回值的接收者也为指针类型

用字符指针操作字符串
● 字符指针可操作字符串
● 操作不确定字符串长度时,使用字符指针更节省内存空间.

结构体

结构体是一种构造类型,它由若干“成员”组成。每一个成员可以是一个基本数据类型或者又是一个构造类型。结构体既然是一种“构造”而成的数据类型,那么在使用之前必须先定义它,也就是构造它。

结构体定义

(一) 用struct定义结构体类型

1
2
3
4
5
struct[结构体名] {
类型标识符1 成员名1;
类型标识符2 成员名2;
.....
};
1
2
3
4
5
6
7
struct student {
char num[10];
char name[20];
char sex;
int age;
float score;
};

说明

  1. struct是关键字,在定义和使用中都不能省略。
  2. 结构体中的每个成员均需作为类型说明,结构体类型名和成员名的命名应符合标识符的命名规则。
  3. 成员名可以与程序中的变量名同名,二者不代表同一对象,互不干扰。
  4. 注意末尾的 “ ; ” 号必不可少

(二) 用typedef定义结构体类型

1
2
3
4
5
typedef struct[结构体名] {
类型标识符1 成员名1;
类型标识符2 成员名2
.....
}新类型标识符;
1
2
3
4
5
6
7
typedef struct student {
char num[10];
char name[20];
char sex;
int age;
float score;
}STU;

结构体赋初值

  1. struct 结构体名 结构体变量 = {初始数据};

    1
    2
    3
    4
    5
    6
    7
    8
    struct student {
    char num[10];
    char name[20];
    char sex;
    int age;
    float score;
    };
    struct student stu1 = {"102","Zhangping","M",18,78.5};
  2. {…….}结构体变量={初始化数据};

    1
    2
    3
    4
    5
    6
    7
    struct student {
    char num[10];
    char name[20];
    char sex;
    int age;
    float score;
    }stu1 = {"102","Zhangping","M",18,78.5};

    3.{…….}结构体变量={初始数据};

    1
    2
    3
    4
    5
    6
    7
    struct {
    char num[10];
    char name[20];
    char sex;
    int age;
    float score;
    }stu1 = {"102","Zhangping","M",18,78.5};

结构体变量成员的引用

  1. 引用结构体变量中的一个成员

    ​ 结构体变量名.成员名;

  2. 结构体嵌套时应逐级引用

    1
    2
    3
    stu1.birthday.year;
    stu1.birthday.month;
    stu1.birthday.day;
  3. 同一种类型的结构体变量间可之间赋值

    1
    2
    stu1 = stu2;
    stu1.birthday = stu2.birthday;

说明

一个结构体变量,系统分配给他的内存是成员中占内存最大者所需内存量

结构体大小计算

  • 1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
  • 2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
  • 3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)

主要关注的是第三条,就是按照最大的长度来填空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct x {					 //double最长 8个位置,按照8来填空
char a; //占第一个8中的开头 0
int b; //占第一个8中的 4-7 (int占4个,只会占4的倍数)
double c; //占第二个8中的 0-7 (占满)
}s1; //总共16个

struct x { //double最长 8个位置,按照8来填空
char a; //占第一个8中的开头 0
double b; //占第二个8的 0-7 (第一个8剩的7不够) (占满)
int c; //占第三个8的 0-3
}s2; //总共24个

struct x { //double最长 8个位置,按照8来填空
double a; //占第一个8的 0-7 (占满)
char b; //占第二个8的 0 (开头)
int c; //占第二个8的 4-7 (int占4个,只会占4的倍数)
}s3; //总共16个

struct x { //double最长 8个位置,按照8来填空
double a; //占第一个8的 0-7 (占满)
char b; //占第二个8中的开头 0
int c; //占第二个8的 4-7 (int占4个,只会占4的倍数)
char d; //占第三个8的 0 (重新开辟一个)
}s4; //总共24个

struct x { //double最长 8个位置,按照8来填空
int a; //占第一个8的 0-3
double b; //占第二个8的 0-7 (第一个8剩下的不够填) (占满)
char c; //占第三个8的 0 (第二个没有了)
int d; //占第三个8的 4-7
char e; //占第四个8的 0 (第三个没了)
}s5; //总共32个

struct x {
char a;
int b;
double c;
}; //占16个
struct y {
char a; //1个
x b;
}; //17个,但是要是8的倍数,所以是24个

指针占4个字节,不管指向什么类型都是4个字节

共用体

几个不同数据类型的变量共同占用一段内存的结构体称作 “共用体” 类型结构,简称共用体。共用体又称为 “联合体”。

定义共用体

(一)

  1. “union” 是定义共用体的关键字;
  2. “共用体名” 是用户定义的新类型变量名;
  3. 个成员的数据类型不相同;
1
2
3
4
5
union 共用体名 { 
类型1 成员1;
类型2 成员2;
. . . .
};

(二)

​ 在定义共用体类型的同时,定义共用体变量。

1
2
3
4
5
union 共用体名 {
类型1 成员1;
类型2 成员2;
. . . .
}变量名1, 变量名2;

定义共用体变量

1
2
3
4
union 共用体名 变量名;

union Score sc;
定义sc为共用体Score类型的变量

共用体起别名

1
2
3
4
typedef union 共用体名 别名;

typedef union Score Score; //以后可以直接使用Score,即Score=union Score
Score sc; <==> union Score sc;

共用体成员引用

共用体变量名.成员名

1
2
3
例如:sc.i;

共用体变量sc中的成员i;

UwTc5j.png

共用体变量所有成员共享同一段内存空间,他占用的内存空间是所需内存最大的成员的空间

共同体变量赋值

共用体各成员的空间共享,所以不能整体赋值和输出,在初始化也只能初始化一个成员

如果对两个及其以上的成员赋值,那么后一个会覆盖前一个的值,也就是说最后赋值的数据有效

枚举类型

枚举类型是用户自定义的一种数据类型。该类型必须为有限的取值范围,可以逐一列举出来。

1
enum week {Monday, Tuesday, Webnesday, Thursday, Friday, Saturday, Sunday};

enum:定义枚举类型的关键字

week:用户给新类型命名的名称

{. . . . . . }:枚举元素或枚举常量

枚举元素用大括号括起;枚举元素之间用逗号隔开;以分号结束定义;

enum 枚举类型名 {枚举常量1, 枚举常量2, . . . . . , 枚举常量n};

C语言中,系统会为每个枚举元素对应一个默认整数值,通常从 “ 0 ” 开始,并顺次加1。

如果要改变这种默认值,可以在定义时进行指定。

enum week {Monday=3, Tuesday, Webnesday, Thursday=0, Friday, Saturday, Sunday=7};

3,4,5,0,1,2,7;

如果只是赋值了一些变量,还有些变量没有赋值,那么没赋值的变量为前一个变量的值加一,

定义枚举变量

(一)先定义枚举类型,再定义枚举变量

1
2
3
4
enum week {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
enum week w1;

枚举类型名 变量名;

(二)定义枚举类型同时定义枚举变量

1
2
enum week {Monday, Tuesday, Webnesday, Thursday, Friday, Saturday, Sunday}w2, w3;
//变量写在分号之间,变量之间用逗号隔开。

文件

文件的分类

按内容分:源程序文件、目标文件、数据文件

按数据组织形式分:文件(字符)文件、二进制文件

文件类型指针

一个指针变量指向一个文件,这个指针称为文件指针,文件进行的各种操作都是通过指针来完成。

定义形式:FILE *指针变量名;

文件的打开与关闭

  1. 文件的打开:fopen()函数

    1
    2
    FILE *p;
    p=fopen(文件名, 文件使用方式); //返回的结果是文件打开的路径

    | 使用方式 | 处理方法 | 含义 | 指定文件不存在 | 指定文件存在 |
    | :———: | :———: | :——————————: | :——————: | :—————: |
    | “r” | 只读 | 为输入打开一个文本文件 | 出错 | 正常打开 |
    | “w” | 只写 | 为输入打开一个文本文件 | 建立新文件 | 覆盖 |
    | “a” | 追加 | 为输入打开一个文本文件 | 建立新文件 | 打开,追加 |
    | “rb” | 只读 | 为输入打开一个文本文件 | 出错 | 正常打开 |
    | “wb” | 只写 | 为输入打开二进制位文件 | 建立新文件 | 覆盖 |
    | “ab” | 追加 | 为输入打开二进制位文件 | 建立新文件 | 追加,打开 |

    1
    2
    3
    4
    if((fp=fopen("file", "r"))==NULL) {	//当需要打开的文件不存在时
    printf("不能打开该文件。\n");
    exit(0);
    }
  2. 文件的关闭(fclose()函数)

    函数调用形式:fclose(文件指针变量);

    注意:使用函数fopen()和函数fclose()应在程序的开头使用命令:#include

读写一个字符

  1. 将一个字符输出到文件——函数fputc()

    一般形式:fputc(ch, fp);

    功能:把字符变量ch的值输出到指针变量fp指向的文件

    说明:函数执行成功,其返回为被输出的字符ch,否则返回值为文件结束标志EOF,EOF是一个符号常量,再stdio.h头文件中被定义为-1

  2. 从文件读入一个字符函数fgetc()

    一般形式:ch=fgetc(fp);

    功能:从指针变量fp指向的文件中读取一个字符赋给字符变量ch。

    说明:函数返回值为读入的字符ch,如果读入的字符是问价结束标志EOF,则返回值为EOF。

读写一个字符串

  1. 从文件读入一个字符串函数fgets()

    一般形式:fgets(str,n,fp);

    功能:从指针变量fp指向的文件中读入n-1个字符,送到字符数组str中。

  2. 向文件输出一个字符串函数fputs()

    一般形式:fputs(str,fp);

    功能:把字符数组str中的字符串输出到指针变量fp指向的文件中,但字符串结束标志’\0’不输出。

格式化输入和输出

  1. 按指定格式从文件读入数据函数fscnaf()

    一般形式:fscanf(文件指针, 格式控制字符串,地址项表);

    功能:从文件指针指向的文件中按格式控制字符串指定的格式读取数据存入地址项表中变量的存储单元。

  2. 按指定格式向文件输出数据函数fprintf()

    一般形式:fprintf(文件指针,格式控制字符串,输出表列);

    功能:把输出表列中变量的值按指定格式输出到问价指针指向的文件中。

feof(*fp)函数:判断是否为文件尾,为文件尾时返回0,否则返回非0;


C语言
https://kd-happy.github.io/posts/2021/04-22b180539b358b.html
作者
KD
发布于
2021年4月22日
许可协议