C语言程序设计知识点总结归纳(全书)

目录

C知识点总结归纳目录

  • 第一章 程序设计和C语言
    • 一、C的入门小概念
    • 二、程序设计的问题
    • 三、首先要搞清楚编译器、编辑器和IDE的区别
  • 第二章 算法——程序的灵魂
    • 一、程序=算法+数据结构
    • 二、算法的特性
    • 三、怎样表示一个算法
    • 四、结构化程序的设计方法
  • 第三章 C程序设计——顺序程序设计
    • 一、常量和变量
      • 1、常量
      • 2、变量
      • 3、常变量
      • 4、标识符
      • 5、数据类型
    • 二、运算符和表达式
    • 三、C语句
      • 1、关于#include的用法
      • 2、关于#define的用法
      • 3、关于%的使用方法
    • 四、数据的输入输出
      • 1、相关概念
      • 2、用printf函数输出数据
      • 3、字符输入输出函数
  • 第四章 选择结构程序设计
    • 一、选择结构和条件判断(略)
    • 二、用if语句实现选择结构(略)
    • 三、关系运算符和关系表达式
    • 四、逻辑运算符和逻辑表达式(略)
    • 五、条件运算符和条件表达式(略)
      • 优先级一览表
    • 六、选择结构的嵌套(略)
    • 七、用switch语句实现多分支选择结构(略)
  • 第五章 循环结构设计
    • 一、关于循环结构中break、continue、return和exit的区别
    • 二、第五章课后习题答案
  • 第六章 利用数组处理批量数据
    • 一、怎样定义和引用一维数组
      • 1、 怎样定义一维数组
      • 2、怎样引用一维数组元素
      • 3、一维数组的初始化
    • 二、怎样定义和引用二维数组
      • 1、怎样定义二维数组
      • 2、怎样引用二维数组的元素
      • 3、二维数组的初始化
    • 三、字符数组
      • 1、怎样定义字符数组
      • 2、字符数组的初始化
      • 3、怎样引用字符数组中的元素
      • 4、字符串和字符串结束标志
      • 5、字符数组的输入输出
      • 6、使用字符串处理函数
    • 四、第六章课后习题答案
  • 第七章 用函数实现模块化程序设计
    • 一、为什么要用函数
    • 二、怎样定义函数
      • 1、为什么要定义函数
      • 2、定义函数的方法
    • 三、调用函数
      • 1、函数的调用形式
      • 2、函数调用时的数据传递
      • 3、函数调用的过程
      • 4、函数的返回值
    • 四、对被调用函数的声明和函数原型
    • 五、函数的嵌套调用(略)
    • 六、函数的递归调用(略)
    • 七、数组作为函数参数
      • 1、数组元素做函数实参
      • 2、一维数组名作函数传递
      • 3、多维数组名作函数参数
    • 八、局部变量和全局变量
      • 1、局部变量
      • 2、全局变量
    • 九、变量的存储方式和生存期
      • 1、动态存储方式和静态存储方式
      • 2、局部变量的存储类别
      • 3、全局变量的存储类别
      • 4、存储类别小结
    • 十、关于变量的声明和定义
    • 十一、内部函数和外部函数
      • 1、内部函数
      • 2、外部函数
    • 十二、第七章课后习题答案
  • 第八章 善于利用指针
    • 一、指针是什么
    • 二、指针变量
    • 三、通过指针引用数组
    • 四、通过指针引用字符串
    • 五、指向函数的指针
    • 六、返回指针值的函数
    • 七、指针数组和多重指针
    • 八、动态内存分配与指向它的指针变量
    • 九、指针的小结
  • 第九章 用户自己建立数据类型
    • 一、结构体
      • 1、定义
      • 2、定义结构体类型的变量
      • 3、结构体变量的初始化和引用
      • 4、结构体数组
      • 5、结构体指针
    • 二、共用体
      • 1、定义
      • 2、区别
      • 3、特点
    • 三、枚举类型
      • 1、定义
      • 2、要点
    • 四、用typedef声明新类型名
  • 第十章 C语言文件的读写
    • 一、字符读写
      • 1、fputc()函数;
      • 2、fgetc()函数
    • 三、字符串读写
      • 1、fputs()函数
      • 2、fgets()函数
    • 二、数据块读写
      • 1、 fread()数据块读函数
      • 2、fwrite()数据块写函数
    • 三、格式化读写
      • 1、fprintf()函数
      • 2、fscanf()函数
  • 打完收工!

第一章 程序设计和C语言

一、C的入门小概念

1. 什么是计算机语言?

机器语言即二进制。计算机发展的初期,一般计算机的指令长度为16,即以16个二进制数组成的一条指令。
计算机能够直接识别和接受的二进制代码成为机器指令。这种机器指令的集合称为机器语言。

2.符号语言(又称为符号汇编语言或汇编语言)

使用一些英文字母和数字表示一个指令。
如:ADD A,B 表示将寄存器A中的数与寄存器B中的数相加,放到寄存器A中。
这种符号语言计算机无法直接识别,需要用汇编程序软件把符号语言转为机器指令。即所谓的代真汇编

3.高级语言

20世纪50年代世界上第一个高级语言出世——FORTRAN语言。
由于其功能更强大,任何计算机都适用,且距离具体计算机较“远”故称为高级语言。
但计算机无法直接识别高级语言

需要经历以下步骤才能让计算机识别

Created with Raphaël 2.3.0 源程序(高级语言编译) 编译程序 目标程序(机器指令的程序)

注:高级语言经历经历了一下发展阶段:

Created with Raphaël 2.3.0 非结构化语言(如:BASIC/FORTRAN/ALGOL) 结构化语言(如:C/QBASIC/FORTRAN 77) 面向对象的语言(如:C++/C#/Visual Basic/Java)

4.C语言的主要优点

  1. 语言简介紧凑、方便灵活;
  2. 运算符丰富;
  3. 数据类型丰富;
  4. 具有结构化的控制语句,即C语言是完全模块化和结构化的语言;
  5. 语句限制不太严格,程序设计自由度大;
  6. C允许直接访问物理地址,进行位(BIT)操作,可直接对硬件进行操作;
  7. 可移植性好;
  8. 生成目标代码质量高,程序执行效率高。

5.C语言程序的结构

  1. 一个程序由一个或多个源程序文件组成

源程序文件可以包括以下3个部分:①预处理指令(#include <stdio.h>);②全局声明;③函数定义。

  1. 函数是C语言的主要组成部分

一个C程序是由一个或者多个函数组成的,其中必须包含一个main函数(有且仅有一个)

  1. 一个函数包括两个部分

函数的首部和函数体(函数体由声明部分和执行部分组成)

  1. 程序总是从main函数开始执行的(可以放置到任何位置)
  2. 程序中要求计算机操作是由函数中的C语句完成的
  3. 在每个数据声明和语句的最后必须有一个分号
  4. C语言本身不提供输入输出语句
  5. 程序应当包含注释

6.C程序的步骤与方法

流程总结如下

编译连接处理

xx.c文件

xx.obj文件

xx.exe文件

二、程序设计的问题

从确定问题到最后完成任务,一般经历以下几个工作阶段:
流程总结如下

问题分析

设计算法

编写程序

对源程序进行编辑.编译和连接

运行程序且分析结果

编写程序文档

第一章详细记录了各种基本知识概念,从第二章起主要记录重点。

三、首先要搞清楚编译器、编辑器和IDE的区别

编译器、编辑器和IDE的区别
编译器就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序。一个现代编译器的主要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 目标代码 (object code) → 链接器 (Linker) → 可执行程序 (executables)。

第二章 算法——程序的灵魂

一、程序=算法+数据结构

数据结构:**对数据的描述。**在程序中要指定用到哪些数据,以及这些数据的类型和数据的组织形式。
算法:**对操作的描述。**要求计算机进行操作的步骤,也就是算法。

算法

数值运算算法

求数值解

非数值运算算法

姓名排序及图书检索等

二、算法的特性

算法的特性

有穷性

确定性

有零个或多个输入

有一个或多个输出

有效性

三、怎样表示一个算法

  1. 用自然语言表示算法
  2. 用流程图表示算法(传统流程图BS、结构化流程图N-S)

三种基本结构:1、顺寻结构,2、选择结构,3、循环结构【当型(while)循环结构和直到型(until)循环结构】

  1. 用伪代码表示算法
  2. 用机器语言表示算法

四、结构化程序的设计方法

结构化程序的设计方法

自定向下

逐步细化

模块化设计

结构化编码

第三章 C程序设计——顺序程序设计

一、常量和变量

1、常量

常量有以下几种组成:

常量

整型常量

实型常量

十进制小数形式

指数形式

字符常量

普通字符

转义字符

字符串常量

符号常量

要区分符号常量和变量! 符号常量不占内存,只是一个临时的符号,代表一个值。为与变量名区别,习惯将符号常量用大写表示。
符号常量常用#define来指定某个符号代表一个常量,如下所示

#define PI 3.1415926       //表示PI代表的就是3.1415926,这个末尾是不能有分号的

2、变量

变量由以下三个部分构成:

变量

变量名

变量值

存储单元

变量代表一个有名字的、具有特定属性的一个存储单元。变量的值如名义所释,可以改变。

3、常变量

C 99 允许使用常变量,定义方法是在一个关键字前加const
如下:

const int a=3;

常变量与常量的异同点:常变量具有变量的基本属性:有类型,占存储单元,只是不允许改变其值。常变量是有名字的不变量,而常量是没有名字的不变量。

4、标识符

在计算机高级语言中,用变量、符号常量名、函数、数组、类型等命名的有效字符序列统称为标识符。

5、数据类型

数据类型

基本类型

派生类型

整型类型

浮点类型

基本整形int

短整型short int

长整型long int

双长整型long long int

字符型char

布尔型bool

单精度浮点型float

双精度浮点型double

复数浮点型float_complex,double_complex,long long_complex

枚举类型enum/空类型void

指针类型*

数组类型

结构体类型struct

共用体类型union

函数类型

二、运算符和表达式

C语言提供了一下运算符:

  1. 算术运算符
  2. 关系运算符
  3. 逻辑运算符
  4. 位运算符
  5. 赋值运算符
  6. 条件运算符
  7. 逗号运算符
  8. 指针运算符
  9. 求字节数运算符(sizeof)
  10. 强制类型转换运算符
  11. 成员运算符(.->)
  12. 下标运算符([])
  13. 其他

三、C语句

一个函数包含声明部分执行部分

C语句分为以下5类:

C语句

控制语句

函数调用语句

表达式语句

空语句

复合语句

最基本的语句:赋值语句

1、关于#include的用法

#include用法的问题,大家可以看看我这一篇:

https://blog.csdn.net/Songbook/article/details/79630777

2、关于#define的用法

#define的用法,大家可以看看我这一篇文章

https://blog.csdn.net/Songbook/article/details/79645982

3、关于%的使用方法

%的使用方法大家可以看看我这一篇

https://blog.csdn.net/Songbook/article/details/79646427

四、数据的输入输出

1、相关概念

1、输入输出是以计算机主机为主体而言的;
2、C语言本身不提供输入输出语句;
3、要在程序文件的开头用预处理指令#include把有关的头文件放在本程序中。

2、用printf函数输出数据

格式字符:

  1. d格式符:用来输出一个有符号的十进制整数;
  2. c格式符:用来输出一个字符;
  3. s格式符:用来输出一个字符串;
  4. f格式符:用来输出实数,以小数形式输出
    ①基本型,用%f
    ②指定数据宽度和小数位数,用%m.nf,如:%7.2f——代表数据占7位,小数2位。
    ③输出的数据向左对齐,用%-m.nf——与上相似,但当数据长度不超过m时,数据向左靠,右端补空格
  5. e格式符:用格式声明%e指定以指数形式输出实数。
  6. 其他格式符
    ①i格式符——与d格式符相同
    ②o格式符——以八进制数的形式输出
    ③x格式符——以十六进制数形式输出整数
    ④u格式符——用来输出无符号数据
    ⑤g格式符——用来输出浮点数系统自动选f格式或e格式输出

使用scanf应注意格式控制后面应当是变量地址,而不是变量名。

3、字符输入输出函数

putchar(c);//是put character的缩写,意为给字符,即从计算机向显示器输出一个字符;
getchar();//是get character的缩写,意为:取字符,即从键盘输入一个字符。且只能接受一个字符

第三章练习题答案已经手编出来了,有需要的可以看看哦
链接在下方
https://blog.csdn.net/Songbook/article/details/123154963

第四章 选择结构程序设计

一、选择结构和条件判断(略)

本章需要大家大量实操,概念什么的已经从这开始就不是那么重要了,重在练习!

二、用if语句实现选择结构(略)

三、关系运算符和关系表达式

比较符称为关系运算符

关系运算符及其优先次序

  1. < (小于)
  2. <= (小于等于)
  3. > (大于)
  4. >= (大于等于)
    以上优先级相同(高优先级)

以下优先级相同(低优先级)
5. == (等于)
6. != (不等于)

优先级从高到低:
算术运算符,关系运算符,赋值运算符

	int X=20,Y=10;

算数运算符
关系运算符

四、逻辑运算符和逻辑表达式(略)

逻辑运算符

五、条件运算符和条件表达式(略)

优先级一览表

六、选择结构的嵌套(略)

七、用switch语句实现多分支选择结构(略)

关于C里面的很多小细节将在代码中给大家呈现,我会酌情将课后大题答案放在每章结尾处,供大家取阅。欢迎大家批评指正!对于里面很多细节部分,时间原因我会在后期慢慢添加相关细节,大家也可以提出建议,我会尽能力弥补漏洞的!

第四章课后习题答案在此!
https://blog.csdn.net/Songbook/article/details/123166383

第五章 循环结构设计

这一章基本上都是算法,我会单独做完发链接,你们可以酌情查阅哦!

一、关于循环结构中break、continue、return和exit的区别

此处有一篇文章写的挺全的,大家可以->戳这<-来看看!

二、第五章课后习题答案

第五章的课后习题,我已经给大家弄出来了,有什么问题欢迎留言!!!
https://blog.csdn.net/Songbook/article/details/123209850

第六章 利用数组处理批量数据

1.数组是一组有序数据的集合;
2.用一个数组名和下标来唯一确定数组中的元素;
3.数组中的每一个元素都属于同一个数据类型。

一、怎样定义和引用一维数组

1、 怎样定义一维数组

int a[10];
/*
定义一维数组的一般形式为:
类型符   数组名[常量表达式];
*/

2、怎样引用一维数组元素

表示形式为:
数组名[下标]

int a[10];//这是定义数组,指定数组中包含十个元素
t=a[6];//这里的a[6]表示引用a数组中序号为6的元素

3、一维数组的初始化

1、在定义数组时对全部数组元素赋予初值:

int a[10]={0,1,2,3,4,5,6,7,8,9};

2、可以只给数组中的一部分元素赋值:

int a[10]={0,1,2,3};

3、如果想使一个数组中全部元素值为0:

int a[10]={0,0,0,0,0,0,0,0,0,0};
//或者这样
int a[10]={0};

4、在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长度:

int a[5]={0,1,2,3,4};
//或者这样
int a[]={0,1,2,3,4};

注意:
int a[10]={1,2,3,4,5}
代表的含义是:只初始化前5个元素,后5个元素为0。

二、怎样定义和引用二维数组

1、怎样定义二维数组

float a[3][4],b[5][11];//定义为3x4(3行4列)的数组,b为5行11列的数组。
//一般形式为:类型说明符  数组名[常量表达式][常量表达式];

2、怎样引用二维数组的元素

二维数组元素的表示形式为:
数组名[下标][下标]

注意:

int a[3][4];//定义a为3x4的二维数组
//但不存在a[3][4]这个元素

按照上面的定义:数组a中的行下标为0-2,列下标为0-3,因此a[3][4]已经超出了数组的范围。

3、二维数组的初始化

1、分行给二维数组赋初值;

int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

2、可以将所有数据写在一个花括号内,按数组元素在内存中的排列顺序对各元素赋初值;

int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

3、可以对部分元素赋初值;

int a[3][4]={{1},{5},{9}};

4、如果对全部元素都赋初值,则定义数组时对第1维的长度可以不指定,但第2维的长度不能省略。

int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12};
//或者
int b[][4]={{1,2,3},{},{8,6}};

三、字符数组

1、怎样定义字符数组

char c[4];
c[0]='l';
c[1]='o';
c[2]='v';
c[3]='e';

2、字符数组的初始化

char c[10]={'i','','a','m','','h','a','p','p','y'};//字符的初始化

3、怎样引用字符数组中的元素

可以引用字符数组中的一个元素,得到一个字符串。

#include <stdio.h>
int main(){
char c[10]={'i','','a','m','','h','a','p','p','y'};//字符的初始化
int i;
for(i=0;i<10;i++)
printf("%c",c[i]);
printf("\n");
return 0;
}

4、字符串和字符串结束标志

当遇到’\0’的的时候系统自动判定字符串结束,并把它前面的字符组成一个字符串
C系统再用字符串存储字符串常量时会自动加一个’\0’作为结束符

5、字符数组的输入输出

1、输出的字符串不包括结束符’\0’;
2、用”%s”格式符输出字符串的时候,printf函数中的输出项是字符数组名,而不是数组元素名;
3、如果数组长度大于字符串的实际长度,也只输出到遇’\0’结束;
4、如果一个字符数组中包含一个以上的’\0’,则遇第一个‘\0’时输出就结束;

#include <stdio.h>
#include <string.h>
int main(){
	char a[]={"abdjad\0\0\0asd"};
	puts(a); 
	printf("%d\n",strlen(a));
	return 0;
} 

程序输出的结果

说明当程序遇到’\0’便自动停止录入!

5、可以用scanf函数输入一个字符串:

scanf("%s",c);

6、使用字符串处理函数

以下字符串处理函数需要加上#include <string.h>头文件

#include <stdio.h>
#include <string.h>
int main(){
	char str[]={'C','h','i','n','a'};
	char str1[20],str2[20];
	//puts函数——输出字符串的函数
	//puts(字符数组)
	//puts输出时将字符串结束标志'\0'转换成'\n',即输出完字符串后换行!
	puts(str);
	//gets函数 ——输入字符串的函数 
	gets(str);//其函数值是字符数组的起始地址
	puts(str);
	//puts函数和gets函数一次只能输出或输入一个字符串 
	//strcat函数——字符串连接函数
	//strcat(字符数组1,字符数组2)作用是将两个字符串连接起来
	gets(str1);
	gets(str2); 
	puts(strcat(str1,str2));//将str1和str2连接起来,并将连接的值赋给str1! 
	puts(str1);
	puts(str2);
	//strcpy和strncpy函数——字符串复制函数
	//一般形式为:strcpy(字符数组1,字符数组2)
	puts(strcpy(str1,str2));//将str2的值复制给str1 
	/*
	注意:
	1、在复制前str1定义得足够大,以便容纳str2的值 ;
	2、数组一的str1必须携程数组名形式, 数组二str2可以使字符数组名,也可以是一个字符串常量:strcpy(sttr1,'china!');
	3、在复制之前若未对str1进行初始化,则str1的各字节中的内容是无法预知的;
	4、不能用赋值语句将一个字符串常量或字符数组直接给一个字符数组,只能用strcpy函数讲话一个字符串复制到另一个字符数组中去; 
	5、 可以用strncpy函数将字符串2中前面n个字符复制到字符数组1中去。 
	*/ 
	//strcmp函数——字符串比较函数
	printf("%d\n",strcmp(str1,str2));//比较字符串str1和str2
	//比较规则:若果全部字符相同,则认为两个字符串相等;若出现不相同的字符,则以第一对不相同的字符的比较结果为准(在英文字典中位置在后面的为大)
	//strlen函数——测字符串长度的函数 
	printf("%d\n",strlen(str1)); //测量字符串的长度并返回相应的值
	//strlwr函数——转换为小写函数
	puts(strlwr(str2));//将里面的所有大写字母转换成小写字母
	//strupr函数——转换为大写的函数
	puts(strupr(str2));//将里面的所有小写字母转换为大写字母
	return 0;
} 

四、第六章课后习题答案

https://blog.csdn.net/Songbook/article/details/123276276

第七章 用函数实现模块化程序设计

一、为什么要用函数

函数就是功能,函数是为了是程序更加精简、更有结构感和层次感而设计的!用来完成一些特定的功能,通过调用函数库从而使用它们,这也是模块化程序设计的思路。

二、怎样定义函数

1、为什么要定义函数

定义函数包括以下几个内容:

  1. 指定函数的名字,以便以后按名调用;
  2. 指定函数的类型,即函数返回值的类型;
  3. 指定函数的参数的名字和类型,以便在调用函数时向他们传递数据。对无参函数不需要这项;
  4. **指定函数应当完成什么操作,也就是函数是做什么的,即函数的功能。**是在函数体中解决的。

注:在文件开头写#include <stdio.h>

2、定义函数的方法

  1. 定义无参函数;如:void
  2. 定义有参函数;如:int max(int x,int y){ int z; z=x+y; return(z);}
  3. 定义空函数;如:void sum{}

三、调用函数

1、函数的调用形式

一般调用形式:函数名(实参表列)

  1. 函数调用语句;
  2. 函数表达式;
  3. 函数参数;函数参数分为形参和实参

2、函数调用时的数据传递

  1. 形式参数和实际参数

在调用有参函数式,主调函数和被调函数之间有数据传递关系。
在定义函数式函数名后面括号中的变量名称为 “形式参数” 或“虚拟参数”
在主调函数中调用一个函数时,函数名后面括号中的参数称为 “实际参数”

  1. 实参和形参间的数据传递

在调用函数的过程中,系统会将实参的值传给形参,其中的数据传递称为 “虚实结合”

3、函数调用的过程

1、形参在为调用之前不占内存存储单元,只有调用的时候系统才会给它分配内存单元;
2、当系统调用的时候,会将实参的值换递给形参;
3、函数调用期间,形参已经分配了临时的存储空间,即已经有值故而直接使用形参进行相关运算;
4、被调用的函数会通过return将值传递回调用函数里面;
5、当调用结束,给形参的存储空间自动释放。

当函数不进行申明自动默认为int类型!

4、函数的返回值

  1. 函数的返回值是从return中获取;
  2. 函数值的类型与函数定义有关;
  3. 当函数值定义为void即不返回值的函数不用进行return,当没有void就需要进行return返回相应的结果。即:函数类型决定返回值的类型。
  4. 对于不返回值的番薯,应当是定义函数为void类型。

四、对被调用函数的声明和函数原型

  1. 被调用的函数必须是已经定义的函数;
  2. 如果使用的是库函数,必须在开头调用#include指令,将有关的库函数所需的信息包含的文件夹中来;
  3. 如果使用的是自己定义的函数,且函数的位置在调用它的函数后面,则需要在主函数中对被调用的函数进行声明。即:简概而论就是将定义的函数头放到主函数中。

五、函数的嵌套调用(略)

六、函数的递归调用(略)

七、数组作为函数参数

1、数组元素做函数实参

在用数组元素做函数实参时,把实参的值传递给形参,是“值传递”方式。数据传递的方向是从实参传到形参,单向传递。

2、一维数组名作函数传递

  1. 用数组名做函数的参数,应该在调用函数和被调用函数都分别定义数组;
  2. 实参和形参的类型应该一致;
  3. 定义形参数组的大小是无任何意义的;
  4. 形参数组可以不指定大小,在定义后面跟一个空的括号即可。

3、多维数组名作函数参数

可以用多维数组名作为函数的实参和形参,在被调用函数中对形参数组定义时可以指定每一维的大小,也可以省略第一维的大小说明。

//以下是合法的
int a[4][8];
int b[][9];
//以下是不合法的
int c[][];
int d[6][];

八、局部变量和全局变量

定义变量可能有以下三种:

  1. 在函数的开头定义;
  2. 在函数内的复合语句内定义;
  3. 在函数的外部定义。

1、局部变量

在函数内部定义变量,该变量的生命周期只有在该函数结束的时候自动“死亡”。因此这类函数是不能在函数的外部使用的,以上被称为“局部变量”;

注意以下几点:

  1. 在主函数定义的变量,也只能在主函数内部使用,不能在其他函数中使用;
  2. 不同函数中可以定义相同名字的变量,互不影响;
  3. 形式参数也属于局部变量;
  4. 在复合语句中(即程序块)定义的变量,生命周期在该复合语句结束的时候自动释放,因此也只能在复合语句中使用。

2、全局变量

在函数外部定义的变量称为外部变量,外部变量是全局变量。它的生命周期为整个文件,当整个文件运行结束的时候,系统才会释放它的存储空间。

全局变量有利有弊,它即可以增加函数之间的数据联系,也为数据值得改变埋下了隐患。

因此建议在不必要的情况尽量减少使用全局变量:

  1. 全局变量的生命周期是整个文件,因此若无必要则浪费系统存储单元空间;
  2. 它会使函数的通用性降低,在划分模块要求将模块的“内聚性”强、与其他模块的“耦合性”弱,即尽量封闭;
  3. 使用全局变量会降低程序的清晰性。

九、变量的存储方式和生存期

1、动态存储方式和静态存储方式

变量可以分为全局变量局部变量,即变量的生存周期长短不一。
变量的存储有两种方式:静态存储方式动态存储方式

静态存储方式:在程序运行期间有系统分配固定的存储空间的方式;
动态存储方式:程序运行期间根据需要进行动态分配存储空间的方式。

内存中供用户使用的存储空间的情况:

  1. 程序区;
  2. 静态存储区;
  3. 动态存储区。

全局变量存储在静态存储区。

在动态存储区存放以下数据:

  1. 函数形式参数;
  2. 函数中定义的没有用关键字static声明的变量,即自动变量(auto);
  3. 函数调用时的现场保护和返回地址等。

在C语言中,每一个变量和函数都有两个属性:数据类型(整形、浮点型等)和数据的存储类别(静态存储和动态存储)
C语言的存储类别有四种:自动的auto、静态的static、寄存器的register、外部的extern

2、局部变量的存储类别

1.自动变量

在调用函数的时候,系统会自动分配这些变量的存储空间,并在函数调用结束的时候自动释放这些空间,这类变量称为自动变量。
自动变量一般用auto作为存储类别的声明,实际上,关键字auto可以省略,不写auto则隐含指定为“自动存储类别”。
存储在动态存储区
2.静态局部变量
即生命周期为整个文件的开始而开始结束而结束。
一般用static进行声明。
1、对静态局部变量,只赋初值一次,在编译时赋初值;自动变量则是在函数调用时赋初值
2、对于静态局部变量来说,如果在编译之初不赋初值,系统自动赋初值0或空字符’\0’;对于自动变量则为系统随机的内存地址;
3、静态局部变量虽然生存周期长,但仍然是局部变量,其他函数不能引用它。
存储在静态存储区。

3.寄存器变量

一般对于使用非常频繁地变量定义为寄存器变量,因为寄存器的存储速度远远高于内存的存取速度,这样可以提高执行效率。
一般用register进行声明。
存储在CPU中的寄存器中。

3、全局变量的存储类别

1)在一个文件内扩展外部变量的作用域

外部变量的生存周期在它定义点到文件结束。它之前的部分不能引用它。可以使用extern进行外部变量声明,就可以从声明处之后引用该变量了。

2)将外部变量的作用域扩展到其他文件

在要调用外部变量的文件中用extern将外部变量的作用域扩展到本文件中来即可。

3)将外部变量的作用域限制在本文件中

当静态外部变量定义声明前加上static,则为限制只能用于本文件的外部变量。
>用static声明一个变量的作用是;
1.对局部变量用static声明,把它分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间始终存在;
2.对全局变量用static声明,则该变量的作用域只限于本文件模块。

4、存储类别小结

对一个数据的定义,需要指定两种属性:数据类型和存储类别。
C语言存储类别小结

十、关于变量的声明和定义

1、声明和定义的区别:

我们已经知道一个函数是由两个部分组成:声明部分执行部分
声明部分的作用是对有关的标识符(如:变量、结构体和共用体等)的属性进行声明。
声明和定义明显,函数的声明是函数的原型,二函数的定义是对函数功能的定义。
对被调用函数的声明是放在主调函数的声明部分中的,而函数的定义显然不在声明部分的范围内,他是一个独立的模块。

   在声明部分出现的变量通常有两种情况:
   一种是需要建立存储空间的;(如:int a;)
   另一种是不需要建立存储空间的。(如:extern a;)
   第一种称为:定义性声明。简称:定义
   第二种称为:引用性声明。

把建立存储空间的声明称定义,把不需要建立存储空间的声明称为声明

十一、内部函数和外部函数

根据函数能否被其他源文件调用,将函数区分为内部函数外部函数

1、内部函数

如果一个函数只能够被本文件中的其他函数所调用,称为内部函数。又称为静态函数

2、外部函数

在定义函数时,在函数首部的最左端加extern,则此函数是外部函数,可供其他文件调用。
函数首部可以为:extern int fun (int a,int b);
因此当在定义的时候省略了extern,则默认为外部函数。前面所有使用的程序都是外部函数。
因此在调用此函数的其他文件中,需要对此函数做说明,即使在本文件中调用一个函数,也要用函数原型进行声明,因此每次将main函数放在文件最下面,可以省略声明,但为了严谨也应该进行声明。

十二、第七章课后习题答案

大家可以看看这一篇!

第八章 善于利用指针

一、指针是什么

简而言之,指针就是存储的某个地址的一个空间,而地址指向的是某个变量的变量单元。因此将地址形象化地称为“指针”。意思是能通过这个指针找到以它为地址的内存单元。
对变量的访问可以分为直接访问间接访问
如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。

二、指针变量

定义指针变量:
类型名 *指针变量名;
int *p1,*p2;
左端的int是在定义指针变量时必须指定的“基类型”。

在定义指针变量时要注意:

  1. 指针变量前面的*表示该变量为指针型变量;
  2. 在定义指针变量时必须指定基类型。一个变量的指针的含义包含两个方面:一是一存储单元编号表示的纯地址,一是它指向的存储单元的数据类型;
  3. 整数型指针用int *,读作指向int类型的指针int指针
  4. 指针变量中只能存放地址。
p=&a;//给指针变量赋值
printf("%d",*p);//输出a的值,即以整数形式输出指针变量p所指向的变量的值
printf("%o",p);//以八进制的形式输出指针p的值,也就是a的地址

注意:C语言中实参变量和形参变量之间的数据传递是单向的“值传递”方式。

三、通过指针引用数组

所谓数组元素的指针就是数组元素的地址。
引用数组元素可以用下标法,也可以用指针法,即通过指向数组元素的指针找到所需的元素。

int *p=&a[0];
//等价于
int *p;
p=&a[0];
//等价于
int *p=a;
//即:将a[0]的首地址给指针变量p;

注意
在指针已指向一个数组元素时,可以对指针进行自加或自减运算。
1、如果指针p已经指向数组中的一个元素,则p+1指向同一个数组中的下一个元素,p-1指向同一数组中的上一个元素;
2、如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址;
3、其实在编译时候a[i]就是按照 *a[i+1]处理的,即按照数组首元素的地址加上相对位移量得到要找的元素的地址,然后找出该单元中的内容,即: *(p+5)=*(a+5)=a[5]。[]实际上就是变址运算符,即将a[i]按a+i计算地址,然后找出此地址单元中的值;
4、如果指针变量P1和P2都指向同一数组中的元素,如执行p2-p1,结果是p2-p1的值除以数组元素的长度,即:两个指针所指元素之间的距离。
两指针不能相加,相加毫无意义。
不用变化指针p的方式,换做变换数组a的方式即 :a++是不行的,因为a是常量,a++无法实现

*(p--)相当于a[i--];
*(++p)相当于a[++i];
*(--p)相当于a[--i];

指针在函数中的运用

C编译都是将形参数组名作为指针变量来处理的。

指针在多维数组元素中的运用

一个二维数组:a[n][n],其中a+1为a[1][0]。
在二维数组中:a+i,a[i],*(a+i),&a[i],&a[i][0]的值相同,即它们都代表同一地址,但基类型不同。

#include <stdio.h>
int main(){
	int a[3][4]={
		1,3,5,7,9,11,13,15,17,19,21,23
	};
	printf("%d,%d\n",a,*a);
	printf("%d,%d\n",a[0],*(a+0));
	printf("%d,%d\n",&a[0],&a[0][0]);
	printf("%d,%d\n",a[1],a+1);
	printf("%d,%d\n",&a[1][0],*(a+1)+0);
	printf("%d,%d\n",a[2],*(a+2));
	printf("%d,%d\n",&a[2],a+2);
	printf("%d,%d\n",a[1][0],*(*(a+1)+0));
	printf("%d,%d\n",*a[2],*(*(a+2)+0));
	return 0;
}

指针在二维数组中的应用

由于分配内存情况不同,所显示的地址可能是不同的,但是上面显示的地址是有共同规律的。

int (*p)[4];//表示定义p为一个指针变量,它指向包含4个整形元素的一维数组;
int *p[4];//表示的是指针数组。(因为[]的运算优先级别高所以会从左至右运行。)

四、通过指针引用字符串

1、字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),绝不是将字符串放到字符指针变量中;
2、可以对字符指针变量赋值,但不能对数组名赋值;
3、可以在定义时对各元素赋初值,但不能用赋值语句对字符数组中全部元素整体赋值;
4、编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元;
5、指针变量的值是可以改变的,而字符数组名代表一个固定的值,不能改变;
6、字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的;
7、如果定义了字符指针变量,并使它指向了数组a的首元素,则可以用指针变量带下标的形式引用数组元素,同样,可以用地址发引用数组元素;但若未指向数组,则无法用此类形式引用数组中的元素;
8、用指针指向格式字符串,可用它代替printf函数中的格式字符串

char *ff;
ff="a=%d,b=%c\n";
printf(ff,a,b);
//相当于
printf("a=%d,b=%c\n",a,b);//这种printf函数称为可变格式输出函数。

五、指向函数的指针

函数名代表的是函数的起始地址

#include <stdio.h>
#include <math.h>
int main(){
   int max(int x,int y);
   int (*p)(int x,int y);//若*p的括号去掉则由于优先级的关系成为了申明一个p函数 而我们是在定义一个指向函数的指针变量 
   int a,b,c;
   p=max;//使p指向max函数
   printf("input a and b:\n");
   scanf("%d%d",&a,&b);
   c=(*p)(a,b);
   printf("最大的值为:%d\n",c); 
   return 0;
}
int max(int x,int y){
   int z;
   x>y?z=x:z=y;
   return(z);
}

指针变量调用所指向的函数

1、定义指向函数的指针变量,只能指向在定义时指定的类型的函数,且一个指针变量可以先后指向同类型的不同函数;
2、如果要用指针调用函数,必须先使指针变量指向该函数;
3、再给函数指针变量赋值是,只给出函数名而不给参数;
4、用函数指针变量调用函数时,将(*p)代替函数名即可;
5、函数指针是不能够进行算术运算的;
6、函数名只能调用所定义的一个函数,而通过指针变量则比较灵活。

#include <stdio.h>
#include <math.h>
int main(){
	int max(int x,int y);
	int min(int x,int y);
	int (*p)(int x,int y);
	int a,b,c;
	printf("input a and b:\n");
	scanf("%d%d",&a,&b);
	printf("请输入 1 or 2 :\n");
	int n;
	scanf("%d",&n);
	if(n==1)
	p=max;
	else if(n==2)
	p=min;
	c=(*p)(a,b);
	if(n==1)
	printf("最大的值为:%d\n",c); 
	if(n==2)
	printf("最小的值为:%d\n",c); 
	return 0;
}
int max(int x,int y){
	int z;
	x>y?z=x:z=y;
	return(z);
}
int min(int x,int y){
	int z;
	x<y?z=x:z=y;
	return(z);
}

这是一个调用函数的雏形,大家要学会哦!

调用函数

指向函数的指针变量的一个重要用途是把函数的入口地址作为参数传递到其他函数。

六、返回指针值的函数

定义返回指针值的函数原型的一般形式为:
类型名 *函数名(参数列表);
对于初学者来说一般不太习惯,所以使用的时候要小心。
比如
int *a(int x,int y);
a是函数名,调用他以后能得到一个int *型(指向整形数据)的指针,即整型数据的地址。x和y势函数的形参,为整形。此函数是指针型函数(函数值是指针),最前面的int表示返回的指针指向整形变量。

七、指针数组和多重指针

一个数组,若其元素均为指针类型数据,称为指针数组。也就是说指针数组中的每一个元素都存放一个地址,相当于一个指针变量。

int (*p)[4];//这是指向一维数组的指针变量

格式:
类型名 * 数组名[数组长度];

指向指针数据的指针变量,简称指向指针的指针

char **p;

这里可以把char **p分为两部分来看,char * 和 (*p),后面的(*p)表示p是指针变量,前面的char *表示p指向的是char *型的数据。
数组元素只能存放地址,不能存放整数

main函数的形参

int main(int argc,char argv[])
其中的argc和argv就是main函数的形参,它们是程序的“命令行参数”。
argc:argument count(参数个数)的缩写。
argv:argument vector(参数向量)的缩写。它是
char指针数组,数组中每一个元素(其值为指针)指向命令行的一个字符串的首字符。
main的函数的形参不一定命名为argc和argv,可以使任意的名字,只是人们习惯使用argc和argv罢了

八、动态内存分配与指向它的指针变量

全局变量是分配在内存中的静态存储区的,非静态的局部变量是分配在内存中的动态存储区的,这个存储区是一个称为的区域。
存储动态分配区域临时存储的数据存放在一个特别的自由存储空间称为区。

对内存动态分配是通过系统提供的库函数来实现的主要有malloc、calloc、free、realloc这四个函数。

其中
用malloc函数开辟动态存储空间;
作用:在内存的动态存储区中分配一个长度为size的连续存储空间。size的类型定位无符号整型

void *malloc(unsigned int size);
malloc(100);//开辟100字节的临时分配域,函数值为其第一个字节的地址

用calloc函数开辟动态存储区;
作用:在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组

p=calloc(50,4);//开辟50x4个字节的临时分配域,把首地址赋给指针变量p
//用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。

malloc和calloc的区别:函数malloc()和函数calloc()的主要区别是前者不能初始化所分配的内存空间,而后者能。如果由malloc()函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;反之,如果这部分内存曾经被分配过,则其中可能遗留有各种各样的数据。也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常进行,但经过一段时间(内存空间还已经被重新分配)可能会出现问题。
函数calloc()会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那麽这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那麽这些元素通常会被初始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零。

用realloc函数重新分配动态存储区;
当通过malloc和calloc函数获得动态空间,想改变其大小,可以用recalloc函数重新分配
用realloc函数将p所指的动态空间大小改变为size。

用free函数释放动态存储区。
作用:释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。

p应该是最近一次调用calloc或malloc函数得到的函数返回值。

void指针类型。
C99使用基类型为void的指针类型,可以定义一个基类型为void的指针变量(void型变量)它不指向任何类型的数据。
void *p;//指向空类型或不指向确定的类型的数据。
将它值赋给另一指针变量时由系统对它进行类型转换,使之适合于被赋值的变量的类型。
void
称为空类型指针它不指向任意一种具体的类型数据,只提供一个纯地址。
这种空类型指针在形式上和其他指针一样,遵循C语言对指针的有关规定,它也有基类型,只是它的基类型是void。
void*型指针代表“无指向的地址”,这种指针不指向任何类型的数据。不能企图通过它存取数据,在程序中它只是过渡性的,只有转换为有指向的地址,才能存取数据。

九、指针的小结

  1. 指针的含义:

1、&a是变量a的地址,也可称为变量a的指针;
2、指针变量是存放地址的变量,也可说,指针变量是存放指针的变量;
3、指针变量也可称为地址变量,它的值是地址;
4、&是取地址运算符,也可说是去指针运算符,&a是a的地址,也是变量a的指针;
5、数组名是一个地址,是数组首元素的地址,也可说是一个指针,是首元素的指针;
6、函数名是一个指针(即函数代码区的首字节),也可以说函数名是一个地址;
7、函数的实参如果是数组名,传递给形参的是一个地址,也可说传递给形参的是一个指针;

  1. 在c语言中,所有的数据都是有类型的,此外不同类型的数据有不同的运算规则,即c语言中的数据都是有类型的数据,或称“带类型的数据”;对地址而言,也同样有类型,地址不是一个数值型数据,它是按指针型数据存储的方式存储的,指针型存储单元是专门用来存放地址的,指针型数据的存储形式就是地址的存储形式;

地址型的数据包含三个信息:
1、表示内存编号的纯地址
2、它本身的类型,即指针类型
3、以它为标识的存储单元中存放的是什么类型的数据,即基类型

  1. 区别指针和指针变量:

指针就是地址,而指针变量就是用来存放地址的变量。指针是没有值的,只有指针变量才有值,即指针变量的值是一个地址;

  1. 什么是“指向”?

地址就意味着指向。对于指针变量来说,把谁的地址存放在指针变量中,就说此指针变量指向谁,但并不是任何类型的数据的地址都可以存放在同一个指针变量中的,只有与指针变量的基类型相同的数据的地址才能存放在相应的指针变量中
void * 指针是一种特殊的指针,不指向任何类型的数据。 若要用此地址指向某种类型,应当先对地址进行类型转换;

  1. 在对数组的操作中正确使用指针,p=a;a是数组,p是指向数组中的元素而并非指向整个数组,准确说p指向a数组的首元素;
  2. 有关指针变量的归纳比较:
变量定义 类型表示 含义
int i; int 定义整形变量i
int*p int * 定义p为指向整型数据的指针变量
int a[5] int [5] 定义整形数组a,它有5个元素
int *p[4]; int *[4] 定义指针数组p,它由4个指向整型数据的指针元素组成
int (*p)[4]; int (*)[4] p指向包含4个元素的一维数组的指针变量
int f(); int () f为返回整形数值的函数
int *p(); int*() p为返回一个指针的函数,该指针指向整型数据
int(*p)(); int(*)() p为指向函数的指针,该函数返回一个整形值
int **p; int ** p是一个指针变量,它指向一个指向整型数据的指针变量
void *p; void * p是一个指针变量,基类型为void(空类型),不指向具体的对象
  1. 指针运算

1、指针变量加或者减一个整数;
2、指针变量赋值;
3、两个指针变量可以相减;(相加则无意义)
4、两个指针变量比较;(两个指针指向同一个数组的元素)
5、指针变量可以有空值;(该指针变量不指向任何变量,在引用指针变量之前应对它赋值)。

指针的优点
①提高程序效率;
②在调用函数时当指针指向的变量的值改变时,这些值能够为主调函数使用,即可以从函数调用得到多个可以改变的值;
③可以实现动态存储分配。

第九章 用户自己建立数据类型

一、结构体

1、定义

为了体现出某些数据的内在联系,将数据组成一个组合数据,可称为结构体。(C语言允许用户自己建立有不同类型的数据组成的组合型的数据结构)。
用户可在程序中自己建立一个结构体类型,类似struct student。
结构体一般形式为:

struct 结构体名
{成员表列};

结构体类型的名字是由一个关键字struct和结构体名组合而成的。
结构体名是由用户指定的,又称 “结构体标记”

2、定义结构体类型的变量

1、先声明结构体类型,再定义该类型的变量;

struct  Student s1,s2;

2、在声明类型的同时定义变量;

struct 结构体名
{成员表列
}变量名表列;

3、不指定类型名二直接定义结构体类型变量

struct
{成员表列
}变量名表列;

①结构体类型与结构体变量是不同的的概念,只能对变量进行赋值、存取或运算,不能对一个类型赋值、存取或运算。在编译的时候,对类型是不分配存储空间的,只对变量分配空间;
②结构体类型中的成员名可以与程序中的变量名相同,但二者不代表同一对象;
③对结构体变量中的成员 (即“域”),可以单独使用,它的作用与地位相当于普通变量。

3、结构体变量的初始化和引用

在定义结构体变量的时候,可以对它进行初始化,即赋初值,然后可以引用这个变量。
①在定义结构体便来哪个是可以对它的成员进行初始化

struct Student b={.name="zhang fang"};//“.”是成员运算符意为:b.name

②可以引用结构体变量中成员的值,方法为:结构体变量名.成员名。“.”是成员运算符,其优先级最高;
③如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级的找到最低一级的成员,且只能对最低一级的成员进行赋值或存取以及运算;
④对结构体变量的成员可以像普通变量一样进行各种运算;
⑤同类的结构体变量可以互相赋值;
⑥可以引用结构体变量成员的地址,也可以引用结构体变量的地址

4、结构体数组

结构体数组与数值型数组的不同之处在于,结构体数组的每个元素都是一个结构体类型的数据,它们都包含着结构体每一项的成员;
定义结构体数组的一般形式为:
①struct 结构体名
{成员表列}数组名[数组长度];
②结构体类型 数组名[数组长度];

#include <stdio.h>
#include <string.h>
	struct student{
	int num;
	char name[20];
	float score;
	};
int  main(){

	void sq(student a[5]); 
	student	p[5]={
	{
		10101,"zhang",78.0
	},{
		10103,"wang",98.5
	},{
		10106,"li",86.0
	},{
		10108,"ling",73.5
	},{
		10110,"sun",100.0
	}
	};

	sq(p);

	return 0;
}

void sq(student a[5]){
	a[0].num=10101;
	strcpy(a[0].name,"ling");
	a[0].score=23;

    student temp;//临时变量也需要定义为结构体类型
	const int n=5;
	int i,j,k;
	printf("The order is:\n");
	for(i=0;i<n-1;i++)
	{
		k=i;
		for(j=i+1;j<n;j++)
		if(a[j].score>a[k].score)
		k=j;
		temp=a[k];a[k]=a[i];a[j]=temp;	
	}
	for(i=0;i<n;i++)
	printf("%6d %8s %6.2f\n",a[i].num,a[i].name,a[i].score);
	printf("\n");
}
//简单编写的一个结构体的运用!

结构体

5、结构体指针

所谓结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针;
即:如果将一个结构体变量的起始地址存放在一个指针变量中,那么这个指针变量就指向该结构体变量;
指向结构体对象的指针变量即可指向结构体变量,也可指向结构体数组中的元素。指针变量的基类型必须与结构体变量的类型相同。
如:(*p).num; 或者 p->num;
(*p)表示p指向的结构体变量,(*p).num是p所指向的结构体变量中的成员num。*p两侧的括号不能省略,因为”.”的优先级最高,若省略则为*p.num就等价于*(p.num)了。

将一个结构体变量的值传递给另一个函数,有三个办法:
①用结构体变量的成员做参数;
②用结构体变量作实参;
③用指向结构体变量(或数组元素)的指针作实参。
->大家若还有疑惑可以看看我这一篇详细写的<-

二、共用体

1、定义

	//一般形式为:
	union 共用体名
	{成员列表
	}变量表列;

2、区别

共用体和结构体的区别:
结构体变量所占内存长度是各成员占的内存长度之和,每个成员分别占有其自己的内存单元;
而共用体变量所占的内存长度等于最长的成员的长度。

3、特点

①同一个内存段可用来存放几种不同类型的成员,但在每一瞬间只能存放其中一个成员,而不是同时存放几个。

#include <stdio.h> 
union Date{
int i;
char ch;
float f;
}a;


int main(){
	a.i=97;
	printf("a.i=%d\n",a.i);
	printf("a.ch=%c\n",a.ch);
	printf("a.f=%f\n",a.f);
	return 0;
}

共用体
②可以对共用体变量初始化,但初始化表中只能有一个常量。

union Date{
int i;
char ch;
float f;
}a={1,'d',2.3};//错误,不能初始化三个成员
union Data a={16};//正确,对第一个成员进行初始化
union Data a={.ch='j'};//C99可以对指定的成员进行初始化,但我使用的是C-free该运行出现错误

正确如下所示

#include <stdio.h> 
union Date{
int i;
char ch;
float f;
};

union Date a={99};

int main(){
	printf("a.i=%d\n",a.i);
	printf("a.ch=%c\n",a.ch);
	printf("a.f=%f\n",a.f);
	return 0;
}

共用体
③共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体变量中的一个成员赋值后,原有变量存储单元中的值就取代;
④共用体变量的地址和它的各成员的地址都是同一地址;
⑤不能对共用体变量名赋值,也不能企图引用变量名来得到一个值;
⑥以前的C规定不能把共用体变量作为函数参数,但可以使用指向共用体变量的指针作函数参数,C99允许用共用体变量作为函数参数;
⑦共用体类型可以出现在结构体类型的定义中,也可以定义共用体数组。

三、枚举类型

1、定义

如果一个变量只有几种可能的值,则可以定义为枚举类型,所谓“枚举”就是把可能的值一一列举出来,变量的值只限于列举出来的值的范围内,声明枚举类型用enum开头。

enum Week{sun,mon,tue,wed.thu,fri,sat};
Wk=mon;//指定枚举常量之一赋值给Wk;

week被定义为枚举变量,sun,mon…,sat被称为枚举元素或枚举常量

2、要点

①C编译对枚举类型的枚举元素按常量处理,故称枚举常量
②每一个枚举元素都代表一个整数,C编译按定义时的顺序默认它们的值为0,1,2,3,4,5,6,…
③枚举元素可以用来做判断表示,即if(wk>=mon)…

四、用typedef声明新类型名

1、简单地用一个新的类型名代替原有的类型名;

typedef int Integer;

2、命名一个简单的类型名代替复杂的类型表示方法:
①可命名一个新的类型名代表结构体类型:

typedef struct
{
int m;
int d;
int y;
}date;

②可命名一个新的类型名代表数组类型;

typedef int N[100];
N a;

③可命名一个新的类型名代表指针类型;

typedef char * S;
S p,s[10];

④可命名一个新的类型名代表指向函数的指针类型;

typedef int (*p)();
p p1,p2;

总结:按定义变量的方式,把变量名换上新类型名,并且在最前面加typedef,就声明了新类型名代表原来的类型;

typedef和#define的区别

#define是在预编译时处理的,它只能做简单的字符串替换
而typedef是在编译阶段处理的,它并非是简单的字符串替换,例如

typedef int Num[10];//采用的是融通定义变量的方法那样先生成一个类型名,然后去定义变量。
Num a;

第十章 C语言文件的读写

关于细节几部分,我就不仔细介绍了,首先,我只想说说关于C语言中的文件读写时一般常用的文件的读写函数的记载:

fgetc()函数和fputc()函数:字符的读写函数;
fgets()函数和fputs()函数:字符串的读写函数;
fread()函数和fwrite()函数:数据块的读写函数,主要针对二进制文件;
fscanf()函数和fprintf()函数:格式化的读写函数,主要针对文本文件。
文件随机读写函数:fseek()函数、rewind()函数和ftell()函数

下面,我来讲讲这些函数的格式,即使用说明:

一、字符读写

1、fputc()函数;

格式:fputc(c,fp);

功能:用来将一个字符写入到文件中。

在格式中fp是已经定义的文件指针变量;c为要写入的字符,可以是字符常量或字符型变量,该函数是有返回值的。
返回值:如果执行成功,返回写入的字符;否则,返回EOF,(EOF是C语言编译系统定义的文本文件结束标志,其值为-1,十六进制表示为0xff,在stdio.h中定义),表示写操作失败。

这里也可以用putc()函数来实现字符写功能,与fputc()完全等价,调用格式:putc(c,fp);

//例如:
#include <stdio.h>
#include <stdlib.h>
void main(){
	FILE *fp;
	char c[80],*pt=c;
	if((fp=fopen("c:\\1\\wenjiu1.txt","w"))==NULL)
	{
		printf("Cannot open the file,strike any key to exit!\n");
		getchar();
		exit(0);
	}
	printf("Input a string:\n");
	for(int i=1;i<=6;i++){
		gets(pt);
		while(*pt!='\0'){
			fputc(*pt,fp);
			pt++;
		}
		fputc('\n',fp);
	}
	fclose(fp);
}


2、fgetc()函数

格式: c=fgetc(fp);

功能:用来从指定的文本文件中读取一个字符。

其中,fp为文件指针,c为要写入的字符。该函数的功能是从指定的文件中读取一个字符,并赋值给字符型变量c。
返回值:读取成功返回读取字符,读取错误或遇到结束标志EOF,返回EOF。

*同上也可以用**getc()**函数来实现,与fgetc()完全等价,格式:getc(ch,fp); *

//fgetc();输入的第一行第一个字符会出现乱码问题,应该是我用的VC6.0版本问题
#include <stdio.h>
#include <stdlib.h>
void main(){
	FILE *fp;
	char c;
	if((fp=fopen("c:\\1\\wenjiu2.txt","r"))==NULL){
		printf("Cannot open the file,strike any key to exit!\n");
		getchar();
		exit(0);
	}
	while(c!=EOF){
		putchar(c);
		c=fgetc(fp);
	}
	fclose(fp);
}

三、字符串读写

1、fputs()函数

格式:fputs(s,fp);

功能:用来将一个字符串写入指定的文本文件。

其中,fp为文件指针,s可以是字符数组名,字符型指针变量或字符串常量。该函数的功能是将字符串s写入由fp指向的文件中,字符串末尾的‘\0’字符不允写入。

返回值:执行成功,返回所写的最后一个字符;否则,返回EOF。

//fputs

#include <stdio.h>
#include <stdlib.h>
void main(){
	FILE *fp;
	char s[][15]={"BeiJing","ShangHai","GuangZhou","NanJing","HangZhou"};
	if((fp=fopen("c:\\1\\wenjiu3.txt","w"))==NULL)
	/*只能相应文件,创建不了文件夹,fopen()函数会寻找相应位置文件夹然后在其中创建所需文件。
	当没有所需文件夹的时候,系统会自动跳转到NULL!*/
	{
		printf("Cannot open the file,strike any key to exit!\n");
		getchar();
		exit(0);
	}
	for(int i=0;i<6;i++){
		fputs(s[i],fp);
		fputs("\n",fp);
	}
	fclose(fp);
}//输出在文件中的第六行,会出现“'jb”这个字符串

2、fgets()函数

格式:fgets(s,n,fp);

功能:用于从指定的文件中读一个字符串到字符数组中。

其中,s可以是字符型数组名或字符串指针,n是指定读入的字符个数;fp为文件指针。n是一个正整数,表示从文件中最多读取n-1个字符,并将字符串指针s定位在读入的字符串首地址。fgets()函数从文件中读取字符直到遇到回车符或EOF为止,函数会在最后一个字符后加上字符串结束标志’\0’;若有EOF,则不予保留。

返回值:该函数如果执行成功,返回读取字符串;如果失败,则返回空指针NULL,这时,s中的内容不确定。

//fgets()函数

#include <stdio.h>
#include <stdlib.h>
void main(){
	FILE *fp;
	char s[6][15];
	if((fp=fopen("c:\\1\\wenjiu3.txt","r"))==NULL){
		printf("Cannot open the file,strike any key to exit!\n");
		getchar();
		exit(0);
	}
	for(int i=0;i<6;i++){
		fgets(s[i],15,fp);
		if(i%2==0)
			printf("%s",s[i]);
	}
	fclose(fp);
}



二、数据块读写

1、 fread()数据块读函数

格式:fread(char *ps,int len,int n,FILE *fp);

功能:用来指定文件中一次读取由若干个数据项组成的一个数据块,存放到指定的内存缓存区中,数据块的大小取决于数据块中数据项大小(字节数)和数据项的个数。

其中ps是用来指向存放数据块内存缓冲区的首地址,len是用来指出数据块中数据项的大小的整型数,fp是一个指向被操作 文件的指针。fread()函数一次读取数据块的大小等于len与n的乘积。
举例:要从fp指针所指向的文件中一次读取500个整数,存放到内存的buf缓存中,可以用下列语句实现这一功能:
fread(buf,sizeof(int),500,fp);

返回值:该函数正常返回值实际读取的数据项数,出错时返回NULL。

2、fwrite()数据块写函数

格式:fwrite(char *ps,int len,int n,FILE *fp);

功能:fwrite()函数用来将指定的内存缓冲区的数据块内的数据项写入指定的文件中。所写入的数据块的大小是由数据项的大小和数据项个数决定的。

其中的参数同fread()函数类似。

返回值:该函数参数与fread()函数返回参数相同,函数正常返回值是实际写入文件中的数据项个数。

//fread() AND fwrite()

#include <stdio.h>
#include <stdlib.h>
struct student{
	char name[10];
	int ID;
	int score;
};

void main(){
	struct student a[4],b[4],*pa,*pb;
	FILE *fp;
	pa=a;
	pb=b;
	if((fp=fopen("c:\\1\\wenjiu4.txt","wb+"))==NULL){
		printf("Cannot open the file,strike any key to exit!\n");
		getchar();
		exit(0);
	}
	printf("\nInput date\n");
	for(int i=0;i<3;i++,pa++)
		scanf("%s%d%d",&pa->name,&pa->ID,&pa->score);
	pa=a;
	fwrite(pa,sizeof(struct student),3,fp);
	rewind(fp);//内部位置指针移动到文件首位!
	fread(pb,sizeof(struct student),3,fp);
	printf("\n\nname		ID		score		\n");
	for(i=0;i<3;i++,pb++)
		printf("%-10s %-6s %-5d\n",pb->name,pb->ID,pb->score);
	fclose(fp);
}

三、格式化读写

1、fprintf()函数

格式:fprintf(fp, format, arg1, arg2,…,argn);

功能:fprintf()用来将输出项按指定的格式写入指定的文本文件中,其中格式化规定与printf()函数功能相似,所不同的只是fprintf()函数是将输出的内容写入文件中,而printf()函数则是在屏幕输出。

其中,fp为文件指针,format为指定的格式控制字符串;arg1~argn为输出项,可以是字符、 字符串或各种类型的数值。该函数的功能是按格式控制字符串format给定的格式,将输出项arg1,arg2,…,argn的值写入fp所指向的文件中。

返回值:如果函数执行成功,返回实际写入文件的字符个数;若出现错误,返回负数。

fprintf()中格式控制的使用与printf()相同。
例如:fprintf(fp,“r=%f,area=%f\n”,r,area);

//fprintf()函数

#include <stdio.h>
#include <stdlib.h>
void main(){
	FILE *fp;
	int data[1000];
	for(int i=0;i<1000;i++)
		data[i]=(rand()%1000);//产生0~999的随机整数
	if((fp=fopen("c:\\1\\wenjiu5.txt","w"))==NULL){
		printf("Cannot open the file,strike any key to exit!\n");
		getchar();
		exit(0);
	}
	for(i=0;i<1000;i++){
		if(i%20==0)
			fprintf(fp,"\n%5d",data[i]);
		else
			fprintf(fp,",%5d",data[i]);
		if((i+1)%20==0)
			fprintf(fp,"\n");
	}
	fclose(fp);

}


2、fscanf()函数

格式:fscanf(fp,format,arg1,arg2,…,argn);

功能:fscanf()用来按规定的格式从指定的文本文件中读取数据。它与scanf()函数的功能相似,都是按规定的格式读数据的函数,只是fscanf()函数读的对象不是键盘区,而是文件。

其中,fp为文件指针,format为指定的格式控制字符串;arg1~argn为输入项的地址。该函数的功能是从文件指针fp所指的文本文件中读取数据,按格式控制字符串format给定的格式赋予输入项arg1,arg2,…,argn中。

返回值:如果该函数执行成功,返回读取项目个数;如果遇到文件末尾,返回EOF;如果赋值失败,返回0.

fscanf()中格式控制的使用与scanf()相同。
例如:fprintf(fp,“%d,%d\n”,&a,&b);

//fscanf()函数

#include <stdio.h>
#include <stdlib.h>
void main(){
	FILE *fp;
	int data[1000];
	if((fp=fopen("c:\\1\\wenjiu5.txt","r+"))==NULL){
		printf("Cannot open the file,strike any key to exit!\n");
		getchar();
		exit(0);
	}
	for(int i=0;i<1000;i++){
		if(i%20==0)
			fscanf(fp,"\n%5d",&data[i]);
		else
			fscanf(fp,",%5d",&data[i]);
	}
	fseek(fp,0,SEEK_END);
	fprintf(fp,"\n\n");
	int j=0;
	for(i=0;i<1000;i++){
		{
			if(data[i]%7==0)
			{
				if(j%20==0)
					fprintf(fp,"%5d",data[i]);
				if((j+1)%20==0) fprintf(fp,"\n");
				j++;
			}
		}
		fclose(fp);
	}
}


打完收工!

历时一个多月,一个字一个字给敲上去的,里面融入了我自己的思考,当然很多权威性的解释还是沿用了书本上的,毕竟咱还是很知道天高地厚的。大家可以把这篇文章当个梳理思路的文章过一过,这里面也有很多漏洞存在,我会在后期一一完善和补充的,也希望大家发现什么不足留个言,感谢大家支持!!!
整本书知识点很多,有很多概念前后都重复介绍过好几遍,很适合新手阅读,当然也存在一定的深度!

版权声明:本文为博主作者:量子,相反而皆相成也原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/Songbook/article/details/123147978

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2024年1月6日
下一篇 2024年1月6日

相关推荐