目录
一、指针引入
写⼀个函数,交换两个整型变量的值(经典问题)
#include <stdio.h>
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d b=%d\n", a, b);
swap(a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
这是初学者常会遇到的问题,究其原因就是在函数调用时,形式参数仅仅是实际参数的一份拷贝,无法影响到实际参数的值。若要修改,就要用到本文下面讲的指针相关知识。
二、计组相关知识补充
如果把一个存储体看作一幢大楼,那么每个存储单元可看作大楼中的每个房间,每个存储元可看作每个房间中的一张床位,床位有人相当于 “1” ,无人相当于 “0” 。床位数相当于存储字长。显然,每个房间都需要有一个房间编号,同样可以赋予每个存储单元一个编号,称为存储单元的地址号。主存的工作方式就是按存储单元的地址号来实现对存储字各位的存(写入)、取(读出)。这种存取方式称为按地址存取方式,即按地址访间存储器(简称访存)。
C语⾔中给地址起了新的名字叫:指针
三、指针变量详解
3.1指针变量的创建
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a; //取出a的地址存储到指针变量pa中
return 0;
}
3.2指针相关操作符
- 取地址操作符&: 取出变量的地址
#include <stdio.h> int main() { int a = 10; printf("%p\n", &a); return 0; }
- 解引⽤操作符*:将地址转为它指向的数据
#include<stdio.h> int main() { int a = 100; int* pa = &a; *pa = 0; //修改a的值为0 return 0; }
3.3指针变量的理解
int * pa = &a
- * :说明是指针变量
- pa:变量名
- int :指针指向数据(此处为a)的类型
- 指针自身也是变量,也有属于自己的地址
3.4指针变量的大小
- 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节。
- 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节。
- 指针变量的⼤⼩和类型是⽆关的,只与平台位数有关。
3.5指针类型的意义
指针变量的⼤⼩和类型⽆关,相信读者会有这样的疑问:为什么还要存在指针类型呢?
- 决定了每次访问数据的字节数:int-4, double/float-8, char-1
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4 };
short* p = (short*)arr;
for (int i = 0; i < 4; i++)
{
*(p + i) = 0;
}
for (int i = 0; i < 4; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
分析:short类型的指针决定了指针每次只能访问2个字节,而int类型为4个字节。也就意味着两次通过指针修改才可以改动一个整型的值。所以最后结果仅仅改变了两个数组元素的值。
- 决定了指针加减整数的步长:int-4, double/float-8, char-1
int main()
{
int n = 10;
char* p1 = (char*)&n;
int* p2 = &n;
printf("%p\n", &n);
printf("%p\n", p1);
printf("%p\n", p1 + 1);
printf("%p\n", p2);
printf("%p\n", p2 + 1);
return 0;
}
分析:指针的类型决定了指针向前或者向后的步长
3.6泛型指针void*
- 这种类型的指针可以⽤来接受任意类型地址
- 缺陷是:无法进行指针加减整数运算,无法解引用操作
3.7const修饰指针
- const如果放在*的左边:修饰的是指针指向的内容(即为指向变量的值),保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。
int a = 10; int b = 10; const int* p = &a; *p = 0;//错误,无法修改 p = &b;//可以
- const如果放在*的右边:修饰的是指针变量本身的内容(即为指向的变量),保证了指针变量的内容不能修改。但是指针指向的内容可以改变。
int a = 10; int b = 10; const int* p = &a; *p = 0;//可以 p = &b;//错误,无法修改
四、指针的运算
- 指针+-整数:可用于数组遍历
- 指针-指针:得到的是两个指针间的元素个数
- 指针的关系运算:大小比较
五、指针与数组
5.1数组名的理解
- 数组名就是数组⾸元素(第⼀个元素)的地址。
- 但是有两个例外:
- sizeof(数组名):sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节
- &数组名:这⾥的数组名表⽰整个数组,取出的是整个数组的地址,地址的值依然为首元素的地址,但是地址类型为数组指针(见后文讲解)。
int main()
{
int arr[5] = { 1,2,3,4,5};
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0] + 1);
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr + 1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr + 1);
return 0;
}
分析:arr和arr+1 相差4个字节;&arr 和 &arr+1相差40个字节。
5.2⼀维数组传参的本质
- 在数组传参的时候,传递的是数组名,所以说本质上数组传参传递的是数组⾸元素的地址。
- 正因如此:⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
5.3二维数组传参的本质
- ⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址
- 类比一维数组:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。
5.4指针数组
指针数组为存放指针的数组
创建举例:int * arr[5]
5.5数组指针
数组指针是指针
创建举例:int (*p)[10]
赋值举例:int * arr[5] = arr (arr为数组名)
六、指针与函数
6.1函数指针变量
int (*pf) (int x, int y)
| | ------------
| | |
| | pf指向函数的参数类型和个数
| |
| 函数指针变量名
|
pf指向函数的返回类型
int (*) (int x, int y) //pf函数指针变量的类型
6.2函数指针的使用
- 函数指针名(参数,参数......)
- (*函数指针名)(参数,参数......)
注意:使用第二种方式是前面一定要加括号
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int(*pf)(int, int) = Add;
printf("%d\n", (*pf)(2, 3));
printf("%d\n", pf(3, 5));
return 0;
}