周记(四)

来源:互联网 发布: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函数进行文件的打开,主要的四个文件打开方式的使用:“rw” ab

文件处理函数的使用:fclosefeoffputcfputsfgetcfgetsfwritefreadfprintffscanf

学习总结:

1. 用指向数组的指针作函数参数

一维数组名可以作为函数参数,多维数组名也可作函数参数。

用指针变量作形参,以接受实参数组名传递来的地址。

可以有两种方法:

①用指向变量的指针变量

②用指向一维数组的指针变量

2.const int* p int const* p

1两者意义是相同的

*pconst 类型的常量,值不可再改变

p是一个指针变量,因为p是变量,因此p的值(即指针的指向)可以被改变

2p可以指向什么类型的对象?

p可以指向const对象也可以指向非const的对象

3通过p间接访问对象

可访问*p,p不能修改所指向对象的值

int* const

1)p本身是什么类型的对象?

p是一个被const修饰的指针变量,其值不可修改,在定义p时必须初始化

const* int a;int * const a;   //aconst,但*a可变

2) p可以指向什么类型的对象?

p只能指向int对象,不能指向const  int的对象

3)通过p间接访问对象

通过p可以修改所指向对象的值

3.指向数组的指针:

数组指针(也称行指针)

定义类型名称 (*指针名)[数组长度];

()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。

4.指针数组的基本概念:

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

定义一维指针数组的一般形式为

     类型名 *数组名[数组长度];

指针数组作用:

指针数组比较适合用来指向若干个字符串,使字符串处理更加方便灵活

可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串

由于各字符串长度一般是不相等的,所以比用二维数组节省内存单元

5.数组指针和指针数组区别:

数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。

还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。

比如要表示数组中ij列一个元素:

*(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码。

二进制文件

      二进制文件是按二进制的编码方式来存放文件的。

      例如:mp3rar 等。

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(buffersizecountfp);

fwrite(buffersizecountfp);

使用说明:

buffer:是一个地址

fread来说,它是用来存放从文件读入的数据的存储区的地址

fwrite来说,是要把此地址开始的存储区中的数据向文件输出

size:要读写的字节数

count:要读写多少个数据项

fpFILE类型指针

说明:

fwrite是按一个字节块一个字节块的方式写到文件。

完成一次写操(fwrite())作后必须关闭流(fclose());

完成一次读操作(fread())后,如果没有关闭流(fclose()),则指针(FILE * fp)自动向后移动前一次读写的长度,不关闭流继续下一次读操作则接着上次的输出继续输出;

25.静态和动态的内存分配

静态分配

    编译器在处理程序源代码时分配

动态分配

    程序执行时按动态要求来分配

26.动态分配和静态分配的区别:

静态内存分配是在程序执行之前进行的因而效率比较高,但是它缺少灵活性,要求在程序执行之前就知道所需内存的类型和数量

静态对象是有名字的变量,我们直接对其进行操作而动态对象是没有名字的变量,我们通过指针间接地对它进行操作.

静态对象的分配与释放由编译器自动处理.动态对象的分配与释放必须由程序员显式地管理相对来说比较容易出错

27.

动态内存分配   

stdlib.h里面定义了五种类型、一些宏和通用工具函数。 类型例如size_twchar_tdiv_tldiv_tlldiv_t; 宏例如EXIT_FAILUREEXIT_SUCCESSRAND_MAXMB_CUR_MAX等等; 常用的函数如malloc()calloc()realloc()free()system()atoi()atol()rand()srand()exit()等等。

对内存的动态分配是通过系统提供的库函数来实现的

主要有malloccallocfreerealloc4个函数。

动态内存分配   

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应是最近一次调用callocmalloc函数时得到的函数返回值。

free函数看上去挺狠的,但他到底做了什么呢?其实他就做了一件事:斩断指针变量和这块内存的关系。从此p和那块内存之间再无瓜葛。

即指针变量p本身保存的地址并没有改变,但是他对这个地址的那块内存已经没有所有权了。

如果对p连续2次使用free函数,肯定会发生错误。因为第一次使用free函数时,p所属的内存已经被释放,第2次使用时,已经没有内存可以释放了。所以一夫一妻制。一个malloc,一个free

29.内存泄露问题

计算机中最宝贵的资源就是内存。因此需要动态分配内存的程序一定要坚持“好借好还,再借不难”的原则

内存泄露(memory leak)

指一块动态分配的内存我们不再拥有指向这块内存的指针因此我们没有办法将它返还给程序供以后重新使用.

1)重新赋值

2)首先释放父块

30.野指针概念

“野指针”不是NULL指针,是指向“垃圾”内存的指针 

成因主要有三种:

1)指针变量没有被初始化

2)指针pfree或者delete之后,没有置为NULL,让人误以为p是个合法的指针 

3)指针操作超越了变量的作用范围 (不易发现)

31.野指针成因:访问越界

内存读取越界 (overread) 是指所读取的字节数多于它们应有的字节数。这个问题并不太严重,在此就不再详述了。下面的代码提供了一个示例。

野指针成因:指针释放后,没有设置为null

指针pfree或者delete之后,没有置为NULL,让人误以为p是个合法的指针。它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。

对授课教师意见建议:

原创粉丝点击