英文原版:P241
因为指针非常重要,所以本书打算用3个章节来讲述指针相关内容:第11章讲述C语言的基础,第12章和第17章会介绍指针的高级应用
本章的主要内容
- 11.1节介绍指针变量的声明。
- 11.2节介绍指针变量的初始化,及地址操作符和间接操作符。
- 11.3节介绍使用指针来给变量赋值。
- 11.4节介绍指针作为函数参数。
- 11.5节介绍指针作为函数返回值。
11.1节 指针变量
如何理解指针?
第一步:理解为什么需要指针?
第二步:指针是什么?
...
第n步:待补充
为什么需要指针?
- 首先,不能用整数来存储地址。因为虽然可用数来表示地址,但是地址的取值范围可能不同于整数的范围。
- 其次,可使用指针变量来存储地址。
指针是什么?
指针就是地址,指针变量就是存储地址的变量。
基础知识补充:
- 内存是以字节为单位划分的,每个字节由8个信息位组成。
- 为了区分每个字节,对每个字节都进行编号,即每个字节都有一个唯一的地址。
- 如果内存中有n个地址,则可认为地址就是一个在0到n-1之间的数。
- 可执行程序一般是由代码(对应的是C源文件里的语句)和数据(对应的是C源文件里的变量)组成。
- C程序里的每个变量需要至少一个字节的存储单元。
概念1 变量的地址
变量的地址就是存储该变量值的首个字节的地址。
例1 变量的地址:首字节的地址
假设在地址2000和2001存储着变量i的值,则i的地址就是2000。
使用指针变量p来存储变量i的地址,称作p指向i。
11.1.1节 声明指针变量
格式:
// p是一个指向int类型对象的指针变量
int *p;
C语言对指针的要求:
- 每个指针变量只能指向一种特定类型的对象(引用类型对象)。
- 对引用类型是什么没有限制。
- 甚至,一个指针变量可指向另一个指针。
注意:
- 声明一个变量为指针变量的效果只是给该变量预留存储空间,并不会使其指向一个对象。
- 在使用指针变量前必须将指针变量初始化。
例1 声明指针变量1
int *p;
int i, j, a[10], b[10], *p, *q;
例2 声明指针变量2
int *p;
double *q;
char *r;
11.2节 取地址运算符和间接寻址运算符
- 取地址运算符:
&
- 间接寻址运算符:
*
11.2.1节 取地址运算符&
格式:
&x //该表达式的值是变量x在内存中的地址
例1 使用取地址运算符将指针变量初始化
int i, *p;
...
p = &i;
例2 在声明时将指针变量初始化
int i;
int *p = &i;
例3 将指针变量的声明和初始化放在一起
int i, *p = &i;
11.2.2节 间接寻址运算符
格式:
*p //如果p是一个指针,则*p表示p当前指向的对象,该表达式的值就是该对象的值
注意:
- 永远不要对未初始化的指针变量使用间接寻址运算符。
- 当源代码中出现没初始化的指针变量时,编译器会发出警告信息,务必要注意任何您得到的警告信息。
例1 输出变量i的值
int i, *p = &i;
...
printf("%d\n", *p);
例2 理解*p
和变量i
的等价关系
int i;
int *p = &i;
i = 1;
printf("%d\n", i);// 输出1
printf("%d\n", *p);// 输出1
*p = 2;
printf("%d\n", i);// 输出2
printf("%d\n", *p);// 输出2
例3 对未初始化的指针变量使用间接寻址运算符*
int *p;
printf("%d\n", *p);//可能会打印垃圾信息,导致程序崩溃或者有其他副作用
解释:
如果指针变量p
还没初始化,则尝试使用p
的值将导致未定义的行为。
int *p;
*p = 1;
解释:
指针变量p
还没初始化,尝试给*p
赋值是非常危险的。
- 如果
p
包含的是有效内存地址,则*p=1;
会尝试修改保存在前述有效地址里的数据。 - 如果该赋值语句修改的内存单元属于该程序,则后果可能只是是出错;
- 如果该赋值语句修改的内存单元属于操作系统,则很可能会导致系统崩溃;
11.3节 使用指针来赋值
C语言允许:只要左边的指针变量和右边的指针变量具有相同的类型,就可使用赋值运算符来来拷贝指针。
例1 使用赋值运算符来拷贝指针
int i, j, *p, *q;
...
p = &i; //指针变量初始化
q = p; //使用指针来给指针赋值
*p = 1;
*q = 2;
例2 间接寻址运算符赋值
int i, j, *p, *q;
p = &i;
q = &j;
i = 1;
*q = *p;
11.4节 使用指针作为函数参数
例1 计算一个double数的整数部分和分数部分
函数调用
decompose(3.141519, &i, &d);
函数原型
void decompose(double x, long *int_part, double *frac_part);
函数定义
void decompose(double x, long *int_part, double *frac_part)
{
*int_part = (long)x;
*frac_part = x - *int_part;
}
例2 使用指针读取整数
int i;
...
scanf("%d", &i);
或者
int i, *p;
...
p = &i;
scanf("%d", p);
注意:
如果一个函数的参数是指针类型,但调用该函数传递的却不是指针类型,则会有可怕的后果。
例3 传递非指针参数值给指针参数
decompose(3.141519, i, d);//这条语句会导致程序修改存储在未知内存地址里的数据。
程序示例:求一个数组中的最大值和最小值
源文件:maxmin.c
#include <stdio.h>
#define N 10
void max_min(int a[], int n, int *max, int *min);
int main(void)
{
int b[N], i, big, small;
printf("Enter %d numbers: ", N);
for (i=0; i<N;i++) {
scanf("%d", &b[i]);
}
max_min(b, N, &big, &small);
printf("Largest: %d\n", big);
printf("Smallest: %d\n", small);
return 0;
}
void max_min(int a[], int n, int *max, int *min)
{
int i;
*max = *min = a[0];
for(i=1; i<N; i++){
if(a[i]>*max) {
*max = a[i];
}else {
*min = a[i];
}
}
}
11.5节 指针作为函数返回值
- 返回指针类型的函数参数作为返回值;
- 返回指向外部变量的指针作为返回值;
- 返回指向局部非静态变量的指针作为返回值;
- 返回指向数据元素的指针作为返回值;
注意:永远不要返回指向局部变量的指针作为返回值
例1 错误示例:返回指向局部变量的指针作为返回值
int * f(void)
{
int i;
...
return &i;
}
编译器会发出警告:函数返回了局部变量的地址。
例2 返回值是指向int类型的指针
函数调用
int i, j, *p;
...
p = max(&i, &j);
函数定义
int *max(int *a, int *b)
{
if (*a > *b) {
return a;
}else {
return b;
}
}
例3 返回指向数组元素的指针作为返回值
int *find_middle(int a[], int n)
{
return &a[n/2];
}
网友评论