- 指针:指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
个人理解: 指针就是个存放地址的变量。
- 指针长度:32位机器,有32根地址线,每根地址线都产生一个电信号(1/0),共有32位,所以需要4字节来存储地址。64位机器则需要8字节。
- 指针的类型:指针的类型取决于它指向的地址装的变量类型,定义指针只需要将指针类型设为“变量类型*”即可。
例如:
#include<stdio.h>
int main()
{
int a = 1;
int* p = &a;//Int*类型的指针指向a,&a表示取出a的地址。
printf("%d\n", *p);//*p表示找到p指针里装的变量。
return 0;
}
- 指针的意义:既然指针大小只跟计算机处理器有关系为什么要设计指针的类型?而不是统一用同一个类型?
指针的类型决定了指针可以访问的内存大小及其步长(指针+1的位置)
- 步长实例:
#include<stdio.h>
int main()
{
int a = 1;
int* p = &a;
char* pc = (char*)&a;
printf("%p\n", p);
printf("%p\n", p+1);
printf("%p\n", pc);
printf("%p\n", pc+ 1);
return 0;
}
上列代码输出结果:
由输出结果我们可以知道指针类型不同,指针的步长也不相同,char*类型指针步长为1(char长度),int*类型步长为4(int长度)。
2.指针访问地址实例:
int main()
{
int n = 0x11223344;
char* pc = (char*)&n;
int* pi = &n;
*pc = 0;
*pi = 0;
return 0;
}
一步步调试:
n初始化:
用char*指针改变变量n的值,只改了一位。 用int*指针改变变量n的值,改了4位。
结论:不同的指针类型可以读取/修改不同的长度,它们的步长大小也不相同。
- 指针在使用中经常会有使用不当,造成报错的情况,下面总结一下经常出现的问题:
- 野指针,指针未初始化
int main() { int *p;//局部变量指针未初始化,默认为随机值 *p = 20; return 0; }
-
指针越界访问
int main() { int arr[10] = {0}; int *p = arr; int i = 0; for(i=0; i<=11; i++) { //当指针指向的范围超出数组arr的范围时,p就是野指针 *(p++) = i; } return 0; }
-
指针指向的空间释放
- 如何规避野指针
1. 指针初始化2. 小心指针越界3. 指针指向空间释放即使置 NULL4. 指针使用之前检查有效性
int main() { int *p = NULL; //.... int a = 10; p = &a; if(p != NULL) { *p = 20; } return 0; }
-
指针的运算
- 指针可以加减整数(得到的结果是指针)
int main() { int a[10] = { 0,1,2,3,4,5,6,7,8,9 }; int i; for (i = 0; i < 10; i++) { printf("%d\t", *a + i);//其实就是打印a[i],每次加1指针都向后走4位。 } return 0; }
运行结果
-
指针可以-指针(得到的结果是数字)
int main() { char a[10] = "abcde"; char* tmp = a; while (*tmp != '\0')//找到\0位置 { tmp++; } printf("%d\n", tmp - a);//结果是字符串长度 return 0; }
运行结果:
- 指针和数组:数组名就是数组首地址,两种情况除外:
- sizeof(a) //a是数组名,这种情况取得是整个数组的长度。
- &a //a是数组名,这种情况取得是整个数组的地址,尽管数组地址和数组首元素地址相同,但是步长不同。
int main() { char a[10] = "abcde"; printf("%p\n",&a); printf("%p\n",&a[0]); printf("%p\n", &a+1); printf("%p\n", &a[0] + 1); return 0; }
运行结果:
由运行结果可知:数组地址和首元素地址是相同的,但是&a+1和&a[0]+1结果不同,&a+1是数组首元素地址向后移动10位(步长是整个数组的长度),&a[0]+1是首元素地址向后移动1位(移动一个sizeof(char)字节)。
- 指针数组(装数组的指针):int* a[n],表示有一个数组a,里面可以存放n个类型为int*的变量。
- 数组指针:指向数组的指针:int(*p)[3],表示指针p指向一个长度为3,存放数据类型为int的数组。
#include<stdio.h> void f(int (*a)[3], int n, int m)//a是一个指向长度为3,里面数据类型为int的数组的指针。 { int i, j; for (i = 0; i < n; i++) { for (j = 0; j < m; j++) { printf("%d\t",a[i][j]); } printf("\n"); } } int main() { int a[2][3] = { 0,1,2,3,4,5 }; f(a, 2, 3); return 0; }
运行结果:
练习:
int arr [ 5 ]; 一个装有5个int类型变量的数组int * parr1 [ 10 ]; 一个装有10个int*类型变量的数组int ( * parr2 )[ 10 ]; 一个指向装有10个int类型变量的数组的指针int ( * parr3 [ 10 ])[ 5 ]; 一个装有10个int*[5]数组的数组emmmm
- 一维数组传参的写法:(假设int a[10]要作为f的参数)
int f(int* a) //a就是数组首地址
int f(int a[10])
int f(int a[])
- 二维数组传参时可以省略第一个[]里的值,但是第二个[]的值不能省略,因为第二个值是宽度,省略了 的话没法儿放。
int f(int a[][3]) //可以
int f(int a[][]) //NO NO NO
- 函数指针:指向函数的指针
int f(int a)的指针:int (*p)(int)
- 函数指针数组:装有函数指针的数组
int (*p[])(int)
- 回调函数:把一个函数作为参数传递给另一个函数,并在需要的时候执行这个函数。
qsort函数的使用:
qsort是一个系统函数,功能是用快速排序实现任何类型数据的排序。
先用man看一下它参数都是啥
-