指针变量用来记录地址数据,指针变量的用途就是找到另外一个变量,没有记录有效地址的指针不能用来找到其它变量,声明指针变量时必须在变量名称前写*.如果一个指针变量记录了另外一个变量的地址就可以认为它们之间存在捆绑关系,在指针变量名称前使用*操作符可以表示它所捆绑的变量
/* * 指针演示 * */ #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", p_num, p_num1, p_num2); /*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; }*/ 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 ", *p_num, *p_num1, *p_num2); return 0; }
声明指针变量的时候也需要提供类型名称,它表示这个指针适合与什么类型的存储区捆绑
可以在一条语句里声明多个同类型指针,必须在每个指针变量名称前单独加*
没有记录有效地址的指针分为两类
1.空指针里面记录空地址(NULL),这个地址的数值就是0.
2.其他没有记录有效地址的指针都叫做野指针
程序里禁止出现野指针,所有指针变量必须初始化
/* * 指针演示 * */ #include <stdio.h> int main() { int arr[] = {1, 2, 3, 4, 5}, num = 0; int *p_num = arr; for (num = 0;num <= 4;num++) { printf("%d ", arr[num]); printf("%d ", p_num[num]); } printf(" "); return 0; }
对指针变量进行初始化的时候*没有参与赋值过程,指针和变量的捆绑关系会随着程序的执行不断变化,指针可以看作是存储区的某种身份
如果指针和数组里第一个存储区捆绑就可以通过指针找到数组里的每个存储区,指针后使用下标就可以表示数组里的存储区
地址数据可以参与如下计算过程
地址 + 整数 地址 - 整数 地址 - 地址
地址加减整数n实际上加减的是n个捆绑存储区的大小.数组名称加下标可以得到下标对应存储区的地址,采用如下写法也可以表示数组里的存储区
*(arr + num)
其中arr是数组名称,num是下标,这个写法可以表示数组里下标为num的存储区,地址减地址的结果是两个地址之间包含的捆绑存储区个数
/* * 地址计算规则演示 * */ #include <stdio.h> int main() { int arr[] = {1, 2, 3, 4, 5}; int *p_num = arr; printf("arr是%p,arr + 3是%p, arr - 3是%p ", arr, arr + 3, arr - 3); printf("&arr[3]是%p ", &arr[3]); printf("arr[3]是%d ", arr[3]); printf("*(arr + 3)是%d ", *(arr + 3)); printf("p_num[3]是%d ", p_num[3]); printf("*(p_num + 3)是%d ", *(p_num + 3)); printf("&arr[3] - arr是%d ", &arr[3] - arr); return 0; }
可以使用const关键字声明指针变量,声明指针变量的时候可以把const关键字写在指针变量名称前。这种指针不可以被赋值,但是可以通过这种指针对它的捆绑存储区进行赋值。
声明指针变量的时候可以把const关键字写在类型名称前面。这种指针本身可以被赋值,但是不可以通过这种指针对它捆绑的存储区做赋值。
/* * const关键字演示 * */ #include <stdio.h> int main() { int num = 0; int * const p_num = # const int *p_num1 = # *p_num = 10; printf("num是%d ", num); //p_num = NULL; 错误 //*p_num1 = 100; 错误 p_num1 = NULL; return 0; }
声明指针的时候可以使用void作为类型名称可以把这种指针叫做无类型指针.这种指针可以和任意类型的存储区捆绑,无法通过这种指针本身知道它捆绑存储区的类型,不应该在这种指针前直接加*操作符也不应该对这种指针做加减整数的计算,这种指针必须首先强制类型转换成有类型指针然后才能使用
/* * 无类型指针演示 * */ #include <stdio.h> int main() { char ch = 'o'; int num = 45; float fnum = 4.5f; void *p_v = NULL; p_v = &ch; printf("*(char *)p_v是%c ", *(char *)p_v); p_v = # printf("*(int *)p_v是%d ", *(int *)p_v); p_v = &fnum; printf("*(float *)p_v是%g ", *(float *)p_v); return 0; }
所有跨函数使用存储区都是通过指针实现的,数组做形式参数的时候真正的形式参数其实是一个指针
/* * 跨函数使用存储区演示 * */ #include <stdio.h> int *read(void) { static int num = 0; printf("请输入一个数字:"); scanf("%d", &num); return # } int main() { int *p_num = read(); printf("数字是%d ", *p_num); return 0; }
声明指针形式参数的时候尽量加上const关键字,无类型指针通常作为形式参数使用
使用存储区的地址做返回值可以让调用函数使用被调用函数的存储区,这个时候被调用函数需要提供一个指针类型的存储区存放作为返回值的地址数据,不可以把非静态局部变量的地址作为返回值使用