C/C++ 之 内存管理

1、char*

首先来看这样一段代码:

int main()
{
	char *str = "abcd";
	return 0;
} 

DEV-C编译器会提示 Warning:

[Warning] deprecated conversion from string constant to ‘char*’[-Wwrite-strings]

VS编辑器中会直接编译错误

首先说解决方案:

  • 解决方案一

    在VS2019中依次点击项目->属性->C/C+±>语言->符合模式,将原来的“是”改为“否”即可。

  • 解决方案二

    在声明变量 char* 时改成 const char *即可

错误的原因:
  上述声明的含义是:str指向的是一个字符串常量,字符串常量是不允许被更改的,任何更改字符串常量的行为都是错误的,例如:str[2]=‘c’。为了严谨,编译器一般需要在char*前面加上一个const,更加保险、也更能让人清楚的知道str指向的内容不可修改。

而下面的代码却可以正常运行:

int main()
{
	const char *str = "abcd";
	printf("str = %d\n",str);
	str = "12345";
	printf("str = %d\n",str);
	return 0;
} 

输出的内容是:
str = 4210688 //str存储的是地址
str = 4210703
  实际上,我们只是修改了str指针的指向,并没有修改str指针指向的内容,所以不会报错。这也是很多初学者不太理解的地方。C语言的精髓还是在指针这里。



2、内存管理

  上面提到了字符串常量,为什么字符串常量不可更改呢? 这就设计到内存管理这块的知识了。
  C语言中,常见的可以分为:代码段、数据段、BSS段、堆、栈。

在这里插入图片描述

  • 堆(heap):用于动态内存分配,一般由程序员分配和释放,若程序员不释放,程序结束时有可能由OS回收。堆中的内存区域不是连续的,而是将有效的内存区域经过链表指针连接起来的。当进程调用malloc 等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。在将应用程序加载到内存空间执行时,操作系统负责代码段、数据段和BSS段的加载,并将在内存中为这些段分配空间。栈段亦由操作系统分配和管理,而不需要程序员显示地管理;堆段由程序员自己管理,即显式地申请和释放空间。

  • 栈(stack):该部分存储的变量一般是在函数体中定义的局部变量(不包括static声明的变量)。栈又称堆栈, 是用户存放程序临时创建的局部变量,常见的就是函数内部声明的变量(static、const除外)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/ 恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

  • 代码段(text):通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读区域。代码区的指令中包括操作码和要操作的对象(或对象地址引用)。如果是立即数(即具体的数值,如5),将直接包含在代码中;如果是局部数据,将在栈区分配空间,然后引用该数据地址;如果是BSS区和数据区,在代码中同样将引用该数据地址。另外,代码段还规划了局部数据所申请的内存空间信息。

  • BSS:Block Started by Symbol的简称,通常是指用来存放程序中未初始化的全局变量和静态变量。

  • 数据段(data):静态内存分配,通常是指用来存放程序中已初始化的全局变量和静态变量以及常量数据。data又分为读写数据段(rw.data) 和 只读数据段(ro .data)。

  上面所讲到的字符串常量,一般存在只读数据段,理论上也可以存在代码段(具体和编译器有关)。



3、char* 和 char a[]

这是一个会困扰C语言初学者的问题,

#include <stdlib.h>  
#include<stdio.h>  
  
int main()  
{  
     const char * p = "asdfsasd";
     //p里面存储的是字符串的第一个字符的地址
     
     printf("%c",*p);   //a
     
     printf("%s",p);	//asdfsasd
     
     printf("%s",*p);	//报错
     
     return 0;
}  

  C语言中操作字符串是通过它在内存中的存储单元的首地址进行的,这是字符串的终极本质。
  C语言中规定数组代表数组所在内存位置的首地址,也是 str[0]的地址,即str = &str[0];
  而printf("%s",str); 为什么用首地址就可以输出字符串。 因为还有一个关键,在C语言中字符串常量的本质表示其实是一个地址,这是许多初学者比较难理解的问题。
  用一句话来概括,就是 char *s 只是一个保存字符串首地址的指针变量, char a[ ] 是许多连续的内存单元,单元中的元素为char 。之所以用 char *能达到char a [ ] 的效果,还是字符串的本质,地址,即给你一个字符串地址,便可以随心所欲的操所他。但是,char* 和 char a[ ] 的本质属性是不一样的。char*声明的是字符串常量,存储在只读数据段。char a[],存放在栈或读写数据段。

PS:
动态存储方式:在程序运行期间根据需要进行动态的分配存储空间的方式。动态存储变量是在程序执行过程中,使用它时才分配存储单元, 使用完毕立即释放。主要是堆、栈存储。

静态存储方式:在程序编译时分配固定的存储空间的方式。像全局变量、静态变量等都是静态存储。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值