当前位置:网站首页>c语言分层理解(c语言指针(上))
c语言分层理解(c语言指针(上))
2022-08-04 00:04:00 【爱玩的青轴】
文章目录
保持空杯心态,干就完了!
望小伙伴们多多关注哦!
1.指针是什么?
指针是内存中的一个最小单元的编号,也就是地址。
怎么来理解这句话呢?看图:
平时我们口语中所说的指针通常是指针变量,是用来存放内存地址的变量。
总结:指针就是地址,口语中所说的指针通常是指针变量,用来存放地址。
初步了解指针概念后再来看看这个图:
这里就更清楚内存的概念。
再来说说指针变量:
我么通过&操作符取出变量的内存起始地址,把地址可以存放在一个变量中,这个变量就是指针变量。而指针变量也是有地址的。
总结:
指针变量是用来存放地址的变量。
这里有两个问题:1.一个小的单元到底是多大?2.如何编址的?
经过仔细的计算和权衡发现一个字节给一个对应的地址是比较合适的。
对于32位机器,假设由32跟地址线,那么假设每跟地址线在寻址的时候产生高电压和低电压也就是1或者0.那么32跟地址线产生的地址会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
…
11111111 11111111 11111111 11111111
这里就是232次方个地址。
每个地址标识一个字节,那么我们就给(232byte == 232/1024kb == 232/1024/1024mb=232/1024/1024/1024 == 4GB)4G的空间进行编址,那么64位机器下就是232*4GB的空间。
恍然大悟:在32位机器上,地址是32个0或者1组成的二进制序列,那地址得用4个字节的空间来存储,所以指针变量的大小是4个字节。在64位机器上,如果有64跟地址线,那么一个指针变量的大小就是8个字节。
再看一遍:
总结:
指针变量是用来存放地址的,地址是唯一标识一个内存单元的。
指针的大小在32位平台上是4个字节,在64位平台上是8个字节。
2.指针和指针类型
指针有很多类型,比如:
int*、double*、float*、char*、short* 等等
我们说既然指针在内存中相同平台下(32位平台或者64位平台)都是一样的大小,那么我们要指针的类型有什么意义呢?
通过例子来说明指针类型有什么用。
2.1 指针解引用
2.2 指针±整数
总结:指针类型其实是决定解引用时访问几个字节的大小以及决定了指针的步长。
3.野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
3.1.1 指针未初始化
3.1.2 指着越界访问
#include <stdio.h>
int main()
{
int arr[10] = {
0 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (i = 0; i <= sz; i++)//这里当下标为sz时就数组越界访问了
{
*p = i;
p++;
}
return 0;
}
3.1.3 指针指向的空间释放
动态内存管理中会着重讲解,这里点一下。
int* test()
{
int num = 100;
return #
}
int main()
{
int* p = test();
*p = 200;
return 0;
}
这段代码看上去其实没什么问题,问题就是看上没什么问题,其实问题在于函数调用时开辟内存空间,结束时释放内存空间。所以这里的num在函数执行完后,又返回给了操作系统,这里的&num不知道取的哪里的地址,很危险,再解引用对其这一个块内存空间修改,这就是一个野指针问题。
3.2 如何规避野指针
1.指针初始化
2.小心指针越界
3.指着指向空间释放,及时置为NULL(空指针)
4.避免返回局部变量地址
5.指针使用之前要检查是否具有有效性
对第5点进行解释:
#include <stdio.h>
int main()
{
int* p = NULL;
int a = 20;
p = &a;
if (p != NULL)
{
*p = 20;
}
return 0;
}
如果不检测,假如给一个空指针的指针变量对其给改值:
4.指针运算
4.1 指针±整数
#include <stdio.h>
#define N_VALUES 5
float values[N_VALUES];
float* vp;
//指针+-整数;指针的关系运算
int main()
{
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
return 0;
}
分析:
4.2 指针-指针
指针-指针是有意义的,使用场景是求数组元素个数等等。
在生活中,日期-日期=天数,但是你要是日期+日期这有什么意义呢,指针也是一样,指针+指针没有使用场景。
举一个例子:
//计算字符串中元素个数
#include <stdio.h>
int my_strlen(char *p)
{
char* ptr = p;
while (*p != '\0')
{
p++;
}
return p-ptr;
}
int main()
{
char arr[] = "abcdef";
int num = my_strlen(arr);
printf("%d\n", num);
return 0;
}
指针的关系运算
两段代码相比一个是访问数组前的地址,一个是访问后的地址。这实际上是可以顺利完成任务的,但是应该避免上面第一种写法,因为标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
5.指针和数组
我们知道了数组名就是首元素地址,那么我们可以通过指针来间接访问数组元素,如:
#include <stdio.h>
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}//最终输出1 2 3 4 5 6 7 8 9 10
因为数组在内存中的地址是连续存放的,所以可以用指针来访问每个元素的地址。
数组这方面知识比较薄弱可以点这个链接来看看哟c语言数组!
6.二级指针
通过一个实例,来观察二级指针:
这里怎么去解释呢?
这里有二级指针的概念那么有三级指针或者四级指针吗?理论上是有的,下面来看看实例:
这里和二级指针逻辑一样,就不再说明关系了。
7.指针数组
首先我们要搞明白指针数组是什么?
其实指针数组是一个存放指针的数组。
还是用实例来理解:
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int e = 50;
int* arr[5] = {
&a,&b,&c,&d,&e };
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", *(arr[i]));
}
printf("\n");
for (i = 0; i < 5; i++)
{
printf("%p\n", arr[i]);
}
printf("\n");
printf("%p\n", &a);
printf("%p\n", &b);
printf("%p\n", &c);
printf("%p\n", &d);
printf("%p\n", &e);
return 0;
}
通过代码进行分析:
再看内存地址:
分析得到:
数组中的元素在内存是连续存放的,但是指针数组中的元素在内存中是随机存放的,因为变量是创建后,地址拿到数组中存放,千万别把数组的这个性质也和指针数组搞的一样,那就有点尴尬了。
再看一个应用场景:
#include <stdio.h>
//用一维数组模拟一个二维数组
//前提是必须每个一维数组的元素个数一样
int main()
{
int arr1[] = {
1,2,3,4,5 };
int arr2[] = {
2,3,4,5,6 };
int arr3[] = {
3,4,5,6,7 };
int arr4[] = {
4,5,6,7,8 };
//指针数组arr中存的是每个数组的数组名(也就是每个数组的首元素地址)
int* arr[] = {
arr1,arr2,arr3,arr4 };
//外层for循环指指针数组arr
int i = 0;
for (i = 0; i < 4; i++)
{
//内存for循环指指针数组中的内容
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
#include <stdio.h>
int main()
{
int arr1[] = {
1,2,3,4,5 };
int arr2[] = {
2,3,4,5,6 };
int arr3[] = {
3,4,5,6,7 };
int arr4[] = {
4,5,6,7,8 };
int* arr[] = {
arr1,arr2,arr3,arr4 };
int i = 0;
for (i = 0; i < 4; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", *( * (arr + i) + j));
}
printf("\n");
}
return 0;
}
逻辑上不用分析,这里我们来看看指针数组的存储以及两种打印形式的理解:
边栏推荐
- 响应式织梦模板除尘器类网站
- 浅谈我国产业园区未来的发展方向
- ping数据包中的进程号
- Justin Sun: Web3.0 and the Metaverse will assist mankind to enter the online world more comprehensively
- A simple understanding of TCP, learn how to shake hands, wave hands and various states
- ros mavros stereo读取rosbag并记录IMU和图片到文件夹
- 射频芯片ATE测试从入门到放弃之参数测试
- In V8 how arrays (with source code, picture and text easier to understand)
- View the version number of CUDA, pytorch, etc.
- V8中的快慢数组(附源码、图文更易理解)
猜你喜欢
A simple understanding of TCP, learn how to shake hands, wave hands and various states
689. 三个无重叠子数组的最大和
Using matlab to solve the linear optimization problem based on matlab dynamic model of learning notes _11 】 【
Creo 9.0二维草图的诊断:着色封闭环
密码学基础以及完整加密通讯过程解析
Shell 用法梳理总结
The world's first mass production, with the most fixed points!How does this AVP Tier1 lead?
Salesforce's China business may see new changes, rumors may be closing
分子个数 数论(欧拉函数 前缀和
绕任意轴旋转矩阵推导
随机推荐
689. 三个无重叠子数组的最大和
libnet
JVM垃圾回收总结(未完待续)
关于mnn模型输出的数据杂乱无章问题
【OpenCV图像处理】 图像拼接技术
In V8 how arrays (with source code, picture and text easier to understand)
TypeScript学习
小米--测试开发
LeetCode 0155. 最小栈
响应式织梦模板塑身瑜伽类网站
栈的压入、弹出序列
Install third-party packages via whl
Internship: Upload method for writing excel sheet (import)
Shell 用法梳理总结
OpenCV 图像拼接
简单了解下 TCP,学习握手和挥手以及各种状态到底是怎么样的
通过whl安装第三方包
2021年数据泄露成本报告解读
利用matlab求解线性优化问题【基于matlab的动力学模型学习笔记_11】
全球首款量产,获定点最多!这家AVP Tier1如何实现领跑?