C语言指针的图文详解
作者:黎丶辰 发布时间:2021-07-26 11:26:25
指针是什么?
指针(Pointer)是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。
换句话说就是可以通过指针找到以它为地址的内存单元。
理解:内存图解。
指针是个变量,存放内存单元的地址(编号)。
int main(){
int a = 10;//在内存中开辟空间存储
int* p = &a;//先对变量a取出它的地址,可以使用&操作。
//将a的地址存放在p变量中国,p就是一个指针变量
}
小结:指针就是变量,内容是地址。(存放在指针中的值被当做地址处理)
指针的大小
在32为计算机上指针大小4字节。
在64为计算机上指针大小8字节。
指针和指针变量
关于地址
printf("%p \n",&a);//%p地址格式 &a取a的地址
int* p = &a;
//int*指针类型
//p 指针变量
//&a 取地址
使用
*p //解引用操作符
int a =10; //在内存中存储10 还有char*等类型
int* p = &a;//定义指针,位置为a的内存
*p = 20; //更改指针指向内存的 值
printf("a= %d",a);//结果为a=20
int* p的理解 p是int类型的一个指针(仅此而已),一般*p指向的也是一个int型的
1. 指针类型决定了指针进行解引用操作的时候,能访问空间的大小
int main(){
int n = 0x112233;
char* p = (char*)&n;
int* pi = &n;
*pc = 0; //在调试的过程中观察内存的变化。
*pi = 0;
return 0;
}
int*; *p可以访问4个字节。
char*; *p可以访问1个字节。
double*; *p可以访问8个字节。
原因 是类型本身所需的内存空间就是指针可以控制的空间。
意义:使用时选用合适的指针类型进行定义
2. 指针加减整数
int main(){
int a = 0x11223344;
int* p1 = &a;
char* p2 = &a;
printf("%p\n",p1);
printf("%p\n",p1+1);
printf("%p\n",p2);
printf("%p\n",p2+1);
return 0;
}
int类型时0C->10 变化4, char类型时0C->0D 变化1。
理解:指针加一不是指向下一个紧挨着的地址,是指向下一个指针变量对应的类型变量开始的地址。
意义 指针类型决定了:指针走一步走多远(指针的步长)
野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针的成因
1.指针未初始化
int main(){ int a;//局部变量不初始化,默认是随机值 int *p;//局部的指针变如果没有初始化,就被初始化为随机值。}int main(){
int a;//局部变量不初始化,默认是随机值
int *p;//局部的指针变如果没有初始化,就被初始化为随机值。
}
2.指针越界访问
int main(){
int arr[10];
int *p = arr;
for(int i = 0;i<12;i++){
p++;
}
//当指针的范围超出数组的范围时,p就是野指针。
}
3.指针指向的空间释放
int main(){
int arr[10];
int *p = arr;
for(int i = 0;i<12;i++){
p++;
}
//当指针的范围超出数组的范围时,p就是野指针。
}
解析:在main函数调用test()时,进入test()函数,int a语句开辟临时的内存空间并将这个内存空间存储为10;返回函数的时候返回的临时的a的地址给*p,然后test函数已经在执行完test函数后结束,a的内存空间被销毁。这时的*p就是指向的地址正确但是内容已经改变。
将未知位置的值进行修改是非常危险的
如何避免野指针
1.指针初始化
2.小心指针越界
3.指针指向内存释放 即 指向NULL
4.指针只用之前检查有效性
指针运算
1.指针加减整数
2.指针-指针
3.指针的关系运算
指针加减指针
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr)/sizeof(arr[0]);
int* p = arr;
for(int i=0;i<sz;i++){
printf("%d ",*p);
p = p+1;// p++
}
int* p = &arr[9];
for(int i=0;i>0;i++){
printf("%d ",*p);
p-=1;// p++
}
return 0;
}
指针-指针
int main(){
int arr[10]={1,2,3,4,5,6,7,8,9,10};
printf("%d",&arr[9]-&arr[0]);//输出9 中间元素的个数。
printf("%d",&arr[0]-&arr[9]);//输出-9
return 0;
}
指针减指针必须是自己减去自己。否则结果不可预知。
指针实现strlen()
int my_strlen(char* str){
char* start = str;
char* end = str;
while(*end != '\0'){
end++;
}
return ;
}
int main(){
char arr[] = "hello";
int len = my_strlen(arr);
printf("%d\n",len);
return 0;
}
指针的关系运算
int main(){
float values[5];
for(float* vp=&values[5];vp>&values[0];){
printf("haha ");
*--vp = 0;
}
return 0;
}
这里碰到了两个问题 1. values[5]本身不属于数组的部分。但是可以使用。经测试values[5]不会警告,但是values[-1]及以下或values[6]及以上都会报错。2.指针的加减是类型位置的移动数组总也就是一个一个往过走。
for(float* vp=&values[5-1];vp>=&values[0];vp--){
printf("haha ");
*vp = 0;
}
这里在绝大多数的编译器上是可以顺利完成任务的,然而我们应该避免这第二种写法,因为标准不能保证他是可行的。
标准规定:允许指向数组元素的指针和指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个位置的指针进行比较。
指针和数组
int main(){
int arr[10]={0};
printf("%p\n",arr); //地址-首元素地址
printf("%p\n",&arr[0]);
}
一般情况数组名都代表首元素的地址
除了:
1. &数组名 这时数组名代表整个数组的地址
2. sizeof(数组名) 这时也是代表整个数组。
二级指针
将第一层指针1想成变量,再取这个变量的地址存为一个指针2。那么指针2指向指针1,指针1指向原变量。原变量的地址存在了指针1中,指针1的地址存在了指针2中。
int main(){
int a = 10;
int* pa = &a;
int** ppa = &pa;//ppa就是二级指针。
//存在 * 及以上指针,(无限套娃)
}
指针数组、数组指针
指针数组其实是个数组,数组指针是个指针
指针数组:存放指针的数组
int a = 10;
int b = 20;
int c = 30;
int* arr[3] = {&a,&b,&c};//指针数组
数组指针:指向数组的指针。
main(){
int a = 10;
int* pa = &a;
int** ppa = &pa;//ppa就是二级指针。
//存在 * 及以上指针,(无限套娃)
}
### 指针数组、数组指针
指针数组其实是个数组,数组指针是个指针
<u>**指针数组**</u>:存放指针的数组
~~~c
int a = 10;
int b = 20;
int c = 30;
int* arr[3] = {&a,&b,&c};//指针数组
数组指针:指向数组的指针。
来源:https://blog.csdn.net/Tian_baby/article/details/122294828


猜你喜欢
- stack介绍和使用stack文档介绍stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插
- 并发与并行并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点
- Android 6本文根据我个人的开发经验,总结了从 Android 6 - Android 13 重要的行为变更。当然,这不是 Andro
- String的字符串是不可变的,StringBuffer和StringBuilder是可变的String:是字符常量,适用于少量的字符串操作
- 从上一节的小demo中我们搞清楚了如何跟易宝对接以及易宝的支付流程。这一节我们来做一下支付页面以及在页面中导入银行图标。1. 存储银行图标银
- 1. 概述官方JavaDocsApi: javax.swing.JFrameJFrame,窗口。JFrame 是一个可以独立显示的组件,一个
- 一、添加pom.xml依赖<parent> <groupId>org.springfram
- Java注解是在JDK5时引入的新特性,鉴于目前大部分框架(如spring)都使用了注解简化代码并提高编码的效率,因此掌握并深入理解注解对于
- SpringBoot 项目启动之后执行自定义方法的两种方式在测试配置中心的配置时,想在项目启动成功之后打印配置项,然后需要执行自定义的类一般
- 前言昨晚想在Android应用中增加一个int映射到String的字典表,使用HashMap实现的时候,Eclipse给出了一个警告,昨晚项
- 引言最近的项目需求中有使用到后端发送http请求,在网上寻找资料后发现可以使用spring自带的RestTemplate类实现,故作此记录项
- 今天我们介绍的是jpa删除和事务的一些坑,接下来看看具体内容。业务场景(这是一个在线考试系统)和代码:根据问题的id删除答案reposito
- 一、反射概述反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一
- 一、前言知识补充:Arrays.copyOf函数:public static int[] copyOf(int[] original, in
- 只需要调用该类的一个方法createNewFile(),但是在实际操作中需要注意一些事项,如判断文件是否存在,以及如何向新建文件中写入数据等
- C#接口的学习,在编程中,我们经常会用到接口,那什么是接口呢?接口描述的是可属于任何类或结构的一组相关功能,所以实现接口的类或结构必须实现接
- 1.新建一个数组,把原来数组的内容搬到新数组中。这种方法实现的思路是:先新建一个数组(前提条件是长度得比原来的长),然后把原来数组的内容搬到
- 1.首先,需要指定获取的文件夹,以及获取文件的文件名;文件夹:strLocalPath = System.Windows.Forms.App
- 上一篇文章已经介绍了如何为RecyclerView添加FootView,在此基础上,要添加分页加载的功能其实已经很简单了。 上一篇文章地址:
- Class.forName(xxx.xx.xx) 返回的是一个类一.首先你要明白在java里面任何class都要装载在虚拟机上才能运行。1.