C 指针
- C语言--备忘
- 一、指针定义
- 二、指针语法
- 三、指针初始化
- 四、解引用
- 五、空指针和野指针
- 1. 空指针
- 2. 野指针
- 3. 合理使用
- 六、指针运算
- 七、指针与数组
- 八、指针类型
- 1. 常量指针
- 2. 指针常量
- 3. 常量指针常量
- 4. 无数据类型指针:void *
- 九、指针与函数
- 十、指针 函数 数组
- 总结
一、指针定义
指针的本质就是一个变量,永远只能存储一个地址【编号】
- 通过指针变量保存的变量的地址可以对这块内存区域任意访问;
- 指针指向的内存区域可以保存一个数字,而这个数字有数据类型;
- 指针变量定义的语法格式:
2.1 定义指针变量后,这个变量 pa 能保存一块内存区域的首地址,这块内存区域保存着int 类型的数据;
2.2 由于指针也是变量,也需要分配内存,与计算机硬件相关 [固定]。32位系统:一个地址值32位,4字节;64位系统,一个地址为64位,8字节;
结论: 指针变量本身没有数据类型,它指向的内存区域有数据类型,所以int 不是给指针变量用,而是给指针指向的内存区域保存的数字用;
// 形式一:int * 变量名;
int * pa;//定义一个指针变量
// 形式二:int* 变量名;
int* pa;//定义一种指针变量
// 形式三:int *变量名
int *p;//定义一个指针变量***推荐
//连续定义指针
int *pa,*pb;//两个指针变量
int *pa, pb;//pa为指针,pb为普通int 变量
三、指针初始化若指针不初始化,地址值为任意值,危险,此指针为野指针;
初始化:
- 指针变量初始化通过取地址&来进行;
- 函数定义语法:
//分配四字节
int a=250;
//定义指针变量[4字节内存空间]//"*"为形式上的*
int *pa=&a;
// "*"为解运算符
printf("请打印%d",*pa);//
代码对应图例:
四、解引用开辟4字节地址空间,指向整型变量;
- 通过解引用运算符:’ * ’ 和指针变量对指向内存区域进行查看和修改。
char a= 100;
char *pa=&a;
//结果pa=&a, &pa 指向pa地址, *pa=a。
printf("%p,%p,%p,%hhd,%cn",pa,&a,&pa,*pa,a);
//分别为1、4,指针永远为4个字节
printf("sizeof(a)=%d,sizeof(pa)=%d",sizeof(a),sizeof(pa));
五、空指针和野指针 1. 空指针sizeof(指针变量名)=4B(永远)//和硬件相关,32位
定义: 空指针变量:保存空地址,用NULL表示,其实就是编号为0的地址;NULL=0地址;不可随意访问,否则程序崩溃;
short *pb=NULL;
//报错,无法调出空指针指向的内容
printf("PB指向0地址保存的数据为:%#x",*pb);
*pb=20;
printf("测试信息为.b");
2. 野指针
**定义:**没有初始化的指针变量,保存着随机地址,指向一块无效的内存,这块内存操作系统并没有给你分配;
int *pc;
printf("PB指向0地址保存的数据为:%#x",*pc);
*pc=250; //向随机地址写入数据
printf("测试信息");
return 0;
3. 合理使用
- 如果定义一个指针标量,不清楚指向谁,一定需要初始化NULL,否则野指针。
- 如果初始化为空指针,后续使用指针时需要对指针变量安全判断,判断是否为NULL。
- 若为NULL,让程序结束或者返回,如果有效,程序才能通过指针进行操作。
int *p=NULL;//赋值为空指针
int a=250;
p=&a;//p指向a
if(NULL==pa){
printf("pa指向空指针,不能继续访问.n");
return -1;//exit(0)程序返回和退出
}else{
printf("pa指向一块有效内存,可以继续访问");
printf("%dn",*pa);
*pa=250;
}
六、指针运算
- 指针可以和一个整数做加减运算,简称地址运算
切记:计算结果和指针指向的变量数据类型有关系
| 指针类型 | 实际地址 |
|---|---|
| (char*)p +1 | +1 |
| (short*)p +1 | +2 |
| (int*)p +1 | +4 |
| (long*)p +1 | +4 |
char *pa=0x1000;//pa指向0x1000 pa++;//pa=0x1001,实际地址+1 short *pa=0x1000;//pa指向0x1000 pa++;//pa=0x1002,实际地址+2 long *pa=0x1000;//假设pa指向0x1000//[int/long]型指针+1 pa++;//pa=0x1004,表示实际地址+4七、指针与数组
- 数组名就是数组的首地址;
- 遵循数组的首地址,遵循运算公式
- &a[2],实际为第3个元素的首地址
- a+2也是第3个元素的首地址
int a[4]={1,2,3,4,5};
int *p=a;//定义指针变量保存整个数组的首地址
//写法1---规矩
for(int i=0;i
printf("%dn",*(p+i));//查看所有元素的值//i为元素+1,如上图
*(p+i) *=10;
}
//写法2
int length=sizeof(a)/sizeof(a[0]);//元素多少
for(pa=a;pa printf("%dn",*p);
*pa++ *=10;//先算*p,后面再做P++
}
//写法3
// 切记:"[]"运算符经过两步运算; 例:a[2]: 1.先计算地址:a+2 2.取值*(a+2) 等价于 p[2]: 1.先算地址:p+2 2.取值*(p+2)
// 申请一个数组,等价于给一个指针~首地址
p=a;//p指向数组,此时p为数组a首地址
for(int i=0;i
printf("%dn",p[i]);//查看所有元素的值//i为元素,如上图
p[i] *=10;
}
八、指针类型 1. 常量指针公式:a[i] = p[i] = *(a+i) =*(p+i)
定义:不能通过指针变量来修改指向的内存区域的值;
//指向的内存变量不可改变,指针地址可变
int a=250;
const int *p=&a; //初始化定义一个常量指针
//或
int const *p=&a;//初始化定义一个常量指针,
//若尝试修改
// *p=200; //gcc编译报错
printf("%dn",*p);//仅仅是查看没问题,打印250
2. 指针常量
定义:指针永远指向一块内存,不可能指向别的地方(保护指针不可乱指向),指针不变;
int a; // 指针指向的内存空间包含的内容可以改变, 指针的固定地址不可改变 int* const p=&a; *p=300;//可以,可以修改指向的内存区域 int b=200; p=&b;//不可以修改地址,gcc报错3. 常量指针常量
定义:代表p本身不可改变,同事指向p的目标也不能改变;
//const int* const p;//用法 int a=100; const int* const p=&a; *p=300;//gcc报错,不可修改指向内容 p=&b;//gcc报错,p地址无法修改4. 无数据类型指针:void *
- 无数据类型指针(void*):是一个指针变量,只是指向的内存区域的数据类型是不可知的
- 无法直接对无类型指针进行解引用操作‘ * ’,因为不知道指向的内存区域数据类型,不知道几个字节
- 如果想要无类型指针获取内存的数据,不许进行数据转换(推荐强制转换)
int a=250; void *p=&a;//虽然p指向a,但是通过p无法获取a变量的数据类型 *p=300; //gcc报错 error
//通过一个指针来获取a的数值
//传统一次性获取4字节方法
int a=0x12345678;
int *p=&a
printf("%d",*p);
//此种方法虽然能一次性获取4字节数据,
//但若不想获取四字节,获取其中的1、2、4字节的话,这种方式无法实现
//通过有类型的指针变量来获取1、2、4字节指针
int a=0x12345678;
//char *p=&a;//隐式转换,可读性差,gcc还要警告
char *p=(char *)&a;//把&a的类型 int 强制转换为 char*,把0x1000~0x1004分段为0x1000~0x1001~0x1002~0x1003~0x1004
printf("%d",*p++);//结果0x78
printf("%d",*p++);//结果0x56
printf("%d",*p++);//结果0x34
printf("%d",*p++);//结果0x12
short *p=(short *)&a;//强制转换,将&a的int类型转换为short*
printf("%d",*p++);//结果0x5678,先解运算符,++运算直接+2字节
printf("%d",*p++);//结果0x1234
return 0;
//结论:此方法虽然满足要求,但是隐式转换,gcc警告,去掉指针变量++时类型相关问题
// 通过无类型指针来获取1字节,2字节,4字节
int a=0x12345678;
void *p= &a;
char *p1=(char *)p;
printf("%#xn",*p1++);//0x78 //++直接+1个字节
printf("%#xn",*p1++);//0x56
printf("%#xn",*p1++);//0x34
printf("%#xn",*p1++);//0x12
//或者省略p1 //p为无类型指针,加1就是加1字节,加2就是加2字节....
int a=0x12345678;
void *p= &a;
printf("%#xn",*(char *)(p+0));//0x78 //空指针,+1就是加1个字节,为步长
printf("%#xn",*(char *)(p+1));//0x56
printf("%#xn",*(char *)(p+2));//0x34
printf("%#xn",*(char *)(p+3));//0x12
//若为short
void *p= &a;
printf("%#xn",*(short *)(p+0));//0x5678
printf("%#xn",*(short *)(p+2));//0x1234//p需要+2字节才到下一个元素//需要人为对内存空间有清晰的认知
九、指针与函数
形式一:指针作为函数的形参,函数通过指针能够访问操作指向的内存;
void A(int *px,int *py,int *pm,int *pn)
{
...;
}
形式二:指针作为函数的返回值,函数的返回值是一个指针,千万注意别返回一个野指针;
语法格式:返回值数据类型 *函数名(形参表){函数体语句}
int g_a=250; //全局非静态
static int g_b=250; //全局静态,生命周期等程序结束,全局使用范围
int *A(void)//指针函数返回的地址值只和变量生命周期有关系,与范围无关
{
int a=200; //局部非静态
static int b=100;//局部静态
//1. 花括号消失时,200被抹去,内存回收,无指向后变为野指针
return &a;
//2. 静态变量生命周期:一次分配,终生适用;虽然使用范围在A,但是在main函数可以修改
return &b;
//3. 可以,一次分配终生使用
return &g_a;
//4. 全局存在,可以,一次分配终生使用,
return &g_b;
}
int main(void)
{
int *p=A();//切记勿返回局部非静态指针,报错
*p=30000;
printf("%dn",*p);
return 0;
}
十、指针 函数 数组
函数访问数组的公式:
- void A(int a[ ], int length); //其中a是数组的地址
- void A(int *p,int length); //两者等价
#include总结extern void print(int *p,int len); int main(void) { int a[]={1,2,3,4}; int len=sizeof(a)/sizeof(a[0]); print(a,len); return 0; } void print(int *p,int length) { for(int i=0;i p[i] +=2;//两者等价,两步走 } for(int i=0;i printf("my a[]_number_is %dn",p[i]);//两者等价,两步走 } }
- 本章节完整讲述指针的声明,初始化,基本用法,复习备用;



