1.指针变量只能用来记录地址数据
因为地址数据和普通数据的使用方法不同,所以指针变量和普通变量的使用方法不同。
指针变量的主要用途就是找到一个普通变量的存储区。
只有当指针里记录了存储区的地址后,才可以通过这个指针找到能个存储区。
声明指针变量的时候需要在指针变量名称前加*
例子:
如果一个指针记录了一个存储区的地址,就可以认为它们之间实现里捆绑关系。
当一个指针记录了某个存储区的地址后,就可以在指针前使用*操作符找到捆绑的存储区。
例子:
p_num = #将num地址赋值给p_num后,指针变量*p_num就可以给num变量的存储区地址赋值*p_num =10;
输出结果:
声明指针时的类型名称用来表示指针适合与什么类型的存储区捆绑。
例如:int类型的变量就用int类型的指针
可以在一条语句里声明多个类型的指针变量,这个时候必须在每个指针变量名称前单独加*
例子:
没有捆绑的指针分为两种:
一种叫做空指针,它固定记录空地址(NULL),这个地址的数值就是0
其它没有捆绑的指针则都叫做野指针。
程序里不可以出现野指针。
指针变量必须初始化。
例子:
指针变量初始化的时候*没有参与赋值过程。
例子:
#include <stdio.h>
int main(){
int num = 0,num1 = 0, num2 = 0,temp = 0;
int *p_num = &num,*p_num1 = &num1,*p_num2 = &num2,*p_temp = temp;
printf("请输入三个整数:");
scanf("%d%d%d",p_num,p_num1,p_num2);
if(*p_num > *p_num1){
*p_temp = *p_num;
*p_num = *p_num1;
*p_num1 = *p_temp;
}
if(*p_num > *p_num2){
*p_temp = *p_num;
*p_num = *p_num2;
*p_num2 = *p_temp;
}
if(*p_num1 > *p_num2){
*p_temp = *p_num1;
*p_num1 = *p_num2;
*p_num2 = *p_temp;
}
printf("%d %d %d\n",*p_num,*p_num1,*p_num2);
return 0;
}
#include <stdio.h>
int main(){
int num = 0,num1 = 0, num2 = 0,temp = 0;
int *p_num = &num,*p_num1 = &num1,*p_num2 = &num2,*p_temp = temp;
printf("请输入三个整数:");
scanf("%d%d%d", p_num, p_num1, p_num2);
if(*p_num > *p_num1){
p_temp = p_num;
p_num = p_num1;
p_num1 = p_temp;
}
if(*p_num > *p_num2){
p_temp = p_num;
p_num = p_num2;
p_num2 = p_temp;
}
if(*p_num1 > *p_num2){
p_temp = p_num1;
p_num1 = p_num2;
p_num2 = p_temp;
}
printf("%d %d %d\n", *p_num, *p_num1, *p_num2);
return 0;
}
指针和存储区的捆绑关系可以随着程序的执行不断变化。
这种使用方法里可以把指针看作存储区的某种身份或者代表了存储区的某种特征。
如果一个指针和数组里的第一个存储区捆绑就可以通过这个指针找到数组里的每个存储区。
例子:
地址数据可以参与如下计算过程:
地址 + 整数 地址 - 整数 地址 - 地址
地址加减整数n实际上加减的是n个捆绑存储区的大小
运行结果:
例子:
运行结果:
数组里第一个存储区的地址加下标可以得到下标对应的存储区的地址。
以下写法也可以用来表示数组里的存储区:
*(arr+num)
其中arr是数组名称,num是下标。
我们一般这样写: 计算机是这样计算的:(尽量按照计算机计算的方法来写)
arr[num] *(arr + num)
p_num[num] *(p_num +num)
地址之间做减法结果是一个整数,这个整数表示两个地址之间包含的捆绑存储区个数。
例子:
运行结果:
例子:
运行结果:
可以使用指针变量作为循环变量编写for循环依次处理数组里的每一个存储区。
声明指针变量的时候可以使用const关键字。
声明指针变量的时候可以把const关键字写在类型名称前。不可以通过这种指针对捆绑的存储区做赋值,但是可以对这种指针本身做赋值。
例子:
运行结果:
例子:
运行结果:
声明指针变量的时候可以把const关键字写在指针变量名称前。可以通过这种指针对捆绑存储区做赋值,但是不可以对这种指针本身做赋值。
例子:
运行结果:可以运行
例子:
运行结果:运行报错
声明指针变量的时候可以使用void作为类型名称。
这种指针可以叫做无类型指针
这种类型的指针可以和任意类型的存储区捆绑。
无法通过无类型的指针知道它捆绑的存储区的类型。
不应该在无类型指针前直接加*,也不应该对无类型指针直接进行加减整数的计算。
必须先把无类型指针强制类型转换成有类型指针然后才能使用。
例子:
运行结果:
所有跨函数使用存储区都是通过指针实现的。
数组做形式参数的时候真正的形式参数就是一个指针。
例子:
运行结果:
在指针前加上const函数,在被调用函数中就不会修改指针存储区的内容
如果函数不会修改指针形式参数所捆绑存储区的内容,就应该在声明指针形式参数的时候在类型名称前加上const关键字。
无类型指针通常用来作为形式参数使用。
例子:
#include <stdio.h>
void func(const void *p_v,int type){
if(!type){
printf("%c\n",*(const char*)p_v);
}
else if(type == 1){
printf("%d\n",*(const int*)p_v);
}
else if(type == 2){
printf("%g\n",*(const float*)p_v);
}
}
int main(){
char ch = 'p';
int num = 45;
float fnum = 4.5f;
func(&ch,0);
func(&num,1);
func(&fnum,2);
return 0;
}
运行结果:
采用无类型指针通常用来作为形式参数使用,采用无类型指针作为形式参数可以把任意类型的数据传递给被调函数。
练习:
#include <stdio.h>
void func(int *p_num1,int *p_num2){
int temp;
temp = *p_num1;
*p_num1 = *p_num2;
*p_num2 = temp;
}
int main(){
int num1=81,num2=21;
func(&num1,&num2);
printf("num1是%d,num2是%d",num1,num2);
return 0;
}
输出结果:
被调用函数使用存储区的地址做返回值,可以让调用函数使用被调用函数的存储区。
被调用函数需要提供一个指针类型的存储区用来存放作为返回值的地址数据。
例子:
运行结果:
不可以把非静态局部变量存储区的地址当返回值使用。
例子:
运行结果:
练习:
#include <stdio.h>
void *max(const int *p_num,int size){
const int *p_temp = NULL,*p_max=NULL;
for(p_temp=p_num; p_temp<p_num+size; p_temp++){
if(!p_max){
p_max = p_temp;
}
else{
if(*p_temp > *p_max){
p_max = p_temp;
}
}
}
return (int *)p_max;
}
int main(){
int array[]={12,23,8,6,34,80};
int *p_max = max(array,6);
printf("p_max是%d\n",*p_max);
return 0;
}
输出结果: