首页 > 其他分享 >深入理解指针(1)

深入理解指针(1)

时间:2024-03-27 21:58:59浏览次数:23  
标签:10 arr int 地址 理解 深入 printf 指针

 前言:通过指针我们可以访问存储于内存中的数据,也可以通过指针更改内存中的数据。那么今天让我们来揭开指针的神秘面纱吧!


目录

1.内存与地址的关系

2.对内存的访问与使用

3.对指针变量的定义的解读

4. 指针类型的大小

5. 指针类型的意义

扩展(void*)

6.传值调用和传值调用

7.const修饰指针变量

7.1.const修饰*p

7.2.const修饰p 

8.指针的运算

8.1 指针+-整数

8.2 指针-指针

8.3指针的关系运算

9. 指针使用时常见的错误

9.1 指针未初始化

9.2  越界访问

9.3 指向的变量空间被释放 

10.assert函数

祝语





1.内存与地址的关系

关系:内存可以或分为一个一个的内存单元,内存单元中存放着程序的数据,一个内存单元的大小为1字节,并且每个内存单元都对应着一个独一无二的地址。 

2.对内存的访问与使用

这里有一段代码:

#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;//&a的意思就是取出内存中存放a的空间的地址,这句的意思就是将a的地址给p
	*p = 12;//*p是访问p变量中存放的地址所指向的空间,并将空间的数据改为12
	printf("%d", a);
	return 0;
}

代码运行结果: 

解释:如何访问和使用内存呢?

首先, 得有那块内存的地址,这是就需要用到 &(取地址操作符),比如&a 就是取出变量a所在内存单元的地址;然后,就是访问这块空间,就需要用到 *(解引用操作符),比如,*p,这时p中存放的是 a 的地址,*p就是访问a的内存单元。

下面是对上面代码和解释的图解: 

3.对指针变量的定义的解读

    int a = 10;
    //这里先定义一个指针变量
    int *p = &a;

 解读

* 与p 结合表示p是一个指针变量,注意指针变量中存储的数据是地址,int 表示 p指向的内存空间所存放的数据类型为 int。

int* 表示p的类型

图解


4. 指针类型的大小

不管是int* 的指针还是char* 的指针其大小都是固定的,如果是32为机器,其大小为4字节,如果为64位机器,大小为8字节。

代码:

#include <stdio.h>
int main()
{
	printf("%zd\n", sizeof(int*));
	printf("%zd\n", sizeof(char*));
	printf("%zd\n", sizeof(short*));
	//......有兴趣可以自己测
}

x64环境:

x86环境下:


5. 指针类型的意义

 示例代码:

#include <stdio.h>
int main()
{
	int a = 10;
	short b = 2;
	int* pa = &a;
	short* pb = &b;
}

1.如果是int* 的指针pa ,pa中存放的是a 地址的首地址,但因为pa 是int* 的指针所以他可以访问4个字节大小的内存单元,也就是一个int大小的内存空间;同理,pb 就可以访问2个字节大小的内存单元 。

2.int* 指针p进行加减运算时,也会加减相应的其指向数据类型的大小,比如,pa+1,就相当于pa中存放的地址+4,而pb+1,就是pb中存放的地址+2。

扩展(void*)

void* 的指针可以接受任何类型的指针,但是用解引用使用void* 类型的指针时,由于void*类型的指针不能直接解引用,所以必须强制类型转换成其他类型的指针。 


6.传值调用和传值调用

图解:

 解释:

最上面的代码的运行结果仍是10,因为传过来的是a的值,change函数中的a只是main中的一份临时拷贝,两者的地址不同,所以改变change中的a不会改变main中a的值。

而下面的代码是12,因为传过来的是a的地址,通过地址改变dea的值。

7.const修饰指针变量

7.1.const修饰*p

7.2.const修饰p 


8.指针的运算

8.1 指针+-整数

#include <stdio.h>
int main()
{
	int a = 0;
	char b = 'a';
	int* pa = &a;
	char* pb = &b;
	printf("%p\n", pa);
	printf("%p\n", pa + 1);
	printf("%p\n", pa - 1);
	printf("%p\n", pb);
	printf("%p\n", pb + 1);
	printf("%p\n", pb - 1);
}

运行结果:

解释:因为pa指针是int*类型的,pa的+-相当于pa中存放的地址+-4(pa指向的数据类型的大小);pb+-,就相当于pb中存放的地址+-1。 

8.2 指针-指针

代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", (arr+9) - arr);//arr是数组首元素的地址,arr+9指向10
	printf("%d\n", arr - (arr + 9));
	return 0;
}

图解: 

指针-指针的意思就是,两指针间有几个元素。

如果是高地址-低地址是正数;反之,是负数。

比如这题,arr 和arr+9之间有1,2,3,4,5,6,7,8,9这九个元素。 

8.3指针的关系运算

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	if (arr > arr + 9)//比较两个指针的大小
		printf(">");
	else if (arr == arr + 9)
		printf("=");
	else if (arr < arr + 9)
		printf("<");
	return 0;
}

运行结果:

两个指针的关系运算实际上就是,比较指针中存放的地址的大小。 


9. 指针使用时常见的错误

9.1 指针未初始化

错误代码:

#include <stdio.h>
int main()
{
	int* p;//未初始化,并且使用指针
	*p = 10;
	printf("%d", *p);
	return 0;
}

 报错:

 建议:

使用时一定要初始化,如果暂时不知道指针应该指向哪,可以先指针置NULL

9.2  越界访问

错误代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	//错误:i= 10时 ,指针不再指向arr,而指向10的下一个空间,越界访问arr
	for (int i = 0; i <= 10; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

运行结果:

图解:

建议:清楚数据在内存分布,多写代码,多思考,多总结。

9.3 指向的变量空间被释放 

 错误代码:

#include <stdio.h>
int add(int a, int b)
{
	int sum = a + b;
	return &sum;//当运行完这个函数时,在该函数中创建的变量都会给释放
}
int main()
{
	int a = 10;
	int b = 12;
	int * psum = add(a, b);
	printf("%d", *psum);
}

 解释:psum 中时sum 的地址,而运行完add函数后,其内部定义的变量的空间均会被释放,此时psum指向的空间不再有数据,这时如果是用psum就变成了非法访问。

 建议:不要返回函数中定义的变量的地址。


10.assert函数

介绍:assert 可以用来判断 指针是否为NULL,可以防止对空指针的解引用。 

#include <assert.h>
int main()
{
	int* p = NULL;
	assert(p != NULL);//如果p等于NULL,会报错并终止程序
	return 0;
}

祝语

愿大家指针学得越来越好!

标签:10,arr,int,地址,理解,深入,printf,指针
From: https://blog.csdn.net/2402_82658387/article/details/137042345

相关文章

  • 深⼊理解指针5
    1.回调函数是什么?回调函数就是⼀个通过函数指针调⽤的函数。如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于......
  • 如何理解CDN?说说实现原理?
    一、是什么CDN(全称ContentDeliveryNetwork),即内容分发网络构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内......
  • 对数组方法reduce和map的深入理解,用reduce方法实现map方法
     引言:偶然看到一道面试题,希望可以用reduce方法实现map方法,看过之后发现这是个有趣的逻辑思维题、既要对数组方法有深入理解也要有一定编程能力。一、reduce语法reduce:高阶数组方法,对数组中的所有元素应用一个函数(由你提供),将其减少为单个输出值。arr.reduce((prev,cur,ind......
  • AQS 巨好理解的版本
    AQS同步队列(入口等待队列):(ReentrantLock、ReentrantReadWrite)用于实现独占锁,例如ReentrantLock、ReentrantReadWrite首先,CAS尝试加锁。加锁成功:设置state=1(原来state=0),然后设置独占属性exclusiveOwnerThread=thread()(用于可重入锁判断)加锁失败:只能干什么两件......
  • 901-深入浅出Python量化交易实战的配套视频和代码(段小手)中文PDF+源代码(源文件)
    小瓦的故事——从零开始本书源于一个真实的故事,故事的主角是一位名叫小瓦的姑娘。小瓦出生在一个普通的家庭,父母都是老实淳朴的普通人,靠着并不丰厚的收入把小瓦养育成人。18岁那年,小瓦考上了一所不好不坏的大学,所学专业是一个就业前景算不上理想的专业。再加上她本身也谈不......
  • 深入理解 React 中的 children props 和 render props
    深入理解React中的childrenprops和renderprops在React中,childrenprops和renderprops是两种常见的组件复用模式,它们都可以帮助我们更好地组织和复用组件代码。虽然它们的实现方式有所不同,但都能够有效地实现组件之间的数据传递和功能共享。childrenpropsch......
  • 深入解析以太坊Dencun升级:提升网络性能与安全的关键举措
    近年来,以太坊网络一直在不断演进和发展,为了应对日益增长的用户需求和挑战,以太坊社区不断提出并实施各种升级和改进措施。其中,Dencun升级作为最新的一项重大改革,旨在提升以太坊网络的性能和安全性,为其未来发展奠定更坚实的基础。本文将深入解析Dencun升级的关键举措,以及这些举措......
  • 我的芯片测试理解
    前言:芯片测试:确保芯片在生产和使用过程中的稳定性和可靠性 (芯片的质量和性能)。测试芯片主要分为:解密芯片测试,功耗测试,性能测试,可靠性测试,芯片烧录和老化测试。一、解密测试解密测试:通过分析和测试来验证解密芯片的正常运行,它包括对芯进行开放测试(指芯片解密和破解,用来了解......
  • Python教程:深入探索 Python 列表(List)
    在Python中,列表(List)是一种非常常用且强大的数据结构,它能够存储多个元素,并提供了丰富的操作方法。本文将带您深入探索Python列表,从基础知识到高级应用,让您全面掌握列表的各种技巧和用法。1.列表基础1.1什么是列表列表是一种有序、可变、允许重复元素的数据结构,用方......
  • 双指针的详细教程
    双指针算法是一种常用的算法技巧,它通常用于在数组或字符串中进行快速查找、匹配、排序或移动操作。双指针并非真的用指针实现,一般用两个变量来表示下标(在后面都用指针来表示)双指针算法使用两个指针在数据结构上进行迭代,并根据问题的要求移动这些指针。双指针往往也和单调性......