当前位置: 首页 > 编程日记 > 正文

C语言的变量的内存分配

今晚看了人家写的一个关于C语言内存分配的帖子,发现真是自己想找的,于是乎就收藏了。。。
先看一下两段代码:

char* toStr() 
{char *s = "abcdefghijkl";return s;
}
int main()
{cout << toStr() << endl;return 0;
}

char* toStr() 
{char s[] = "abcdefghijkl";return s;
}
int main()
{cout << toStr() << endl;return 0;
}


前一段代码打印出来是字符串,而后一段代码打印出来就是乱码。记得学C语言的时候讲到,字符串是被当做字符数组来处理的。所以字符数组名就相当于指向首地址的指针。那么
1. char *s = "abcdefghijkl";
2. char s[] = "abcdefghijkl";
这两种表达式似乎是一样的,可是为什么程序结果会不一样呢?原因就是没有对内存分配了解好。当然现在的C语言教材不会讲到的。
解释:
程序的意思比较简单,不用解释。
第一种表达式,指针s是局部变量,他的作用域是函数toStr内。它将其指向的地址返回,返回之后s即被销毁,庆幸s指向的地址被返回了回来。最终打印正确。
第二种表达式,那么我们会问第二种与第一种的区别在哪,为何错?原因就是第一种指针s虽然是局部变量,被分配在栈空间,作用域是函数内部,但其指向的内容"abcdefghijkl"是常量,被分配在程序的常量区。直到整个程序结束才被销毁。而第二种,s是一数组,分配到栈空间,"abcdefghijkl"作为数组各个元素被放到数组中,一旦函数退出,栈中这块内存就被释放。虽然返回一个地址,可是已经失去它的意义了。

通过以上例子,我们来学习学习内存分配的问题吧。

首先,需要搞清楚:变量的类型和它的存储类别是两个概念。
数据类型和内存管理没有直接的关系。

一、一个由C/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。  程序结束后由系统释放。
4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

二、例子程序
这是一个前辈写的,非常详细

//main.cpp	
int a = 0; //全局初始化区
char *p1; //全局未初始化区main()
{int b; //栈char s[] = "abc"; //栈char *p2; //栈char *p3 = "123456"; //123456\\0在常量区,p3在栈上。static int c =0;//全局(静态)初始化区p1 = (char *)malloc(10);	p2 = (char *)malloc(20);//分配得来得10和20字节的区域就在堆区。strcpy(p1, "123456"); //123456\\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。		
} 


这下就对程序的内存分配理解更深入了吧。

其实包括其他编程语言,Java等,他们都有所谓的栈空间和堆空间以及常量区,我们经常写完程序之后发现莫名的错误,或者内存被慢慢吞噬,这都是这方面的原因。

以下是堆和栈的理论知识 
2.1申请方式 
stack: 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 
heap: 需要程序员自己申请,并指明大小,在c中malloc函数 
如p1 = (char *)malloc(10); 
在C++中用new运算符 
如p2 = (char *)malloc(10); 
但是注意p1、p2本身是在栈中的。 
 
2.2 申请后系统的响应 
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 
      会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 
 
2.3申请大小的限制 
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS 下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 
所以在程序中自动变量数组(函数内部)不能很大,因为栈(这就是我们通常说的程序的堆栈段,大数组发生段溢出)的大小有限,而可以申请为全局变量,因为那是分配在静态区,大小不受限制。
 
2.4申请效率的比较: 
栈由系统自动分配,速度较快。但程序员是无法控制的。 
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便. 
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活 
 
2.5堆和栈中的存储内容 
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。 
 
2.6存取效率的比较
char s1[] = "aaaaaaaaaaaaaaa"; 
char *s2 = "bbbbbbbbbbbbbbbbb"; 
aaaaaaaaaaa是在运行时刻赋值的; 
而bbbbbbbbbbb是在编译时就确定的; 
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 
比如: 
#include <stdio.h> 
void main() 
char a = 1; 
char c[] = "1234567890"; 
char *p ="1234567890"; 
a = c[1]; 
a = p[1]; 
return; 
对应的汇编代码 
10: a = c[1]; 
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 
0040106A 88 4D FC mov byte ptr [ebp-4],cl 
11: a = p[1]; 
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 
00401070 8A 42 01 mov al,byte ptr [edx+1] 
00401073 88 45 FC mov byte ptr [ebp-4],al 
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。 

转载于:https://www.cnblogs.com/jiangu66/p/3217719.html

相关文章:

java打包_java工程打包(方式一)

1、新建工程myprojectpackage cn.zj;public class TestMain {public static void main(String[] args) {System.out.println("Hello World!");}}2、右击Java工程选择Export—>选择JAR file—>Next3、选择要打包的文件&#xff0c;不需要的文件不必打包&#xf…

s32v 开发板安装 apex 驱动

安装驱动 首先要获取到 apex.ko 文件&#xff0c;这个应该是硬件厂商提供&#xff0c;执行下面命令安装 首先需要给 apex.so 文件加上可执行权限 insmod apex.ko查看是否安装成功 执行下面命令&#xff0c;查看是否安装成功&#xff0c;如果安装成功&#xff0c;执行的结果中…

android常见错误与问题

1. Unable to start activity ComponentInfo 原因有很多种&#xff0c;我碰到一种&#xff1a; 可能是当前Activity里引用的View&#xff0c;并不存在于绑定的Layout里&#xff0c;而是在其他的Layout里&#xff0c;在Copy代码的时候&#xff0c;常会发生这种问题。

链式前向星(模板)

一种非常厉害的存图的数据结构&#xff01; 本质&#xff1a;模拟链表的操作&#xff0c;链式存储图。&#xff08;2&#xff0c;3都可以模拟链表的操作&#xff0c;替代链表&#xff09; &#xff08;1&#xff09;二维数组存图&#xff1a;Map[x][y]&#xff0c;一维代表出发…

tar 打包问题

项目中使用到 tar 文件&#xff0c;同一个 tar 文件解压之后在压缩&#xff0c;在程序执行的时候不能使用了 原因是 tar 对文件名长度有限制&#xff0c;当文件名过程的时候&#xff0c;使用 --formatustar 进行压缩

QT webkit学习笔记(2)

五、QWebDataBase Class介绍 QWebDataBase提供了对基于JavaScript创建的HTML 5数据库。新一代的HTML 5标准也提供对基于javaScript SQL数据库访问的支持。QWebDataBase就是这些数据库的C接口。关于HTML 5的详情&#xff0c;可以参见HTML 5 Draft Standard. 六、QWebHistory Cla…

java 数组越界异常_数组越界异常 求解决!!!

源自&#xff1a;4-3 滚动状态判断与处理数组越界异常 求解决&#xff01;&#xff01;&#xff01;package com.example.imooc;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.ne…

WPF外包公司—北京动点软件WPF最新的电子书整理打包下载

最近看到很多朋友寻找以前的WPF电子书&#xff0c;其实这些书在书店目前是很难买到了&#xff0c;不过还是很经典的&#xff0c;希望大家收藏~ WPF揭秘 http://download.csdn.net/detail/ping_vip/3935100 WPF经典教程 http://kiccp.sinaapp.com/store/info/83 WPF程序设计指南…

java后端判断用户是否关注公众号

/*** 判断用户是否关注了公众号* param openid* return*/ public static boolean judgeIsFollow(String openid){int subscribe 0; // String url "https://api.weixin.qq.com/cgi-bin/user/info?access_token"token"&openid"openid"&a…

QtCreator动态编译jsoncpp完美支持x86和arm平台

如果是做嵌入式开发。 在Qt下支持JSon最好的办法&#xff0c;可能不是采用qjson这个库。QJson这个库的实例只提供了x86环境下的编译方法。 Installing QJson-------------- QJson requires:- Qt 4.0 or greater- cmake 2.6 or greater For Unix/Linux/Mac: mkdir build cd b…

RADStudio连接MySQL_使用FireDac(Delphi)在Firebird中创建数据库

我最近从AnyDac改为FireDac(8.0.5.3365).我们正在运行Delphi 2006.当我使用此组件的AnyDac版本时,我可以通过执行以下操作来创建新数据库.设置我的连接fConnection.LoginPrompt : false;fConnection.ResourceOptions.SilentMode : true;fConnection.Params.Clear;fConnection.P…

valgrind 使用 kcachegrind 查看函数运行时间

安装 首先安装运行分析函数时间的工具 kcachegrind 下载安装包 http://kcachegrind.sourceforge.net/&#xff0c;下载最新的 tar.gz 文件 解压文件&#xff0c;进入解压之后的目录&#xff0c;从 README 中可以找到安装方式&#xff0c;这里记录一下 cmake . make -j8 sudo …

Red Hat Enteripse Linux5下配置yum源的方法

1。确保RHEL5中已经安装了yum。2。修改源配置文件 &#xff03;gedit /etc/yum.repos.d/CentOS-Base.repo在其中加入以下内容[base]nameCentOS-5-Base#mirrorlisthttp://mirrorlist.centos.org/?release$releasever5&arch$basearch&repoos#baseurlhttp://mirror.cento…

echarts如何修改散点大小

由于工作需要 懵懵懂懂接触到了echarts 很牛逼 遇到的问题是如何修改散点图中点点的大小&#xff0c;其他的图我没有涉及哦~ demo地址 http://www.echartsjs.com/examples/editor.html?cscatter-single-axis 找到左侧代码 1 symbolSize: function (dataItem) { 2 3 return da…

字节跳动java笔试题目_牛客网--字节跳动面试题--特征提取

牛客网--字节跳动面试题--特征提取博客说明文章所涉及的资料来自互联网整理和个人总结&#xff0c;意在于个人学习和经验汇总&#xff0c;如有什么地方侵权&#xff0c;请联系本人删除&#xff0c;谢谢&#xff01;来源链接&#xff1a;特征提取 来源&#xff1a;牛客网题目小明…

基于php下载文件的详解

基于php下载文件的详解 本篇文章是对php下载文件进行了详细的分析介绍&#xff0c;需要的朋友参考下php下载文件&#xff0c;比如txt文件。出现的效果就是&#xff0c;弹出浏览器自带的下载框&#xff0c;出现另存为操作。有时候会出现内存溢出和超时的现象。超时的话&#xff…

C#中DateTime.Now.Ticks的用法和说明

在C#中DateTime.Now.Ticks的常用于标示&#xff1a; 自 0001 年 1 月 1 日午夜 12:00:00以来&#xff0c;到当前时间为止&#xff1a;以0.1纳秒(1纳秒0.00000 0001秒)为单位的时间间隔数。用于非常精确的计算中使用。转载于:https://www.cnblogs.com/woaic/archive/2012/09/13/…

spring入门(二) 使用注解代替xml配置

1.导包(略) 2.applicationContext.xml如下: 1 <?xml version"1.0" encoding"UTF-8"?>2 <beans xmlns"http://www.springframework.org/schema/beans"3 xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"4 …

qt 找不到 -lpulse-mainloop-glib,找不到 -lpulse问题

问题&#xff1a;使用 QT 编写视频展现程序报错找不到运行时库文件 解决办法&#xff1a; 首先 sudo find / -name libpulse.so* 然后 sudo cp /usr/lib/x86_64-linux-gnu/libpulse.so.0 /usr/lib/libpulse.so 对于libpulse-mainloop-glib 首先find / -name libpulse-mainl…

INNO SETUP 获得命令行参数

INNO SETUP 获得命令行参数 原文 http://www.cnblogs.com/ahuo/archive/2009/07/30/1534998.html [Code]function GetMyParam(PName:String):String;var CmdLine :String; CmdLineLen :Integer; i :Integer;begin CmdLineLen:ParamCount(); fori:0to CmdLineLen dobeg…

java父子表_数据库二维表转父子关系,java,stream,list

需求描述&#xff1a;把数据库中的省市二维表&#xff0c;查询到内存中后&#xff0c;转换为父子层级关系。通过jdk8中的stream方式实现。数据关系&#xff1a;320004 福建省 320507 南平市430000 湖南省 430100 长沙市320000 江苏省 320583 昆山市…

java 概述

概述&#xff1a; 1.在java中&#xff0c;数据类型具有固定的大小&#xff0c;这消除了代码移植时令人头痛的主要问题。2.在网页中运行java程序成为applet3.public成为访问修饰符&#xff0c;它用于控制程序的其它部分对这段代码的访问级别。4.单引号的数据是char类型&#xff…

HOOK 技术

在介绍 截获系统消息钩子 之前&#xff0c;这几个函数是密切相关的&#xff1a; SetWindowsHookEx() 介绍&#xff1a; 功能&#xff1a;将应用程序定义的挂钩过程安装到挂钩链中。 函数原型&#xff1a;HHOOK SetWindowsHookEx( int idHook, // 钩子类型。…

QT 中使用 OpenCv 的 CascadeClassifier 报错

问题 在 QT 中调用 OpenCv 的 CascadeClassifier 进行人脸框检测的时候&#xff0c;在构造函数中进行检测器的初始化&#xff0c;随后调用相机读取图片的时候就会报错&#xff0c;报的错误是 Segment Fault &#xff08;段错误&#xff09; 解决 尝试使用 gdb&#xff0c;va…

java vuser脚本_loadrunner12中JavaVuser脚本的编写

1、环境准备&#xff1a;友情提示&#xff1a;用本地环境&#xff0c;不要用虚拟机LoadRunner11----->对应JDK1.6版本(32位)LoadRunner12----->对应JDK1.7版本(32位)(一)、JDK下载安装完成后&#xff0c;配置环境变量&#xff1a;1)、系统变量→新建 JAVA_HOME 变量 &…

IT阅读——关于“业务”

本文转自http://www.cnblogs.com/beijiguangyong/archive/2012/11/12/2767054.html 开发当中常常听说“业务”这个词&#xff0c;什么“业务为王”之类的词不绝于耳&#xff0c;那么什么是业务&#xff1f; 百度上的解释是&#xff1a;“‘业务’更白话一些来说&#xff0c;就是…

SSH无需密码密钥登录

2019独角兽企业重金招聘Python工程师标准>>> 无密码ssh登录的主要操作简单概述为&#xff0c;将本机中的ssh密钥对中的公钥如id_rsa.pub拷贝到目标机器的ssh验证文件authorized_keys中。 1、简洁操作步骤 摘录一 &#xff1a;使用ssh-copy-id 在192.168.42.142机器…

5-4 图片修补

import cv2 import numpy as np img cv2.imread(image0.jpg,1) for i in range(200,300): # 直接修改像素值 从200画到300这样一个位置上img[i,200] (255,255,255)#当前这样一根线占三个像素img[i,2001] (255,255,255)img[i,200-1] (255,255,255) for i in range(150,250):…

OpenCV 像素存储

像素存储 OpenCV 中图像矩阵的大小取决于所用的颜色模型&#xff0c;更准确的说是取决于图像所用到的通道数。 如果使用的是灰度图&#xff0c;矩阵大概如图所示&#xff1a; 如果使用的是多通道的图像&#xff0c;矩阵中的列会包含多个子列&#xff0c;子列的个数和通道数相…

java反射用在哪里_Java反射

昨天去参加比赛了&#xff0c;所以没有进行博客迁移。人生中的第一场健体比赛&#xff0c;虽然没得奖&#xff0c;但是收获和带来的思考颇丰。意外地进入了男子B组(174以上)的半决赛&#xff0c;然后在半决赛的时候还被裁判员点名出去单独比较&#xff0c;这个很让我惊喜。最后…