深度理解指针与内存

打印 上一主题 下一主题

主题 984|帖子 984|积分 2952

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
指针的概念

指针是一个特别的变量,它存储的是某块内存空间的地点值
地点的概念

每个数据都有自己的地点,每个地点存放一字节的数据。用下面的例子举例
int a[4]={1,2,3,4};
假设数组a的首地点是0x1000,则第一个元素 1 (元素 1 是int型,占4个字节)的存储方式如下:
内存地点字节内容(十六进制)分析
0x10000x01最低有效字节(LSB)
0x10010x00
0x10020x00
0x10030x00最高有效字节(MSB)
大部门计算机系统都采用小端存储模式,即数据低位字节放在低地点处,高位字节放在高地点处
数组中所有的元素在内存中的存储方式如下:
地点内容(值)分析
0x10001a[0](首元素)
0x10042a[1]
0x10083a[2]
0x100C4a[3]

数组名 a 其实就是数组首元素的地点(即 &a[0]),它本身不是一个变量,只是表示数组的起始地点
如果对 a 取地点(&a)得到的地点值与 a 类似,但类型差别:

  • a 的类型是 int* (指向 int 的指针)
  • &a 的类型是 int(*)[4](指向长度为4的数组的指针)
int *p = a;
指针变量 p 指向数组首元素,相当于 指针p 的值为 0x1000,就是数组的起始地点
指针的大小

32位的系统上,地点长度就是32位,也就是4个字节。所以一个指针变量就是4个字节。 char *p; sizeof(p) = 4;
取地点& 解引用*

&a 的结果是一个指针,指针的类型就是 a 的类型加上 * ,指针指向的类型是 a 的类型,指针指向的地点是 a 的地点。
*p 的结果是 指针p 所指向的内容,这个内容可以是一个数值、字符串、数组、指针。
注意:
指针的类型决定:
1.指针算术运算的步长,即 p+1 移动的字节数。
2.解引用时,访问多少字节的数据
例如:
  1. int a[10]={0x0101,1,2,3,4,5,6,7,8,9};
  2. int *p=a;
  3. printf("%d %d",*p,*(p+4)); //打印257 4
  4. printf("%d",*(char*)p); //打印1,(char*)类型强转只是告诉编译器:
  5.                         //1.如何解释该地址处的数据(按一字节读取),从低位数据开始读
  6.                         //2.指针算术的步长
  7. char *p=a;
  8. printf("%d %d",*p,*(p+4)); //打印1 1
复制代码
指针 与 数组

char *p = "abcde";
p:指针指向字符串开头,也就是第一个字节a的地点,sizeof(p)=4
p+1:指针指向了第二个字节b的地点,sizeof(p+1)=4
*p:指针所指向的内容,也就是第一个字节a,sizeof(*p)=1 p[0]:同上
&p:对指针变量 p 取地点,sizeof(&p)=4
&p+1:指针变量 p 的地点的下一个地点,然后将地点往后移动4个字节的大小(这里+1的步长是sizeof(char*)=4字节),sizeof(&p+1)=4
&p[0]+1:先取第一个字节a的地点,然后将地点向后移动1个字节的大小(这里+1的步长是sizeof(char)=1字节),得到p[1]的地点,也就是字符b的地点。sizeof(&p[0]+1)=4

int arr[5] = {1,2,3,4,5};
arr:表示整个数组,sizeof(arr)=5
arr+0:表示首元素的地点,sizeof(arr+0)=4
*arr:arr本质上是指向数组开头的指针,也就是指向第一个元素,*arr则是对第一个元素解引用,所以*arr=1,sizeof(*arr)=1
arr[1]:arr的第二个元素
&arr:对数组取地点,sizeof(&arr)=4
&arr+1:数组arr的地点的下一个地点,然后将地点往后移动4个字节的大小(这里+1的步长是sizeof(int*)=4字节),sizeof(&arr+1)=4
&arr[0]+1:取出第一个元素的地点,然后将地点向后移动4个字节的大小(这里+1的步长是sizeof(int)=4字节),sizeof(&arr)=4
内存的三种分配方式

1.静态内存分配(全局变量、静态变量、常量)
特点:


  • 内存的分配和释放由编译器在编译阶段完成
  • 分配的内存在步伐整个生命周期内存在(直到步伐结束才释放)。
优点:


  • 无需手动管理,生命周期明白。
缺点:


  • 内存固定,无法动态调整大小。
  • 大概浪费内存(未使用的静态内存无法释放)。
2.栈内存分配(函数内的局部变量、函数参数)
特点:


  • 内存的分配和释放由编译器自动管理(通过函数调用栈)。
  • 分配的内存随函数调用结束自动释放(局部变量的生命周期)。
优点:


  • 分配速度快(仅移动栈指针)。
  • 无需手动管理,内存自动回收。
缺点:


  • 内存大小固定(栈空间有限,默认几MB)。
  • 大对象大概导致栈溢出(如大数组 int arr[1000000])。
3.堆内存分配(动态大小的数据结构(链表、数组)、需要长期存在或跨函数使用的数据)
特点:


  • 内存的分配和释放由步伐员手动控制(通过 malloc, calloc, free 等函数)。
  • 内存生命周期完全由代码逻辑决定
优点:


  • 内存大小灵活(按需分配)。
  • 生命周期可控。
缺点:


  • 分配速度较慢(需查找可用内存块)。
  • 需手动管理,易导致内存走漏(忘记 free)或野指针。
常用的动态内存分配函数:


  • malloc:用于分配指定大小的内存块,返回指向该内存块起始地点的指针。如果分配失败,返回空指针(NULL)。
  • calloc:与malloc类似,但它还会将分配的内存块初始化为零。函数原型:void* calloc(size_t num_elements, size_t element_size);。
  • realloc:用于重新分配已经分配的内存块大小,可以扩大或缩小内存块。函数原型:void* realloc(void* ptr, size_t new_size);。
  • free:用于释放动态分配的内存块,将该内存块返回给堆,以便其他步伐可以使用。函数原型:void free(void* ptr);。
函数功能初始化特别性
malloc分配未初始化内存基础分配函数
calloc分配并初始化为零全零适合数组初始化
realloc调整已分配内存的大小大概移动内存块
free释放内存N/A必须与分配函数成对使用

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

悠扬随风

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表