周记(四)
来源:互联网 发布:apache windows 编辑:程序博客网 时间:2024/06/06 03:54
姓名
徐秀
周次
4
方向
3G
内
容
本周学习知识点:
(1)多维数组的指针
(2)C语言结构体和联合体
(3)内存管理
(4)文件操作
本周学习收获:
(5)(1)多维数组、const指针的使用
(6)指向数组指针的使用
(7)指向函数的指针的使用
(8)内存区域的划分标准,以及各个区域数据的使用
(9)静态分配和动态分配
(10)野指针的概念以及程序中避免出现野指针的方法
(11)内存泄露的概念,malloc函数原型的含义,功能和使用方式
(12)使用free进行动态内存的释放工作
(13)结构对象创建方式、初始化和赋值
(14)嵌套结构的定义,以及访问
(15)结构数组的使用意义,以及对结构数组的初始化、对数组成员的访问方式
(16)结构作为函数参数时,值传递和地址传递的区别,以及对程序带来的影响
(17)C文件的有关基本知识
(18)C语言文件操作中FILE指针的意义,以及使用方式
(19)使用fopen函数进行文件的打开,主要的四个文件打开方式的使用:“r”, “w” , “a”, “b”
文件处理函数的使用:fclose,feof,fputc,fputs,fgetc,fgets,fwrite,fread,fprintf,fscanf
学习总结:
1. 用指向数组的指针作函数参数
一维数组名可以作为函数参数,多维数组名也可作函数参数。
用指针变量作形参,以接受实参数组名传递来的地址。
可以有两种方法:
①用指向变量的指针变量
②用指向一维数组的指针变量
2.const int* p 和int const* p
1)两者意义是相同的
*p是const 类型的常量,值不可再改变
p是一个指针变量,因为p是变量,因此p的值(即指针的指向)可以被改变
2)p可以指向什么类型的对象?
p可以指向const对象也可以指向非const的对象
3)通过p间接访问对象
可访问*p,但p不能修改所指向对象的值
int* const
1)p本身是什么类型的对象?
p是一个被const修饰的指针变量,其值不可修改,在定义p时必须初始化
const* int a;或int * const a; //a是const,但*a可变
2) p可以指向什么类型的对象?
p只能指向int对象,不能指向const int的对象
3)通过p间接访问对象
通过p可以修改所指向对象的值
3.指向数组的指针:
数组指针(也称行指针)
定义: 类型名称 (*指针名)[数组长度];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
4.指针数组的基本概念:
一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。
定义一维指针数组的一般形式为
类型名 *数组名[数组长度];
指针数组作用:
指针数组比较适合用来指向若干个字符串,使字符串处理更加方便灵活
可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串
由于各字符串长度一般是不相等的,所以比用二维数组节省内存单元
5.数组指针和指针数组区别:
数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中i行j列一个元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]
6.指针的指针
指针的指针:指向指针变量的指针变量。指针的指针存放的是指针变量地址.
指针变量的指针变量(指针的指针)的定义:
类型 **指针变量名;
数组的指针是指向数组元素的指针;
指针数组的指针,也是指向其数组元素的指针。
指针数组的数组元素是指针,所以指向指针数组的指针就是指针的指针。也就是说,可以使用“指针的指针”指向指针数组。
7.函数的指针:函数的入口地址(函数的首地址)。C语言规定函数的首地址就是函数名,所以函数名就是函数的指针。
指向函数的指针变量:存放函数入口地址(函数指针)的变量,称为指向函数的指针变量。简称函数的指针变量。
函数可以通过函数名调用,也可以通过函数指针调用。
通过函数指针实现函数调用的步骤:
1)指向函数的指针变量的定义:
类型 (* 函数指针变量名)();
例如 int (*p)(); 注意:两组括号()都不能少。int表示被指向的函数的类型,即被指向的函数的返回值的类型。
2)指向函数的指针变量的赋值,指向某个函数:
函数指针变量名=函数名;
3)利用指向函数的指针变量调用函数:
(* 函数指针变量名)(实参表)
8.返回指针值的函数
一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。
这种带回指针值的函数,一般定义形式为
类型名 *函数名(参数表列);
9.
C语言程序中少量变化的数据用变量来处理。数量不宜多。
批量同类型数据的处理用数组。
不同类型的数据的集合用什么数据结构来存放呢?这就是本单元要介绍的内容:用结构体类型处理不同类型数据的集合。
用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体
10.定义结构体变量:
先声明结构体类型,再定义该类型变量
声明结构体类型struct Student,可以用它来定义变量
不指定类型名而直接定义结构体类型变量
其一般形式为:
struct
{ 成员表列 }变量名表列;
指定了一个无名的结构体类型 。
11.结构体变量初始化
结构变量初始化的格式,与一维数组相似:
结构变量={初值表}
不同的是:如果某成员本身又是结构类型,则该成员的初值为一个初值表。
注意:初值的数据类型,应与结构变量中相应成员所要求的一致,否则会出错。
12.结构体变量访问
表示结构变量成员的一般形式
结构变量名.成员名
int nNo = stBoy1.nNo;
嵌套结构变量成员的表示形式
结构变量名.成员名.成员名
int nMonth = stBoy1.stBirthday.nMonth;
13.结构体数组
(1)定义结构体数组一般形式是
① struct 结构体名
{成员表列} 数组名[数组长度];
② 先声明一个结构体类型,然后再用此类型定义结构体数组:
结构体类型 数组名[数组长度];
对结构体数组初始化的形式是在定义数组的后面加上:
={初值表列};
14.指向结构体的指针
指向结构变量的指针的定义
struct 结构名 *结构指针变量名 = &结构变量;
struct STUDENT *pStu = &boy;
通过指针访问结构变量的成员
结构指针变量名->成员名
int nNo = pStu->nNo;
(*结构指针变量名).成员名
int nNo = (*pStu). nNo;
结构体数组
将整个结构作为参数传递(值传递)
void Show(struct STUDENT stuObj);
将结构的地址作为参数传递(地址传递)
void Show(struct STUDENT *pStu);
15.共用体(联合体)
有时想用同一段内存单元存放不同类型的变量。
使几个不同的变量共享同一段内存的结构,称为 “共用体”类型的结构。
定义一个共用体的一般形式:
union 联合名
{
数据类型 成员名1;
数据类型 成员名2;
…
数据类型 成员名n;
};
16.使用共用体注意以下特点
(1) 同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一个成员,而不是同时存放几个。
(2)可以对共用体变量初始化,但初始化表中只能有一个常量。
(3)共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体变量中的一个成员赋值后,原有变量存储单元中的值就取代。
(4) 共用体变量的地址和它的各成员的地址都是同一地址。
(5) 不能对共用体变量名赋值,也不能企图引用变量名来得到一个值。
(6) 以前的C规定不能把共用体变量作为函数参数,但可以使用指向共用体变量的指针作函数参数。C99允许用共用体变量作为函数参数
(7) 联合体和结构体体可以互相嵌套
(8) 同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一个成员,而不是同时存放几个。
(9)可以对共用体变量初始化,但初始化表中只能有一个常量。
(10)共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体变量中的一个成员赋值后,原有变量存储单元中的值就取代。
17.文件
文件有不同的类型,在程序设计中,主要用到两种文件:
(1) 程序文件: 包括源程序文件(后缀为.c)、目标文件(后缀为.obj)、可执行文件(后缀为.exe)等。这种文件的内容是程序代码。
(2) 数据文件: 文件的内容不是程序,而是供程序运行时读写的数据,如在程序运行过程中输出到磁盘(或其他外部设备)的数据,或在程序运行过程中供读入的数据。如一批学生的成绩数据,或货物交易的数据等。
18.输入输出流
输入输出是数据传送的过程,数据如流水一样从一处流向另一处,因此常将输入输出形象地称为流(stream),即数据流。流表示了信息从源到目的端的流动。
文件标识符
文件要有一个唯一的文件标识,以便用户识别和引用。
文件标识包括三部分:
(1)文件路径
(2)文件名主干
(3)文件后缀
19.文件的分类
●从用户观点:
特殊文件(标准输入输出文件或标准设备文件)。
普通文件(磁盘文件)。
●从操作系统的角度看,每一个与主机相连的输入
输出设备看作是一个文件。
例:输入文件:终端键盘
输出文件:显示屏和打印机
从文件编码的方式来看
ASCII文件
ASCII文件也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。
二进制文件
二进制文件是按二进制的编码方式来存放文件的。
例如:mp3、rar 等。
20.文件操作的基本步骤
引入头文件(stdio.h )
定义文件指针
打开文件
文件读写
关闭文件
21.文件类型指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”
每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置等)
这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名为FILE。
声明FILE结构体类型的信息包含在头文件“stdio.h”中
一般设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量
22.用fopen打开文件
对文件读写之前应该“打开”该文件,在使用结束之后应“关闭”该文件。
所谓“打开”是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输入输出的数据)
在编写程序时,在打开文件的同时,一般都指定一个指针变量指向该文件,也就是建立起指针变量与文件之间的联系,这样就可以通过该指针变量对文件进行读写
所谓“关闭”是指撤销文件信息区和文件缓冲区
fopen函数说明
计在打开一个文件时,通知编译系统以下3个信息:
①需要访问的文件的名字
②使用文件的方式(“读”还是“写”等)
③让哪一个指针变量指向被打开的文件
23.格式化的方式读写文件
一般调用方式为:
fprintf(文件指针,格式字符串,输出表列);
fscanf (文件指针,格式字符串,输入表列);
如:
fprintf (fp,”%d,%6.2f”,i,f);
fscanf (fp,”%d,%f”,&i,&f);
24.使用二进制的方式读写一组数据:
一般调用形式为:
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
使用说明:
buffer:是一个地址
对fread来说,它是用来存放从文件读入的数据的存储区的地址
对fwrite来说,是要把此地址开始的存储区中的数据向文件输出
size:要读写的字节数
count:要读写多少个数据项
fp:FILE类型指针
说明:
fwrite是按一个字节块一个字节块的方式写到文件。
完成一次写操(fwrite())作后必须关闭流(fclose());
完成一次读操作(fread())后,如果没有关闭流(fclose()),则指针(FILE * fp)自动向后移动前一次读写的长度,不关闭流继续下一次读操作则接着上次的输出继续输出;
25.静态和动态的内存分配
静态分配
编译器在处理程序源代码时分配
动态分配
程序执行时按动态要求来分配
26.动态分配和静态分配的区别:
静态内存分配是在程序执行之前进行的, 因而效率比较高,但是它缺少灵活性,要求在程序执行之前就知道所需内存的类型和数量
静态对象是有名字的变量,我们直接对其进行操作. 而动态对象是没有名字的变量,我们通过指针间接地对它进行操作.
静态对象的分配与释放由编译器自动处理.动态对象的分配与释放, 必须由程序员显式地管理, 相对来说比较容易出错
27.
动态内存分配
stdlib.h里面定义了五种类型、一些宏和通用工具函数。 类型例如size_t、wchar_t、div_t、ldiv_t和lldiv_t; 宏例如EXIT_FAILURE、EXIT_SUCCESS、RAND_MAX和MB_CUR_MAX等等; 常用的函数如malloc()、calloc()、realloc()、free()、system()、atoi()、atol()、rand()、srand()、exit()等等。
对内存的动态分配是通过系统提供的库函数来实现的
主要有malloc,calloc,free,realloc这4个函数。
动态内存分配
1.malloc函数
其函数原型为
void *malloc(unsigned int size);
1)size这个参数的含义是分配的内存的大小(以字节为单位)
2)返回值:失败,则返回值是NULL(空指针)。
成功,则返回值是一个指向空类型(void)的指针 (即所分配内存块的首地址)
用malloc申请0字节的问题
另外还有一个问题,用malloc函数申请0字节内存会返回NULL指针吗?
可以测试一下,也可以与去查找关于malloc函数的说明文档。申请0字节内存,函数并不返回NULL,而是返回一个正常的内存地址,但是你却无法使用这块大小为0的内存。这好比尺子上的某个刻度,刻度本身并没有长度,只有某两个刻度一起才能量出长度。对于这点一定要小心,因为这时候if(p!= NULL)将起作用。
说明:
第一、malloc 函数返回的是 void * 类型,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。
第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成
int* p = (int *) malloc (1);
如果往里头存入一个整数,就会有3个字节无家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容全部被清空。
28.free函数
既然有分配,那就必须有释放。不然的话,有限的内存就会用光,而没有释放的内存却在空闲。
其函数原型为 void free(void *p);
其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值。
free函数看上去挺狠的,但他到底做了什么呢?其实他就做了一件事:斩断指针变量和这块内存的关系。从此p和那块内存之间再无瓜葛。
即指针变量p本身保存的地址并没有改变,但是他对这个地址的那块内存已经没有所有权了。
如果对p连续2次使用free函数,肯定会发生错误。因为第一次使用free函数时,p所属的内存已经被释放,第2次使用时,已经没有内存可以释放了。所以一夫一妻制。一个malloc,一个free。
29.内存泄露问题
计算机中最宝贵的资源就是内存。因此需要动态分配内存的程序一定要坚持“好借好还,再借不难”的原则
内存泄露(memory leak)
指一块动态分配的内存, 我们不再拥有指向这块内存的指针, 因此我们没有办法将它返还给程序供以后重新使用.
1)重新赋值
2)首先释放父块
30.野指针概念
“野指针”不是NULL指针,是指向“垃圾”内存的指针
成因主要有三种:
1)指针变量没有被初始化
2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针
3)指针操作超越了变量的作用范围 (不易发现)
31.野指针成因:访问越界
内存读取越界 (overread) 是指所读取的字节数多于它们应有的字节数。这个问题并不太严重,在此就不再详述了。下面的代码提供了一个示例。
野指针成因:指针释放后,没有设置为null
指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。
对授课教师意见建议:
- 周记(四)
- 周记(四)
- (四)
- 浅谈简约四策略(四)
- 四、U-boot的使用(四)
- 四天精通shell编程(四)
- HEVC代码追踪(四。四)
- 四、runtime之Method Swizzling(四)
- 《加密与解密》笔记四(四)
- django开发环境部署四(四)
- TCP/IP (四)
- 闲谈BCB(四)
- 幽雅BDE(四)
- JDBC 概述(四)
- JDBC 概述(四)
- 英文求职信(四)
- 恶斗EJB(四)
- PHP画图(四)
- DynamicResource与StaticResource的区别
- 《TCP/IP协议详解:卷一》读书笔记---IP、UDP、TCP协议基础
- Visual Studio 2010——C#的LinkLabel控件的使用
- 周记(三)
- 《编程导论(Java)》电子参考文献索引
- 周记(四)
- 开发者使用JasperReport——通过数据源生成报表
- poj 动态规划题目列表
- 调试技巧
- HDU 2159 FATE
- 周记(五)
- error 25541 failed to open xml file c:\windows\microsoftnet\framework\v4.0.30319\config\ machine.con
- Class文件结构
- 继承那些事。。。。实例说明(1)