《第5章 指针 教学PPT_190815ppt课件.pptx》由会员分享,可在线阅读,更多相关《第5章 指针 教学PPT_190815ppt课件.pptx(79页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第5章 指针 教学PPT_190815 第第5章章 指针指针 内存操作函数 指针与const 二级指针 计算机内存与C程序 指针与指针运算 特殊类型指针 5.1 计算机内存与C程序内存是计算机中暂时存放数据的地方,程序的执行与计算都要在内存中完成。 5.1 计算机内存与C程序1、物理内存地址内存具有线性存储的特点,并且内存具有真实的物理地址的映射,这意味着可以通过指针寻址的方式获取内存中的数据,即通过指针的方式获取地址或修改内存中的数据。 5.1 计算机内存与C程序左图是32位系统中内存的分布图,即CPU的最大寻址范围为4GB内存空间。左边十六进制用于表示映射的真实物理内存地址,右边的每一个小
2、方格代表一个bit位,即每一个寻址的地址代表一个字节的内存。 5.1 计算机内存与C程序真实的物理内存空间由操作系统虚拟内存映射进行管理,具有严格的地址区间划分。 5.1 计算机内存与C程序当程序被装载到内存中,内存可划分为7个部分: 系统内核空间:供操作系统使用,不允许用户访问。 栈空间:用于存储局部变量等数据,在为局部变量分配空间时,栈总是先分配高地址空间,再分配低地址空间,即栈内存的增长方式是从高到低。 动态库内存空间:用于存储程序运行时链接的库文件。 5.1 计算机内存与C程序 堆空间:由用户自己申请释放,其增长方式是从低到高。 读/写数据内存空间:读/写数据内存空间用于存储全局变量、
3、静态全局变量等数据。 只读代码/数据内存空间:用于存储函数代码、常量等数据保留区保留空间:内存的起始地址,具有特殊的用途,不允许用户访问。 5.1 计算机内存与C程序2、程序在内存中的布局内存具有线性存储的特点,并且内存的使用会有严格意义上的划分,程序在内存中的分布情况如下表。 5.1 计算机内存与C程序C语言程序编译运行过程中主要用到的内存空间包括栈、堆、数据段、代码段,这四个部分就是C程序员通常所说的内存四区。栈堆数据段代码段 5.1 计算机内存与C程序栈(Stack)是用来存储函数调用时的临时信息区域,栈也称为堆栈,一般只有10兆左右大小的空间栈顶地址和栈大小是系统预先规定好的。栈内存主
4、要用于数据交换,如函数传递的参数、函数返回地址、函数的局部变量等。 5.1 计算机内存与C程序栈内存由编译器自动分配和释放。栈具有先入后出的特点,由于栈由高地址向低地址增长,因此先入栈的数据地址较大,后入栈的数据地址较小。数据入栈后栈顶地址减小,数据出栈时栈顶地址增大。 5.1 计算机内存与C程序堆(Heap)是不连续的内存区域,各块区域由链表将它们串联起来。该区域一般由程序员分配或释放,若程序员不释放,程序结束时可能由操作系统回收(程序不正常结束则回收不了)。堆区的上限是由系统中有效的虚拟内存决定的,因此获得的空间较大,而且获得空间的方式也比较灵活。 5.1 计算机内存与C程序虽然堆区的空间
5、较大,但必须由程序员自己申请,而且在申请的时候需要指明空间的大小,使用完成后需要手动释放。同时堆区的内存空间是不连续的,容易产生内存碎片,因此堆中数据的执行速度比较慢。 5.1 计算机内存与C程序数据段(Data)也称为静态存储区,其中BSS(Block Start By Symbol)段用于存储未初始化或初始化为0的全局变量、静态变量(static修饰的变量),数据段data存储初始化的全局变量、静态变量(static修饰的变量)。被const关键字修饰的初始化全局变量和字符串常量,会存放在初始化的只读变量区域。 5.1 计算机内存与C程序代码段也称为文本段(Text Segment),存放
6、的是程序编译完成后的二进制机器码,存放处理器的机器指令,当各个源文件单独编译之后生成目标文件,经链接器链接各个目标文件并解决各个源文件之间函数的引用,可执行的程序指令从代码段获取后得以运行。在编程中如果访问到代码段内存空间中的数据,编译程序会出现段错误提示(Segmentation Fault)。 5.2 指针程序运行过程中产生的数据都保存在内存中,内存是以字节为单位的连续存储空间,每个字节都有一个地址,这个地址就称为指针。通过指针就可以获取保存在内存中的数据的地址。指 针 5.2.1 指针变量的概念指针是一种特殊的变量类型,指针在本质上是一个有类型,保存了内存地址的常量。程序在运行过程中,变
7、量、常量、函数等都保存在内存中,而内存是有固定地址编号的线性存储单元,而指针就是用来保存内存地址的,定义了指针变量就可以访问指针所指向内存空间的数据。 5.2.1 指针变量的概念1、指针与内存地址定义一个int类型变量a:int a=10;a在内存中的存储如右图。 编译器会根据变量a的类型int,为其分配4个字节地址连续的存储空间。假如这块连续空间的首地址为0 x0037FBCC,那么这个变量占据0 x0037FBCC0 x0037FBCF这四个字节的空间,0 x0037FBCC就是变量a的地址。5.2.1 指针变量的概念 因为通过变量的地址可以找到变量所在的存储空间,所以说变量的地址指向变量
8、所在的存储空间,地址是指向该变量的指针。5.2.1 指针变量的概念 若将编写程序比喻成购买火车票,程序执行就类似于验票乘车去往目的地。火车就是计算机内存,那么火车上有顺序排列的座位号就是内存中的地址编号,座位上的乘客就是存储在内存中的数据,通过座位号可以准确找到乘客,类似于使用地址访问内存中的数据。5.2.1 指针变量的概念 乘务员就是指针变量,乘务员通过查看座位号就能确认乘客信息,就像指针可以通过内存地址获取内存中的数据是一个道理。5.2.1 指针变量的概念 2、指针变量的定义存储变量a的内存地址为0 x0037FBCC,如果用一个变量保存该地址,如变量p,那么p就称为指向变量a的指针。5.
9、2.1 指针变量的概念 定义指针变量的语法如下:变量类型 *变量名;5.2.1 指针变量的概念 (1)变量类型指的是指针指向的变量的数据类型。即指针类型在内存中的寻址能力,如char类型决定了指针指向1个字节地址空间,int类型决定了指针变量指向4个字节地址空间。(2)*表示了定义的变量是一个指针变量类型,没有*符号就是定义的基本数据类型或构造数据类型。(3)变量名是存储内存地址的名称。即指针变量,其命名遵循标识命名规则。5.2.1 指针变量的概念 char* i;/char类型的指针变量iint* t;/int类型的指针变量tdouble* c;/double类型的指针变量clong* a;
10、/long类型的指针变量along double* s;/long double类型的指针变量sunsigned int* T;/unsinged int类型的指针变量T5.2.1 指针变量的概念 5.2.1 指针变量的概念一定要注意,指针就是内存地址。某种类型的指针,表明指针是一个变量,指针变量存储的内容是某种数据类型变量在内存的地址,类比定义“int a”,整型变量a存储的是整型数据,定义的“int *p”指针变量p存储的是地址,该地址存放的是int类型数据的地址。指针类型变量和其他数据类型变量一样本身在内存中有自身的地址,整型变量a和指针变量p在内存中有各自的地址。 5.2.2 指针变量
11、的类型及大小在32位操作系统中指针的大小是4字节,在64位操作系统中,指针的大小是8个字节。指针的大小可以使用sizeof运算符计算。指针类型与大小 5.3 指针的运算指针的运算都是针对内存中的地址来实现的,主要包括取址运算、指针间接访问、指针算术运算。指针的运算 5.3.1 取址运算符在程序中定义变量时系统会为变量在内存中开辟一段空间,用于存储该变量的值,每个变量的存储空间都有唯一的编号,这个编号就是变量的内存地址。C语言支持以取址运算符“&”获得变量的地址。 6.2.1 取址运算符&符号的使用方法:&变量; int a = 10; /定义变量a int *p = &a; /定义int类型的
12、指针p,并取变量a的地址赋值给p 如果试图改变未知的内存地址中的数据会造成系统破坏或者异常错误出现。由于未知地址存放的数据是无从知晓的,访问未知地址是很危险的。 小提示:小提示:指针访问未知内存地址指针访问未知内存地址 小提示:小提示:指针访问未知内存地址指针访问未知内存地址char* p = (char*)0 x08000CEF;/变量p保存的是地址0 x08000CEF*p = x;/间接的改变0 x08000CEF地址的数据上述代码中,给char类型的指针变量p赋了一个未知地址,然后试图通过p改变该地址中的的数据,程序运行时会出现异常:写入访问权限冲突。这是因为该地址是不合法的,例如它可
13、能指向内核空间或者是正在运行程序的进程空间地址。因此,在使用指针变量时,要避免通过指针访问未知的内存地址,以免程序发生不可预知的错误。 5.3.2 指针间接访问指针变量存储的数值是一个地址,直接对地址操作容易出错,针对指针变量的取值并非取出它所存储的地址,而是间接取得该地址中存储的值。C语言支持以取值运算符“*”取得指针变量所指内存中存储的值。 *符号使用方法:*指针表达式int a = 10; /定义变量a int *p = &a; /定义int类型的指针p,并取变量a的地址赋值给p int b = *p; /定义int型变量b,并取指针变量p中存储的变量值赋给b5.3.2 指针间接访问 5
14、.3.3 指针算术运算1、指针变量与整数相加减指针变量可以与整数进行相加或相减操作:p+n, p-np是一个指针变量,p+1表示将指针向后移动1个数据长度。数据长度是指针对应的基类型所占的字节数,也称为步长,若指针是int*类型的指针,则p的步长为4字节,执行p+1,则p的值加上4个字节,即p向后移动4个字节。 如果p为int*类型的指针变量,则p与p+1的位置如下图。p步长为4字节5.3.3 指针算术运算 指针变量的加减运算实质上是指针在内存中的移动。5.3.3 指针算术运算 对于单独零散的变量,指针的加减运算并无意义,只有指向连续的同类型数据区域,指针加、减整数才有实际意义,因此指针的加减
15、运算通常出现在数组操作中。5.3.3 指针算术运算 2、指针表达式的自增自减运算指针类型变量也可以进行自增或自减运算:p+ , p- , +p, -p指针的自增自减运算与指针的加减运算含意是相同的,每自增(减)一次都是向后(前)移动一个步长,即p+、+p最终的结果与p+1是相同的。5.3.3 指针算术运算 3、同类指针相减运算同类指针类型可以进行相减操作。pm-pnpm和pn是两个指向同一类型的指针变量。同类指针进行相减运算其结果为两个指针之间数据元素的个数,即指针的步长个数。5.3.3 指针算术运算 同类指针之间只有相减运算,没有相加运算,两个地址相加是没有意义的,此外,不同类型指针之间不能
16、进行相减运算。5.3.3 指针算术运算 指针算术运算与一般的算术运算的区别是,指针的算术运算是一种具有数值和数据类型的运算,即加上或减去整数值是以指针变量类型大小为单位进行的运算。这种运算方式常用于连续内存空间的相关操作,如数组、动态内存分配的空间。 小提示:小提示:指针算术运算的本质指针算术运算的本质 NULL是一个宏,是C语言中的保留值,空指针指的是指针变量指向NULL,空指针表明指针指向的地址是不可读取也不可以写入的。C语言标准库中对NULL的定义如下:#define NULL (void *)0)5.4.1 空指针 定义指针赋值为NULL与赋值为(vod*) 0)指向同一块地址空间。5
17、.4.1 空指针int* p = NULL;int* pp=(void*) 0); 上述指针p和pp均指向地址为0的空间,地址为0的空间是一个特殊的地址空间。在程序中,有时可能需要用到指针,但是又不确定指针在何时何处使用,可以先使定义好的指针指向空。5.4.1 空指针 5.4.2 野指针 5.4.2 野指针野指针的形成原因有两种:(1)一种是指针变量定义后未初始化。(2)另一种是指针指向了一个已释放的内存空间。 5.4.2 野指针对野指针执行读操作或者是写操作会出现错误,程序在编译过程中会对野指针读或者写操作是无法通过的。在编程中应当确保不会出现野指针,最好将未初始化的指针和释放指向内存空间的
18、指针赋值为NULL,防止意外操作野指针。 5.4.3 void*指针void为无类型,“void *”就是无类型指针,无类型指针是一种可以指向任意类型的指针,也称为通用指针。 5.4.3 void*指针通用指针指向的内存可以存放任意指针类型的数据,但程序无法正确解读该内存中的数据,访问空指针指向的数据会提示“不允许使用不完整类型”错误,因此,空类型指针在使用时需要强制转换为其他类型的指针进行访问。 5.4.3 void*指针int a = 1;int* p = &a;void* pp=p;printf(int类型的指针变量p的地址为:%pn,p);printf(void类型的指针变量pp的地址
19、为:%pn, pp);printf(int指针p地址空间的值为:%dn, *p);printf(void指针pp地址空间的值为:%dn,(*(int*)pp); 5.4.3 void*指针“void *”类型的指针不允许进行算术运算,进行自增、自减、加减运算是错误的。 5.5.1 堆内存申请函数(1)malloc()函数void* malloc(size_t size);返回值类型:void *。参数:size_t。size_t是系统对unsigned int类型的重定义。 5.5.1 堆内存申请函数malloc( )函数的功能是分配size字节大小的内存空间,申请成功后返回指向该内存空间的指
20、针,若申请内存空间失败,返回值为NULL。通常在申请内存时使用if语句确认内存是否申请成功。 5.5.1 堆内存申请函数(2)calloc()函数void* calloc(size_t nmemb,size_t size);返回值类型:void *。参数:nmemb代表分配数据类型的个数。size代表分配内存大小。 5.5.1 堆内存申请函数该函数用于申请元素大小为size,一共有nmemb个元素格式的空间。并返回指向申请内存空间的指针,失败返回NULL。它与malloc( )函数的区别是calloc( )函数在申请内存后会将申请的内存空间元素的值初始化为0。 5.5.1 堆内存申请函数(3)
21、realloc()函数void* realloc(void *ptr, size_t size);返回值类型:void *。参数:ptr指向一个已分配好的内存空间。size表示要申请的新内存大小。 5.5.2 堆内存释放申请的堆内存使用完成后必须使用free( )函数释放内存,归还内存空间,避免内存泄露现象的发生。free()函数原型如下:void free(void *ptr);free( )函数的参数ptr为指向申请使用完成后的堆内存空间的指针,该函数没有返回值。 5.5.3 其他内存操作函数(1)memset()函数void *memset(void *s, int c, size_t
22、n);memset( )函数返回值类型是void *,memset( )函数的功能是填充内存空间,第一个参数s指向填充的内存空间,第二个参数c指的是使用常量填充申请的内存空间,第三个参数n指的是填充空间的字节数。 5.5.3 其他内存操作函数由于malloc( )函数申请内存后未对内存初始化,内存中存储元素的值是没有用的数据或随机值,如常见的控制台输出会显示一串“烫烫”就是由于未初始化造成的。为了规范操作,通常使用memset( )函数初始化malloc( )函数申请的堆内存空间。此外,memset( )函数也可用于字符数组的初始化。 5.5.3 其他内存操作函数(2)memcpy( )函数v
23、oid *memcpy(void *dest, const void *src, size_t n);memcpy( )函数返回值类型为void *,第一个参数dest指向存放拷后数据的地址空间,第二个参数src指向需要拷贝数据,第三个参数n表示要拷贝的字节数。该函数表示将n字节数据从内存区域src拷贝到内存区域dest,函数返回指向拷贝后空间的指针。需要注意的是,dest与src指向的内存区域不能重叠。 5.5.3 其他内存操作函数(3)memmove( )函数void *memcpy(void *dest, const void *src, size_t n);memmove( )函数为v
24、oid *类型,第一个参数dest指向存放拷后数据的地址空间,第二个参数src指向需要拷贝数据的地址空间,第三个参数n表示要拷贝的字节数。该函数表示将n字节从内存区域src拷贝到内存区域dest,函数返回指向拷贝后空间的指针。 5.5.3 其他内存操作函数memmove()函数可以处理空间重叠的情况,如果dest和src指向的内存空间发生重叠,memmove( )函数能够将src空间的数据在被覆盖之前拷贝到des目标区域。拷贝完成之后,src内存区域的数据会被更改。如果dest和src指向的内存空间不重叠,则memmove()函数与memcpy( )函数功能一样。 5.6.1 常量指针常量指针
25、表示指针指向的数据是被const修饰的变量,其定义形式如下:const 指针类型 * 指针变量名;在上述格式中,在定义的指针数据类型前加const关键字,表明该指针指向的数据是只读的,不允许通过该指针修改指向变量的值,而指针变量可以指向其他对象。 5.6.1 常量指针int a = 1;const int b=2;const int* p = &a;p = &b;/允许修改指向*p = 2;/错误,不允许通过const修饰的指针类型间接修改指向变量的值 5.6.2 指针常量常量指针表示指针指向的地址不允许被修改,指针常量的定义形式如下:指针变量类型 *const 指针变量名在上述格式中,con
26、st放在指针变量名称前,修饰的是指针变量,指针变量的值不能被更改,但指针变量指向的内存空间的数据可以被更改。 5.6.2 指针常量int a = 1;int b = 2;int * const p = &a;p = &b;/错误,不允修改指针指向其他变量的地址*p=3;/可以通过const修饰的指针变量修改指向地址的值 5.6.3 常量的常指针常量的常指针,意味着不能修改指针的指向,并且不能通过当前指针修改变量的值。常量的常指针定义形式如下:const 指针变量类型 *const 指针变量 5.6.3 常量的常指针int a = 1;int b = 2;const int * const p
27、= &a;*p = 3;/错误p = &b;/错误 5.7 二级指针指针还可以指向一个指针,即指针中存储的是指针的地址,这样的指针称为二级指针。使用二级指针可以间接修改一级指针的指向,也可以修改一级指针指向的变量的值。 5.7 二级指针二级指针定义格式如下:变量类型 *变量名; 5.7 二级指针1、通过二级指针间接修改变量的值int a = 1;/整型变量int* p = &a;/一级指针p,指向整型变量aint* q = &p;/二级指针q,指向一级指针pprintf(变量a的地址:%pn,&a);printf(一级指针p的地址:%pn, p);printf(二级指针q存储的值:%pn, *
28、q);printf(二级指针q的地址:%pn, q);*q = 2;/二级指针间接改变,printf(变量a的值%dn, a); 5.7 二级指针指针q是一个二级指针,其中存储一级指针p的地址,而p中存储整型变量a的地址,第11行代码通过间接访问运算符“*”间接修改二级指针变量q中存储的指针所指向的值,从而修改变量a的值。它们之间的逻辑关系如下图。 5.7 二级指针2、通过二级指针改变一级指针的指向int a = 1;/整型变量int* p = &a;/一级指针p,指向整型变量aint* q = &p;/二级指针q,指向一级指针pint b = 3;printf(变量a的地址:%pn, &a)
29、;printf(一级指针p的地址:%pn, p);printf(二级指针q存储的值:%pn, *q);printf(二级指针q的地址:%pn, q);printf(=n);*q = &b;/修改一级指针的指向printf(变量a的地址:%pn, &a);printf(变量b的地址:%pn, &b);printf(一级指针p的地址:%pn, p);printf(二级指针q存储的值:%pn, *q);printf(二级指针q的地址:%pn, q);printf(指针p指向地址存储的值%dn, *p); 5.7 二级指针上述代码中,通过间接访问运算符“*”,修改二级指针的指向,使二级指针变量q中保存
30、的一级指针变量p指向变量b。使得原先一级指针p存储为变量b的地址。它们之间的逻辑关系如下图。 5.8 本章小结本章主要讲解了指针的相关知识,首先讲解了计算机内存的线性存储特性,计算机内存的区域划分的基本知识,同时讲解了可执行程序在内存空间的布局,作为学习指针的引导,然后讲解了本章的核心内容指针,包括指针变量的概念与定义、指针的运算、特殊类型的指针等;其次讲解了内存操作函数,包括内存申请、释放与初始化函数;接着讲解了指针与const修饰符的结合使用;最后讲解了二级指针的使用方法。通过本章的学习,读者应当对指针和内存地址之间的联系有更深入的理解,并对C语言中程序的内存分布以及内存申请函数有初步的了解,为后续学习做好铺垫,