目录
1、指针是什么
补充:计算机中的单位
bit 比特位 二进制中一个比特位中存放0或1
Byte 字节 1 Byte = 8 bit
KB 1 KB = 1024 Byte
MB 1 MB = 1024 KB
GB 1 GB = 1024 MB
TB 1 TB = 1024 GB
......
- 内存是电脑上的存储设备,它被划分为一个个的内存单元,每一个内存单元占一个字节,并且每一个内存单元都有一个编号,这个编号在生活中就相当于地址,通过这个地址我们可以很方便地找到任意一个内存单元,而在c语言中,我们又把这个编号(地址)叫做指针。所以,内存单元的编号=地址=指针。
- 指针是内存中一个最小单元的编号,也就是地址。
- 平时口头上说的指针,通常指的是指针变量,是用来存放内存地址的变量。
- 总结:指针就是地址,地址就是指针,口头上说的指针通常指的是指针变量。
指针变量
通过用&(取地址操作符)取出变量的内存起始地址,把地址存放到一个变量中,这个变量就是指针变量。
2、指针和指针类型
2.1 指针变量的大小
- 对于32位的机器,假设有32根地址线,而每根地址线能产生高电压(1)和低电压(0),那么32根地址线就能产生2^32个0/1二进制序列。每个编号占用32个比特位,即4个字节。
- 指针变量的大小在32位平台是4个字节,在64位平台是8个字节。
2.2 指针类型的意义
- 指针的解引用:指针的类型决定了指针在解引用时访问的权限有多大(一次能操作几个字节)。例如:char*的指针解引用访问1个字节,float*的指针解引用访问4个字节。
- 指针+-整数:指针的类型决定了指针向前向后走一步有多大(+/-整数时跨越几个字节)。
2.3 void*指针
- 无具体类型指针(泛型指针),可以接受任意类型的地址。
- 局限性:void*类型的指针不能直接进行解引用和指针+-整数的运算。
- 用途:一般使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可实现泛型编程的效果,使用一个函数来处理多种类型的数据。
2.4 const修饰指针
int *p;// 无const修饰
int const * p;
- const放在*的左边,修饰的是指针指向的内容,使指针指向的内容不能通过指针来改变,但是指针变量本身的内容可以变,即指针变量存放的地址是可以改变的。
int * const p;
- const放在*的右边,修饰的是指针变量本身,使指针变量的内容不能改变,即该指针变量只能存放一个固定变量的地址,但是指针指向的内容,可以通过指针来改变。
int const * const p;
- *的左右两边都有const,此时指针变量的内容不能改变,指针变量指向的内容也不能改变。
3、指针的运算
- 指针+-整数
- 指针-指针
- 指针的关系运算(指针的大小比较)
4、野指针
4.1 野指针的成因
- 指针未初始化
- 指针越界访问
- 指针指向的空间释放
4.2 如何规避野指针
- 指针初始化:如果明确知道指针指向哪里就直接赋值地址,如果不知道可赋值NULL。
- 小心指针越界。
- 指针变量不再使用时,及时置NULL,指针使用之前检查有效性。(只要是NULL指针就不去访问,同时使用指针之前可以判断指针是否为NULL)
- 避免返回局部变量的地址。
5、指针和数组
- 数组名表示的是数组首元素的地址。
- p+i 计算的是数组arr[ i ]的地址。因此可以通过指针来访问数组。
int arr [ 10 ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int * p = arr;
- 指针p存放数组arr首元素的地址,通过指针+-整数就可以实现对数组每个元素的访问了。
6、二级指针
凡是变量就有地址,而指针变量也是变量,因此也有地址,这个地址可以用二级指针来存放,即存放指针变量的地址的二级指针变量。
- int a = 0;
- int* pa = &a;
- int* *ppa = &p;
对二级指针解引用:
解引用谁的地址就访问谁
- * ppa 通过对 ppa 中存放的地址(即 pa 的地址)解引用,访问 pa 。
- * *ppa 通过对 *ppa 中存放的地址(对ppa解引用得到pa的内容,而pa的内容是a的地址)解引用,访问 a 。即先通过 *ppa 找到 pa,然后对pa解引用,* pa,找到a。
7、字符指针
两种使用方式:
1、char ch = 'a';
char *pc = &ch;
2、const char *pc = "hello world"; // 常量字符串直接赋值给字符指针变量,常量字符串的 printf("%s\n",pc); 值不能被改变,因此加一个const更好。
// 直接把一个常量字符串放到存放到字符指针中,实质 上是将字符串hello world首字符的地址存放到字符指 针pc中。
8、指针数组
指针数组是存放指针的数组。
- int arr1 [ 5 ];//存放了5个整型变量的数组
- char arr2 [ 5 ];//存放了5个字符型变量的数组
- int* arr3 [ 5 ];//存放了5个指针变量的数组
9、数组指针
数组指针存放数组的地址的指针,指向的是数组。
int arr[ 5 ];
int(*p)[ 5 ]= &arr;
区别指针数组和数组指针:
- 指针数组:是一个数组,类型为数组存放的元素的类型。
int* arr[ 5 ]; // arr[ 5 ]是一个数组,数组的类型为int* [ 5 ],数组的元素是指针,而且是指向整型的指针变量。
- 数组指针:是一个指针,指针指向的内容是一个数组。
int arr[ 10 ];
int(*p)[ 10 ] = &arr;数组指针的定义用根据数组的类型去定义。
辨别:
- int arr[ 5 ];// arr是整型数组,数组有5个元素
- int *parr1[ 10 ];// parr1是指针数组,数组有10个元素,每个元素是int*类型的
- int (*parr2)[ 10 ];// parr2是数组指针,该指针指向的是一个数组,该数组有10个元素,每个元素是int类型的
- int (*parr3[ 10 ])[ 5 ];// parr3是一个指针数组,该数组有10个元素,每个元素是指针类型的,而且是数组指针(指向含有5个元素的数组的指针),具体类型为:int(*)[ 5 ] 的数组指针类型。
//数组指针的应用
#include<stdio.h>
void print(int(*p)[4], int r, int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
printf("%d ", (*(p + i))[j]); //p[i][j]
}
printf("\n");
}
}
int main()
{
int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
print(arr, 3, 4);
return 0;
}
10、数组传参和指针传参
10.1 传一维数组
函数形参可以为数组、指针。
int test(int arr [ 5 ] )//形参为数组
int test(int arr [ ] )//数组大小可省略
int test(int *arr )//形参为指针
int main()
{
int arr[ 5 ];
test(arr);
}
10.2 传二维数组
函数形参可以为二维数组、数组指针。
int test(int arr [ 4 ][ 5 ] )//形参为二维数组
int test(int arr [ ][ 5 ] )//二维数组的行可以省略,但列不能省
int test(int (*arr)[ 5 ] )//形参为数组指针,指向的是有五个元素的数组,可表示二维数组中的列
int main()
{
int arr[ 4 ][ 5 ];
test(arr);
}
10.3 一级指针作形参
实参可以为指针、变量取地址、数组名。
int test(int *pc)
int main()
{
int *p;
test(p);
int a;
test(&a);
int arr[ 5 ];
test(arr);
}
10.4 二级指针作形参
实参可以为二级指针、一级指针的地址、指针数组名。
int test(int **pc)
int main()
{
int **p;
test(p);
int *q;
test(&q);
int * parr[ 5 ];
test(parr);
}
11、函数指针
&函数名和函数名都表示函数的地址。
用函数指针存放函数的地址:
int Add (int x,int y)
{
return x+y;
}
int(*p)(int,int) = &Add;
int(*p)(int,int) = Add;
// p是一个存放函数地址的指针变量——函数指针。
11.1 函数指针数组
- 存放函数指针的数组。
11.2 指向函数指针数组的指针
- 本质上是一个数组指针,即该指针变量指向一个数组,而指向的这个数组存放的元素是函数指针类型。