指针
指针变量只能记录地址数据
指针变量和普通变量的使用方法完全不同
指针变量的主要作用就是用来找一个普通变量的
只有记录了有效地址的指针才能用来找普通变量(指针变量的使用有前提条件)
指针变量需要先声明才能使用
声明指针变量时需要在变量名称前加*
当一个指针记录了某个存储区的地址以后就可以说这个指针指向这个存储区
当一个指针指向一个存储区以后就可在指针前使用*操作符,这种写法就可以表示指针指向的那个存储区
通过指针找到的那个存储区的类型只能是声明指针时所提过的类型
可以在一条语句里声明多个同类型指针(类似于变量声明),这个时候需要在每个指针变量前加*
起别名typedef
typedef关键字可以用来给数据类型起别名,程序里可以用别名代替原来的类型名称
指针类型通常可以起别名
用指针类型起别名后:声明的指针变量就不需要再加*号了
//指针变量演示
#include"stdio.h"
typedef int *pint //typedef关键字可以用来给数据类型起别名,程序里可以用别名代替原来的类型名称
int main()
{
int val=0;
int *p_val=NULL;//声明变量必须加*
p_val=&val;//给p_val变量赋值时,因为&val是数字,所以不用加*
*p_val=10;//*p_val代表的是val变量,所以需要加*
printf("val是%d",val);
return 0;
}
声明指针变量时必须带*
,给指针变量赋值时不能带*
没有记录有效地址的指针分为两类
1.空指针里固定记录空地址(NULL)
,这个地址的数值就是0
2.其他没有记录有效地址的指针都叫做野指针
程序里不可以出现野指针,所有指针变量都必须初始化。
指针初始化的时候*没有参与赋值过程
//指针变量演示
//求三个数中最小的数
#include"stdio.h"
int main(){
int val,val1,val2;
int *p_val=&val,*p_val1=&val1,*p_val2=&val;
printf("请输入三个整数");
scanf("%d%d%d",&val,&val1,&val2);
if(*p_val<*p_val1){
if(*p_val<*p_val2){
printf("最小值为%d",*p_val);
}
else {
printf("最小值为%d",*p_val2);
}
}
else{
if(*p_val1<*p_val2){
printf("最小值为%d",*p_val1);
}
else{
printf("最小值为%d",*p_val2);
}
}
}
指针可以代表一种身份或者特征
#include"stdio.h"
int main(){
int val,val1,val2;
int *p_val=&val,*p_val1=&val1,*p_val2=&val;
printf("请输入三个整数");
scanf("%d%d%d",&val,&val1,&val2);
int *p_min=&val;
if(*p_min>val1)
{
*p_min=&val1;
}
if(*p_min>val2)
{
*p_min=&val2;
}
printf("最小值为%d",*p_min);/*指针可以代表一种身份或者特征,这里指针
代表的就是三个数字中最小的值*/
return 0;
}
}
指针指向的存储区可以随着程序的执行不断变化,这个时候可以把指针看作是存储区的某种身份特征。
当一个指针指向数组里的第一个存储区之后可以通过这个指针找到数组里的每个存储区
地址数据只能参与以下计算过程
地址+整数 地址-整数 地址-地址
地址加减整数n的时候实际加减的是n个指向类型存储区的大小(例如加一个整数类型有四个字节,那+两位整数类型存储区就有八个字节)
eg
数组里第一个存储区的地址加下标可以得到下标对应存储区的地址
以下两种写法可以用来表示数组里的存储区
*(arr+num)或*(p_val+num),其中arr是数组名称,p_val是指向数组里第一个存储区的指针,num是下标
#include"stdio.h"
int main(){
int arr[]={1,2,3,4,5};
int *p_val=arr;
int num=0;
for(num=0;num<=4;num++)
{
printf("%d\n",*(arr+num));
printf("%d\n",*(p_val+num));
}
printf("%d",&arr[2]-arr);
return 0;
}
两个地址相减的结果是它们包含存储区的字数
可以使用指针作为循环变量依次处理数组里的每个存储区
在循环里指针应该依次指向数组里的每个存储区
#include"stdio.h"
int main(){
int arr[]={0,1,2,3,4};
int *p_val=NULL;
for(p_val=arr;p_val<=arr+4;p_val++)
{
printf("%d",*p_val);
}
}
声明指针变量的时候可以使用const(不变的)关键字
如果在声明指针变量的时候把const关键字写在类型名称前就表示不可以通过这个指针对它指向的
存储区做赋值,但是可以对指针本身做赋值(const int *p_val=&val;)
如果在声明指针变量的时候把const关键字写在指针变量名称前就表示可以通过这个指针对它指向存储区做赋值,但是不可以对指针本身做赋值
#include"stdio.h"
int main(){
int val=0;
const int *p_val=&val;
int * const sta=&val;
//*p_val=&val;错误的
p_val=NULL;
//错误的:sta=NULL;
*sta=NULL;
}
声明指针变量的时候可以使用void作为类型名称 可以把这种指针叫做无类型指针
这种指针没有告诉我们它指向的存储区是什么类型的
不应该在这种指针前使用*操作符或者对这种指针进行加减整数的计算
这种指针使用前需要先强制类型转换成有类型指针;
//无类型指针演示
#include"stdio.h"
int main(){
char ch='r';
int val=45;
float fval=4.6f;
void *p_v=NULL;
p_v=&ch;
printf("%c\n",*(char*)p_v);
p_v=&val;
printf("%d\n",*((int*)p_v));
p_v=&fval;
printf("%d\n",*((float*)p_v));
}
一个函数可以把自己的存储区开放给另一个函数使用,采用这种方法可以绕过作用域的限制
为了把自己的存储区开放给别的函数使用就需要把存储区的地址传递给
为了使用别的函数开放的存储区就必须用指针记录别的函数传递过来的地址
#include"stdio.h"
void print(int *p_val,int size){
int num=0;
for(num=0;num<=size-1;num++)
printf("%d",*(p_val+num));
}
int main(){
int arr[]={1,2,3,4,5};
print(arr,5);
}
编写程序交换主函数里两个变量的内容
#include"stdio.h"
void print(int *p_val,int *p_val1)
{
int tmp=0;
tmp=*p_val;
*p_val=*p_val1;
*p_val1=tmp;
printf("%d%d",*p_val,*p_val1);
}
int main(){
int val=10,val1=20;
print(&val,&val1);\\切记要加取地址符
printf("%d%d",val,val1);
}
声明指针形式参数时尽量使用const关键字
无类型指针经常作为形式参数使用
//可以通过无类型指针形式参数把任意类型的存储区传递给被调用函数通过无类型指针形式参数可以让调用函数向被调函数开放任意类型的存储区
#include"stdio.h"
void print(void *p_val,int type){
if(!type){
printf("%c",*(char*)p_val);
}
else if(type=21){
printf("%d",*(int *)p_val);
}
else if(type==2){
printf("%g",*(float *)p_val);
}
printf("\n");
}
int main(){
char ch='e';
int val=7;
float fval=1.6f;
print(&ch,0);
print(&val,1);
print(&fval,2);
}
可以在被调函数里把存储区的地址当返回值,这样就可以让被调函数向调用函数开放存储区。
只有符合生命周期规制的存储区才可以开放给别的函数使用(不可以把非静态局部变量存储区的地址做返回值使用)
#include"stdio.h"
int *print(void){
static int val=0;
printf("请输入一个数字");
scanf("%d",&val);
return &val;
}
int main(){
int *p_val=print();
printf("%d",*p_val);
}