1.14k likes | 1.28k Views
C 语言程序设计. 第 7 章 指针. 内容提要. 指针的概念 难点:理解指针数据类型 用指针做函数参数,向函数传递变量值、数组元素和字符串 用指针数组处理多个字符串 指向数组的指针与指针数组的区别 带参数的 main 函数; 动态内存分配函数及其应用 一维、二维动态数组的实现 Skill 用指针做函数参数编程. 为什么引入指针的概念. 指针 为函数提供修改变量值的手段 为 C 的动态内存分配系统提供支持 为动态数据结构(如链表、队列、二叉树等)提供支持 改善某些子程序的执行效率. 内存:计算机内的存储部件 所有指令和数据都保存在内存里
E N D
C语言程序设计 第7章 指针
内容提要 • 指针的概念 • 难点:理解指针数据类型 • 用指针做函数参数,向函数传递变量值、数组元素和字符串 • 用指针数组处理多个字符串 • 指向数组的指针与指针数组的区别 • 带参数的main函数; • 动态内存分配函数及其应用 • 一维、二维动态数组的实现 • Skill • 用指针做函数参数编程
为什么引入指针的概念 • 指针 • 为函数提供修改变量值的手段 • 为C的动态内存分配系统提供支持 • 为动态数据结构(如链表、队列、二叉树等)提供支持 • 改善某些子程序的执行效率
内存:计算机内的存储部件 所有指令和数据都保存在内存里 速度快,可随机访问,但掉电即失 编译或函数调用时为变量分配内存单元 变量的地址 0 0 0 0 变量名 Contents Contents Contents Contents Contents Contents Contents 变量的值 变量 (Variables)与变量的地址 (Address) int a=0; a 0x0037b000 变量是对程序中数据存储空间的抽象 某存储区域
内存中的每个字节都有唯一的一个编号(地址)内存中的每个字节都有唯一的一个编号(地址) 地址是一个无符号整数,其字长一般与主机相同 地址按字节编号,按类型分配空间 变量的地址 0 0 0 0 Contents Contents Contents Contents Contents Contents Contents the Address Operator 变量 (Variables)与变量的地址 (Address) int a=0; a &a 0x0037b000 0x0037b001 0x0037b002 0x0037b003 某存储区域
如何读写内存中的数据? 只要指明要访问的变量的内存单元地址 就可以立即访问到变量所在的存储单元 0 0 0 0 Contents Contents Contents Contents Contents Contents Contents scanf("%d", &a); 变量 (Variables)与变量的地址 (Address) int a=0; a &a 0x0037b000 0x0037b001 0x0037b002 0x0037b003 某存储区域
如何读写内存中的数据? 直接访问:按变量地址存取变量值 0 0 0 0 Contents Contents Contents Contents Contents Contents Contents scanf("%d", &a); 变量 (Variables)与变量的地址 (Address) int a=0; a &a 0x0037b000 0x0037b001 0x0037b002 0x0037b003 某存储区域
如何读写内存中的数据? 0 0 0 0 Contents Contents Contents Contents Contents Contents 0x0037b000 变量 (Variables)与变量的地址 (Address) 间接访问:通过存放变量地址的变量去访问变量 int a=0; a &a 0x0037b000 0x0037b001 0x0037b002 0x0037b003 某存储区域
指针变量 指向 变量 变量的地址(指针) 变量地址存入 指针变量 变量值 指针(Pointer)的概念 • 什么类型的变量可以存放变量的地址? • 指针类型——存放地址型数据的特殊数据类型 • 指针变量——专门用于存放地址型数据的变量 • 变量的指针←→变量的地址
指针变量 如何定义指针变量? Declaring pointers int a=10; …... &a 0x0037b000 整型变量 a 10 0x0037b004 0x0037b008 0x0037b000 0x0037b00B …...
指针变量指向的数据类型称为基类型 指针变量 用指针变量要指向的数据类型 作为指针变量的基类型 Declaring pointers int a=10; …... &a 0x0037b000 整型变量 a 10 0x0037b004 0x0037b008 pa 0x0037b000 int*pa; 0x0037b00B …...
指针变量 仅仅是定义了可以指向int型数据 的指针变量,但并未指向a 使其指向a需对指针变量初始化 Pointer Assignments int a=10; …... &a 0x0037b000 整型变量 a 10 0x0037b004 0x0037b008 int*pa=&a; pa 0x0037b000 int*pa; 0x0037b00B pa = &a; …...
指针变量 Pointers have names, types and values Pointer Assignments int a=10; …... &a 0x0037b000 整型变量 a 10 0x0037b004 0x0037b008 int*pa=&a; pa 0x0037b000 int*pa; 0x0037b00B pa = &a; …...
指针变量 • 只能指向同一基类型的变量,否则将引起warning • floata; • int *pa = &a; • warning C4133: '=' : incompatible types - from 'float *' to 'int *' Pointer Assignments int a=10; …... &a 0x0037b000 整型变量 a 10 0x0037b004 0x0037b008 int*pa=&a; pa 0x0037b000 int*pa; 0x0037b00B pa = &a; …...
判断:正确的? 应在类型相同的指针变量之间赋值 一个指针变量不能指向与其类型不同的变量! int i,*p; p=&i; int i; float *p; p=&i; int *p; float *q; p=q; int *p; p=100; 指针变量只 存放地址! 我是正确的, 你猜对了吗?
24 &i i Dereferencing Pointers and the Address Operator • int i = 24; • i is an int variable • &i is like an int pointer, pointing to the variable i the Address Operator
24 p *p Dereferencing Pointers and the Address Operator • int *p; • p = &i; • p is an int pointer • *p 像普通变量一样使用*p ,*取指针p指向内存的内容 the pointer Operator
Dereferencing Pointers and the Address Operator int a=10; …... *(&a) /*该表达式引用的是 变量a的内容*/ &(*pa) /*该表达式的值 代表的是变量a的地址*/ &a 0x0037b000 a 20 0x0037b004 0x0037b008 pa 0x0037b000 0x0037b00B …... int*pa=&a;
指针变量 间接访问(寻址) Using Pointers int a=10; …... &a 0x0037b000 整型变量 a 10 20 0x0037b004 0x0037b008 int*pa=&a; pa 0x0037b000 int*pa; 0x0037b00B pa = &a; …... *pa = 20;
指针变量 如果指针指向一个非你控制的内存空间 并对该空间进行访问,将可能造成危险 Never use uninitialized pointers. Using Pointers int a=10; …... &a 0x0037b000 整型变量 a 20 0x0037b004 0x0037b008 int*pa=&a; pa 0x0037b000 int*pa; 0x0037b00B pa = &a; …... *pa = 20;
int i;scanf("%d", i); /* 这样会如何?*/ char c;scanf("%d", &c); /* 这样呢?*/ i的值被当作地址。如i==100,那么输入的整数就会从地址100开始写入内存 输入以int的二进制形式写到c所在的内存空间。c所占内存不足以放下一个int,其后的空间也被覆盖
? 3 iPtr jPtr i j Using Pointers int i; int *iPtr = &i; int j = 3; int *jPtr = &j; • Consider: *jPtr = *iPtr; i = 4; *jPtr = i; iPtr = j;
p p+1 pointer manipulation ——算术运算 short *p, a[10]; p = a; p++; • p值增加多少? a[0]
p p+1 pointer manipulation ——算术运算 short *p, a[10]; p = a; p++; • p值增加多少? • 如果short改成long,p值增加多少呢? • 指针的加减运算是以其基类型的字节长度为单位的 a[0]
pointer manipulation ——算术运算 • 指针运算不能乱算 • 指针和整数的加减运算 • 同类型指针之间的减法运算 • 其它运算,比如乘法、除法、浮点运算、指针之间的加法等,并无意义,所以也不支持
*p *p = *p + 1; *p p = p + 1; Pointers and Errors • To increment a dereferenced pointer, use (*p)++ • rather than *p++ /* correct but means … */
pointer manipulation ——关系运算 • 指向同一种数据类型的两个指针才能进行关系运算 • 值为1或0 p > q p < q p == q • 不能与非指针类型变量进行比较
pointer manipulation——赋值运算 • 指针在使用前一定要赋值 • 确定指针指向哪里 • 为指针变量赋的值必须是一个地址 main() { int a,*p=&a; scanf("%d",p); … } main() { int *p; scanf("%d",p); … } TC下不报错 VC下报错
argument a &a a x parameter pointer parameter Pointers and Functions • 简单变量做函数参数——Call by Value • Can not modify the argument 形参(parameter)←实参变量(variable) • 指针做函数参数——Call by Reference • In order to modify the argument use: 指针形参(Pointer parameter) ← &(variable)
例7.1~7.2 两数互换 • void Swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } /* call by reference: int a = 15, b = 8; swap(&a, &b); */
例7.1~7.2 两数互换 • void Swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } /* call by reference: int a = 15, b = 8; int *pa = &a, *pb = &b; swap(pa, pb); */
主调函数 被调 函数 例7.1~7.2:编写函数实现两数的互换 程序 2 程序 1 Trace the execution main() { int a, b; a = 5; b = 9; Swap( &a, &b ); printf("a=%d,b=%d",a,b); } main() { int a, b; a = 5; b = 9; Swap(a, b); printf("a=%d,b=%d",a,b); } void Swap(int x,int y) { int temp; temp = x; x = y; y = temp; } 实 参 void Swap(int *x,int *y) { int temp; temp = *x; *x = *y; *y = temp; } 形 参 Not Work!Why? 结果有何不同?
x y a b 程序 1 主调函数 被调函数 void Swap(int x, int y) { int temp; temp = x; x = y; y = temp; } main() { int a, b; a = 5; b = 9; Swap(a, b); printf("a=%d,b=%d",a,b); } temp 5 y x b a 9 9 5 5 9 形 参 5 实 参 x和 y是内部变量 单向值传递
x y a b 程序 2 形 参 实 参 主调函数 被调函数 void Swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } main() { int a, b; a = 5; b = 9; Swap(&a, &b); printf("a=%d,b=%d",a,b); } temp 5 x y a b 5 9 &a &b 9 5 &a &b *x *y 交换的是x和 y 指向的单元内容
void Swap(int *x, int *y) { int*pTemp; *pTemp = *x; *x = *y; *y = *pTemp; } void Swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } 指针pTemp未初始化 指针pTemp指向哪里未知 对未知单元写操作是危险的
void Swap(int *x, int *y) { int*pTemp; pTemp = x; x = y; y = pTemp; } void Swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; } 非危险操作 但是交换的是地址值 不是指针指向单元的内容
指针变量与其它类型变量的对比 • 共性 • 在内存中占据一定大小的存储单元 • 先定义,后使用 • 特殊性 • 它的内容只能是地址,而不能是数据 • 必须初始化后才能使用,否则指向不确定的存储单元 • 只能指向同一基类型的变量 • 可参与的运算:加、减整数,自增、自减、关系、赋值 • 使用原则 • 明确指针指向了哪里 • 明确指针指向单元的内容是什么
作业 • 习题 • 7.5~7.6
H e l l o C h i n a \0 字符数组与字符指针 • C语言并没有为字符串提供任何专门的表示法,完全使用字符数组和字符指针来处理 字符串就是一串以用''引起来的字符 "Hello China" 数组最后一个元素必须是'\0'才表示字符串 pStr 字符数组就是每个元素都是字符型的数组 字符指针就是指向字符类型数据的指针
H e l l o C h i n a \0 字符数组与字符指针 "Hello China" str • 定义和初始化方法不同 pStr char str[12] = {"Hello China"}; char *pStr = "Hello China"; char *pStr; pStr = "Hello China"; pStr = &str[0];
H e l l o C h i n a \0 字符数组与字符指针 "Hello China" str • 定义和初始化方法不同 pStr char str[12] = {"Hello China"}; str = "Hello China"; 数组名str是地址常量 Why? char *pStr; pStr = "Hello China"; 字符指针pStr是变量
H e l l o C h i n a \0 例7.5 :字符串拷贝——用字符数组编程 void MyStrcpy(char dstStr[], char srcStr[]) { int i = 0; while (srcStr[i] != '\0') { dstStr[i] = srcStr[i]; i++; } dstStr[i] = '\0'; } srcStr[i]=='\0 srcStr[i] i=6 i=7 i=0 i=1 i=3 i=4 i=5 i=8 i=9 i=10 i=2 C h \0 H e l o i n a l dstStr[i]
H e l l o C h i n a \0 srcStr srcStr dstStr dstStr srcStr dstStr srcStr dstStr srcStr dstStr srcStr dstStr srcStr dstStr srcStr dstStr srcStr dstStr srcStr dstStr dstStr srcStr dstStr srcStr 例7.5 :字符串拷贝——用字符指针编程 void MyStrcpy(char *dstStr, char *srcStr) { while (*srcStr != '\0') { *dstStr = *srcStr; srcStr++; dstStr++; } *dstStr = '\0'; } dstStr++ srcStr++ *srcStr=='\0 *srcStr C h H e l o i n a l \0 *dstStr
H e l l o C h i n a \0 srcStr dstStr 例7.5 :字符串拷贝——用字符指针编程 void MyStrcpy(char *dstStr, const char *srcStr) { while (*srcStr != '\0') { *dstStr = *srcStr; srcStr++; dstStr++; } *dstStr = '\0'; } 当只允许访问指针指向的地址中的内容时,把函数的指针形参定义为const ,试图修改内容时编译器会报错 Protecting Parameter Values *srcStr C h H e l o i n a l \0 *dstStr
Aconstqualifier qualify pointers使用const修饰指针变量 const int *p; • pointer to a constant integer • p是一个指针变量,可以指向一个整型常量 • the value of p may change, but the value of *p can not • 执行*p = 10这样的赋值操作,VC下编译会提示如下错误信息: error C2166: l-value specifies const object int *const p; • constant pointer to integer • p是一个常量指针,可以指向一个整数 • the value of *pcan change, but the value of p can not
例7.6 :计算实际字符个数 • 方法1:用字符数组实现 unsigned int MyStrlen(char str[]) { int i; unsigned int len = 0; for (i=0; str[i]!='\0'; i++) { len++; } return (len); } • 方法2:用字符指针实现 unsigned int MyStrlen(const char *pStr) { unsigned int len = 0; for (; *pStr!='\0'; pStr++) { len++; } return (len); }
指针与数组 int a[4]={1,2,3,4}; int*pa=a; a …. int*pa=&a[0]; 0x0037b000 1 a[0] pa 数组名是一个常量指针 不能修改该指针的指向 0x0037b004 2 a[1] 0x0037b008 3 a[2] 指针可当数组名使用 0x0037b00B 4 a[3] …...
指针与数组 int a[4]={1,2,3,4}; int*pa=a; a …. int*pa=&a[0]; 0x0037b000 1 a[0] pa pa[0] 0x0037b004 2 a[1] • 数组元素的等价引用形式 • a[i] • *(a+i) • pa[i] • *(pa+i) a+1 *(a+1) 0x0037b008 3 a[2] pa+2 *(pa+2) 0x0037b00B 4 a[3] *pa …...
指针与数组 for (i=0; i<4; i++) scanf("%d", &a[i]); for (i=0; i<4; i++) printf("%d ", a[i]); int a[4]={1,2,3,4}; a …. 0x0037b000 1 a[0] p *p 0x0037b004 2 a[1] *p 0x0037b008 for (p=a; p<(a+4); p++) scanf("%d", p); for (p=a; p<(a+4); p++) printf("%d ", *p); 3 a[2] 0x0037b00B 4 a[3] …... a+4
1 9 7 5 3 a[0] a[1] a[2] a[3] a[4] a[5] 例7.7 :插入排序 • 算法的关键是: • 找到待插入的位置 • 依次移动插入位置及其后的所有元素 • 腾出这一位置放入待插入的元素 待插入元素x=4 5 9 4 7 插入元素 x=4