8.1 指针用法初识
一、指针是什么
(一) 含义
地址是内存单元的编号
指针就是地址
(二) 通过一个简单程序来认识指针
# include <stdio.h>
int main(void)
{
int * p;
// 定义了一个变量叫P,
//int * 是变量类型 ,p是变量名
//所谓 int * 数据类型,就是存放整型变量地址的数据类型,
//其中 int 也叫基类型,指定此指针变量可以指向的变量的类型。
//表示p只能存放 int型 变量的地址。
//为什么要指定基类型? 因为不同类型数据所占用的内存单元大小和存放方式不一致。
int i =3;
p = &i; //正确
p=i; //Error 类型不一致,P 只能存放整形变量的 “地址” ;而 i 是一个整型变量。
p = 55; //Error 同上
return 0;
}
深层理解 p = &i;
1. P 保存了i 的地址,因此P指向了i
2. P不是i , i 也不是P。
3. 更准确的说,修改P的值不影响i的值,修改 i 的值,也不影响P的值
4. P是指针变量(能存放其他变量的地址)
i 是一个普通变量(只能存放值)
5. 如果一个指针指向了某个普通变量,则 *指针变量 就完全等同于 普通变量
例子: 如果 P 是一个指针变量,并且P存放了普通变量 i 的地址,
则P指向了普通变量 i ; * P 就完全等同于普通变量i
或者说: 在所有出现 * P 的地方都可以替换成i; 在所有出现 i 的地方都可以替换成 * P
6. * P就是以P的内容(P的内容是 i 的地址)为地址的变量
指针与指针变量
- 指针就是地址,地址就是指针
- 地址就是内存单元的编号
- 指针变量就是存放地址(指针)的变量
- 指针和指针变量是两个不同的概念
- 但是要注意
- 通常我们叙述时,会把指针变量简称为指针
- 实际他们含义并不一样
二、指针的重要性
- 通过指针可以一些复杂的数据结构(树、链表、图)
- 快速的传递数据,减少内存耗用
- 是函数可以返回一个以上的值
- 能直接访问硬件
- 能方便地处理字符串
- 是理解面向对象语言中引用的基础
总结:指针是C语言的灵魂
三、指针的定义与分类
概况
一、地址
① 内存单元的编号
② 从0开始的非负整数
③ 范围: 和内存大小、地址总线的条数(CPU)、操作系统等因素有关。[这段写得可能有错,因为没学过组成原理!!!]
例如:
4G 内存,32位CPU
物理地址:0——(2^32-1)
二、指针
① 指针就是地址,地址就是指针
② 指针变量就是存放内存单元的编号,或者说指针变量就是存放地址的变量。
③ 指针和指针变量是两个不同的概念,但是要注意,通常我们叙述时,会把指针变量简称为指针,实际他们含义并不一样。
④ 指针的本质就是一个操作首先的非负整数。(指针不能相加,相乘,相除;只能进行想减)
(一) 基本类型指针
初识
int * P;
int i=3;
p = & i;
基本类型指针的常见错误
读写修改未分配权限空间的值
一、
int main (void)
{
int *P;
/*
定义P,没初始化,给P分配了一块空间,里边是垃圾值
但是垃圾值也是有指向的,指向一处未知空间,暂且把这个记为X
*/
int i =5;
*P=i;
/*
* P 以 P 的内容为地址的值。
现在P的内容为一个垃圾地址,这个地址指向X,
*P 等同于X,但是系统没给分配修改X的权限,这句却要修改人家,必然是错的。
*/
printf("%d\n",*P);
return 0;
}
二、
int main (void)
{
int *P;
int *q;
int i =5;
P=&i;
*P=*q;//语法上没错,但是没实际意义,输出结果,每次运行基本都不一样
printf("%d\n",*P);
return 0;
}
三、
int main (void)
{
int *P;
int *q;
int i =5;
P=&i;
P=q; // q 是垃圾值,q 赋给P,P也变成垃圾值
printf("%d\n",*P); //error
/*
q的空间是属于本程序的,所以本程序可以读写q的内容,
但是如果q的内部是垃圾值,则本程序不能读写*q的内容
因为此时*q所代表的内存单元的控制权限并没有分配给本程序
*/
return 0;
}
野指针
见动态内存部分
基本类型指针案例——互换两个数字
1. 不用函数
int main(void)
{
int a = 5;
int b = 3;
int c;
c = a;
a = b;
b = c;
printf("a=%d;b=%d\n",a,b);
return 0;
}
2. 不用指针的函数能否实现数字互换呢?
void huhuan (int a ,int b)
{
int t;
t=a;
a=b;
b=t;
printf("函数内:a=%d;b=%d\n",a,b);
return;
}
int main(void)
{
int a=5;
int b=3;
huhuan(a,b);
printf("函数调用完:a=%d;b=%d\n",a,b);
return 0;
}
//输出
函数内:a=3;b=5
函数调用完:a=5;b=3
可见,这样的函数,互换操作的是形参,对真正的实参不起作用。
程序运行,从主函数main开始,当遇到huhuan这个函数时,转到函数huhuan执行,
主函数调用huhuan,将两个实参a,b具体的值传到huhuan函数的形参部分,huhuan拿到形参具体的值(5,3),此时,系统自动为形参a,b分配内存空间,现在函数形参a=5,b=3;
函数体语句是对形参a,b进行的操作。
一旦huhuan函数执行完毕,系统为形参开辟的内存空间将被自动释放,对实参不起任何作用。
3. 用指针写函数互换两个变量的值
①.错误写法
void change(int *p,int*q)
{
int *t; // 必须是 int * 类型,否则类型不一致报错
t=p;
p=q;
q=t;
/*
此函数交换的是p,q的内容,
a,b 的值并没有换。
p,q是代表a,b地址的值。
注意 a,b的地址是固定的,不能说交换a,b的地址。
因为a,b是静态局部变量,一旦分配好空间,就一直是在一个地方,一个地址。
*/
return;
}
int main(void)
{
int a=3;
int b=5;
change(&a,&b);
printf("函数调用完:a=%d;b=%d\n",a,b);
return 0;
}
//输出
change函数调用完:a=3;b=5
改变的是p,q里面存放的地址,并不能改变a,b的地址;任何语言都不可能改变静态变量已经分配好的地址。
② 正确写法
void ZZhuhuan(int *p,int *q)
{
int t;
t=*p;
*p=*q;
*q=t;
return;
}
int main(void)
{
int a = 3;
int b = 5;
ZZhuhuan(&a,&b);
printf("ZZhuhuan函数调用完:a=%d;b=%d\n",a,b);
return 0;
}
//输出:
ZZhuhuan函数调用完:a=5;b=3
附注
星号*的含义:
- 乘法
- 定义指针变量 int * P
- 指针运算符(取地址的逆运算符),放在已经定义好的指针变量的前面。如果P是一个已经定义好的指针变量,则*P表示以P的内容的为地址的变量
不用指针,函数只能返回一个值。
通过指针,可以使函数返回一个以上的值。
如何通过被调函数修改主调函数普通变量的值?
- 实参必须为该普通变量的地址
- 形参必须是指针变量
- 被调函数体内,通过* 形参名=…… 的方式就可以修改主调函数相关变量的值。
(二) 指针和数组
见 8.2 指针和数组
(三) 指针和函数
见 8.3 指针和函数