一、指针是什么 1.1 地址是什么 在计算机中,为了对内存空间进行管理,将内存空间划分成若干个存储单元。每个存储单元大小为1字节,并对每个存储单元编号,这个编号被称为地址(内存地址)。可以把内存想象成一个大的教学楼,为了对教学楼管理,分成了许多教室,这里教室等同于存储单元,为了方便找到某一个教室,可以对教室进行编号,如:301教室,这里的教室编号等同于地址。
1.2 指针与指针变量 指针:指针就是内存地址 指针变量:用来存放内存地址的变量 ,存储的地址对应了内存中某个存储单元,通过解引用可以访问到对应存储单元里面的值。
程序执行时会为变量分配存储单元存储变量的值,根据变量类型的不同,分配的存储单元数量(空间大小)也不一样。 char类型 1字节 short类型 2字节 int类型 4字节
指针变量的声明: 类型 * 变量名; 如 : int * p; “变量名” 前 * 表示其后的变量是指针变量,该变量只能存储地址 ”类型“ 表示该指针变量类型,如: int *代表整形指针类型,也代表是为了存放 int 类型变量的地址
1 2 3 4 5 6 7 #include <stdio.h> int main () { int i = 10 ; int * p = &i; printf ("%p\n" , p); return 0 ; }
输出
说明:&取出的是一个变量在内存中起始存储单元的地址,比如:int类型所占4个存储单元,但只会取出4个存储单元中最小的地址
1 2 3 4 5 6 7 8 9 #include <stdio.h> int main () { int i = 10 ; int * p = &i; *p = 20 ; printf ("%d\n" , i); return 0 ; }
输出
二、指针类型意义 我们知道变量有不同类型,比如:int、char、float、double等,那么指针变量也是有不同类型的说明:为了讲解方便,后续的指针指的是指针变量,后面不再阐述
指针是一个变量,那么它也会占内存空间,在32位平台里面,指针所占内存空间大小为4字节。在64位平台里面,指针所占内存空间大小为8字节。指针所占内存空间大小与指针类型无关
1 2 3 4 5 6 7 8 9 #include <stdio.h> int main () { printf ("char * = %d\n" , sizeof (char *)); printf ("short * = %d\n" , sizeof (short *)); printf ("int * = %d\n" , sizeof (int *)); printf ("long * = %d\n" , sizeof (long *)); printf ("double * = %d\n" , sizeof (double *)); return 0 ; }
输出
1 2 3 4 5 char * = 4 short * = 4 int * = 4 long * = 4 double * = 4
既然指针所占内存空间大小与指针类型无关那么为什么还要不同的指针类型呢,比如: int *、char *、double *等指针类型,下面会逐渐讲解
2.1 指针+-整数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> int main () { int i = 10 ; char * pa = &i; short * pb = &i; int * pc = &i; printf ("pa = %p\n" , pa); printf ("pb = %p\n" , pb); printf ("pc = %p\n" , pc); printf ("--------------\n" ); printf ("pa+1 = %p\n" , pa+1 ); printf ("pb+1 = %p\n" , pb+1 ); printf ("pc+1 = %p\n" , pc+1 ); return 0 ; }
输出
1 2 3 4 5 6 7 pa = 006F F804 pb = 006F F804 pc = 006F F804 -------------- pa+1 = 006F F805 pb+1 = 006F F806 pc+1 = 006F F808
指针的类型决定了指针向前或者向后走一步有多大
2.2 指针的解引用 1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { int i = 1127 ; char * pa = &i; int * pb = &i; printf ("%d\n" , *pa); printf ("%d\n" , *pb); return 0 ; }
输出
指针类型决定了,对指针解引用的时候有多大的操作权限 char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
三、野指针 野指针:就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因 3.1.1 指针未初始化 1 2 3 4 5 6 7 #include <stdio.h> int main () { int * p; *p = 30 ; return 0 ; }
3.1.2 指针越界访问 1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> int main () { int arr[5 ] = { 1 ,2 ,3 ,4 ,5 }; int i = 0 ; int * p = &arr[0 ]; for (i = 0 ; i <= 5 ; i++) { printf ("%d " , *(p + i)); } return 0 ; }
3.1.3 指针指向的空间已经释放 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> int * test () { int i = 10 ; return &i; } int main () { int * p = test(); printf ("%d\n" , *p); return 0 ; }
3.2 避免野指针方法
指针初始化
小心指针越界
指针指向空间释放即使置NULL
避免返回局部变量的地址
指针使用之前检查有效性
四、指针运算 4.1 指针+-整数 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> int main () { int arr[5 ] = { 1 ,2 ,3 ,4 ,5 }; int i = 0 ; int * p = &arr[0 ]; for (i = 0 ; i < 5 ; i++) { printf ("arr[%d] = %d\n" , i, *(p + i)); } return 0 ; }
输出
1 2 3 4 5 arr[0 ] = 1 arr[1 ] = 2 arr[2 ] = 3 arr[3 ] = 4 arr[4 ] = 5
可以通过对指针+-整数,访问指针指向的存储单元后面或前面内存区域
4.2 指针-指针 1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main () { int arr[5 ] = { 1 ,2 ,3 ,4 ,5 }; int * pa = &arr[0 ]; int * pb = &arr[3 ]; printf ("指针pa与指针pb中间间隔%d个元素\n" , pb - pa); return 0 ; }
输出
指针-指针得到数字的绝对值是指针和指针之间元素的个数 前提:两个指针指向的是同一个数组
4.3 指针的关系运算 将int arr[5]数组5个元素赋值为0方法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> int main () { int arr[5 ]; int * vp; for (vp = &arr[5 ]; vp > &arr[0 ];) { *--vp = 0 ; } int i = 0 ; for (i = 0 ; i < 5 ; i++) { printf ("%d " , arr[i]); } return 0 ; }
输出
方法二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> int main () { int arr[5 ]; int * vp; for (vp = &arr[4 ]; vp >= &arr[0 ]; vp--) { *vp = 0 ; } int i = 0 ; for (i = 0 ; i < 5 ; i++) { printf ("%d " , arr[i]); } return 0 ; }
输出
C语言标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
五、指针与数组 5.1 数组名意义 在一般情况下数组名就是数组首元素地址
1 2 3 4 5 6 7 8 #include <stdio.h> int main () { int arr[10 ] = { 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,0 }; printf ("%p\n" , arr); printf ("%p\n" , &arr[0 ]); return 0 ; }
输出
我们可以将数组名当成地址存放到指针中去,然后对指针+整数,访问数组后续元素的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> int main () { int arr[] = { 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,0 }; int * p = arr; int sz = sizeof (arr) / sizeof (arr[0 ]); int i = 0 ; for (i = 0 ; i < sz; i++) { printf ("&arr[%d] = %p <====> p+%d = %p\n" , i, &arr[i], i, p + i); } return 0 ; }
输出
1 2 3 4 5 6 7 8 9 10 &arr[0 ] = 00 EFFB7C <====> p+0 = 00 EFFB7C &arr[1 ] = 00 EFFB80 <====> p+1 = 00 EFFB80 &arr[2 ] = 00 EFFB84 <====> p+2 = 00 EFFB84 &arr[3 ] = 00 EFFB88 <====> p+3 = 00 EFFB88 &arr[4 ] = 00 EFFB8C <====> p+4 = 00 EFFB8C &arr[5 ] = 00 EFFB90 <====> p+5 = 00 EFFB90 &arr[6 ] = 00 EFFB94 <====> p+6 = 00 EFFB94 &arr[7 ] = 00 EFFB98 <====> p+7 = 00 EFFB98 &arr[8 ] = 00 EFFB9C <====> p+8 = 00 EFFB9C &arr[9 ] = 00 EFFBA0 <====> p+9 = 00 EFFBA0
5.2 数组名两个例外 sizeof(数组名) :sizeof内部单独放一个数组名,数组名代表整个数组
1 2 3 4 5 6 7 #include <stdio.h> int main () { int arr[5 ] = { 1 ,2 ,3 ,4 ,5 }; printf ("%d\n" , sizeof (arr)); return 0 ; }
输出
&数组名 :取出的是数组地址,数组名表示整个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> int main () { int arr[5 ] = { 1 ,2 ,3 ,4 ,5 }; int * pa = arr; int * pb = &arr; printf ("arr = %p\n" , pa); printf ("&arr = %p\n" , pb); printf ("---------------\n" ); printf ("arr +1 = %p\n" , pa+1 ); printf ("&arr +1 = %p\n" , pb+1 ); return 0 ; }
输出
1 2 3 4 5 arr = 00 DBFE24 &arr = 00 DBFE24 --------------- arr +1 = 00 DBFE28 &arr +1 = 00 DBFE38