指针
指针变量用来记录地址数据。
指针变量的用途就是用来找另外一个变量的,只有记录了有效地址的指针才能使用。
既然指针是变量,那么就应该跟变量一样使用前需要声明变量,指针变量的演示:
#include <stdio.h> int main(){ int num = 0; int *p_num = NULL;//指针变量的声明 p_num = #//将num变量的地址赋值给指针变量 num = 2; printf("num = %d\n", num); printf("*p_num = %d\n", *p_num); printf("p_num = %p\n", p_num); return 0; }
只要在指针变量名称前使用 * 操作符就可以表示捆绑存储区。
指针也分类型,不同类型的指针适合与不同存储区捆绑,也就是不同类型的指针存放的变量地址的变量类型应该一致。可以在一条语句里声明多个同类型的指针,必须在每个指针变量名称前单独加 * 号。
所有没指向地址的指针分为两类:
1、空指针记录固定地址(空地址,用NULL表示,这个地址的数值就是0);
2、除了空指针以外没有指向或者捆绑过的指针都叫野指针。
程序中禁止出现野指针,所有指针变量必须初始化
可以在声明指针变量的时候同时初始化的时候,在初始化的时候 * 没有参与赋值。
指针练习:
#include <stdio.h> int main(){ int num = 0, num1 = 0, num2 = 0, tmp = 0; int *p_num = &num, *p_num1 = &num1, *p_num2 = &num2, *p_tmp = &tmp; printf("请输入三个数字:"); scanf("%d%d%d", &num, &num1, &num2); if(num > num1) { tmp = num; num = num1; num1 = tmp; } if(*p_num > *p_num2) { *p_tmp = *p_num; *p_num = *p_num2; *p_num2 = *p_tmp; } if(*p_num1 > *p_num2) { *p_tmp = *p_num1; *p_num1 = *p_num2; *p_num2 = *p_tmp; } printf("%d %d %d\n", num, num1, num2); return 0; }
可以改变上述代码:
#include <stdio.h> int main(){ int num = 0, num1 = 0, num2 = 0, tmp = 0; int *p_num = &num, *p_num1 = &num1, *p_num2 = &num2, *p_tmp = &tmp; printf("请输入三个数字:"); scanf("%d%d%d", &num, &num1, &num2); /* if(num > num1) { tmp = num; num = num1; num1 = tmp; } if(*p_num > *p_num2) { *p_tmp = *p_num; *p_num = *p_num2; *p_num2 = *p_tmp; } if(*p_num1 > *p_num2) { *p_tmp = *p_num1; *p_num1 = *p_num2; *p_num2 = *p_tmp; } */ if(*p_num > *p_num1){ p_tmp = p_num; p_num = p_num1; p_num1 = p_tmp; } if(*p_num > *p_num2){ p_tmp = p_num; p_num = p_num2; p_num2 = p_tmp; } if(*p_num1 > *p_num2){ p_tmp = p_num1; p_num1 = p_num2; p_num2 = p_tmp; } printf("%d %d %d\n", *p_num, *p_num1, *p_num2); return 0; }
程序执行过程中指针和存储区的捆绑关系可以随时改变。可以把指针看做是存储区的某种身份。
如果用指针记录数组里的第一个存储区的地址就可以用这个指针表示数组里的每个存储区。这个时候就可以认为指针间接捆绑了数组里的所以存储区。在指针后使用下标就可以用来表示数组里的某个存储区。
例如:
#include <stdio.h> int main(){ int arr[5] = {1, 2, 3, 4, 5}; int num = 0; int *p_num = arr; for(num = 0; num <= 4; num++) { printf("%d ", arr[num]); printf("%d ", p_num[num]); } printf("\n"); return 0; }
地址数据可以参与如下计算:
地址 + 整数、 地址 - 整数、 地址 - 地址
地址加减整数 n 实际上加减的是 n 个捆绑存储区的大小。
例如:
#include <stdio.h> int main(){ int arr[5] = {1, 2, 3, 4, 5}; printf("arr = %p, arr+3 = %p\n", arr, arr+3); printf("arr = %p, arr-3 = %p\n", arr, arr-3); printf("&arr[3] - &arr[1] = %d\n", &arr[3] - &arr[1]); return 0; }
地址 - 地址的结果是两个地址间包含的捆绑存储区个数。
所以数组名称加下标可以得到下标对应的存储区的地址。
数组名称不可以被赋值,但是指针可以被赋值
对数组名称做sizeof得到结果是整个数组的字节个数,而对指针做sizeof计算得到的只是这个指针变量所占字节个数
例如:
#include <stdio.h> int main(){ int arr[5] = {1, 2, 3, 4, 5}; int *p_num = NULL; printf("sizeof(arr) = %d\n", sizeof(arr)); printf("sizeof(p_num) = %d\n", sizeof(p_num)); printf("&arr = %p\n", &arr); printf("&p_num = %p\n", &p_num); return 0; }
另外,对数组名称和指针做取地址操作结果也不同。
对一维数组名称取地址是可以当做二维数组进行使用的。
所以跨函数使用的存储区都是通过指针进行实现的。数组做形式参数的时候真正的形式参数就是一个指针。
函数可以把一个存储区的地址作为返回值传递给调用函数,这个时候需要提供一个指针类型存储区记录这个作为返回值的地址,调用函数可以通过这个返回值使用被调用函数提供的存储区。
例如:
#include <stdio.h> int *read(void){ static int num = 0; printf("请输入一个数字:"); scanf("%d", &num); return # } int main(){ int *p_num = NULL; p_num = read(); printf("%d\n", *p_num); return 0; }
不可以把非静态局部变量的地址作为返回值使用。