从字符串常量起说内存分配

char p[];char *p;char *p=new char[];

#include <iostream>
using namespace std;
const int n=20;
int main()
{
    char p[]="hello world";      //correct

    //char *p2="hello world";
    //char *p=new char[n];        //correct
    //strcpy(p,p2);

    //char *p="hello world";      //error

    //char *p=new char[n];        //error
    //p="hello world";

    int i=0,j=strlen(p)-1;
    while (i<j)
    {
        char temp=p[i];
        p[i]=p[j];
        p[j]=temp;
        i++;
        j--;
    }
    cout<<p;

}

c语言的字符串常量赋值是很多人犯的一个错误,上述程序是实现字符串的翻转,4种情况下,标明correct的是能够正确运行,标明error的是发生写入内存错误的。对此一一分析,

1.char p[]="hello world";//correct 定义的是一个字符串数组,里面存的内容是hello world\0,可以对这一数组进行更改。它是在栈上分配内存的。

2.char *p="hello world"; //error 定义的p这个指针指向字符串常量,字符串常量是位于静态存储区的,其内容是不能改变的。

3.char *p=new char[n];p="hello world"; //error 在堆动态分配n个字符的内存,返回的是指向这一内存的指针p。但第二行的功能却是把这一指针指向了位于静态存储区的字符串常量。而不是把字符串常量复制到p所指的堆区。所以发生错误。

4.char *p2="hello world"; char *p=new char[n]; strcpy(p,p2); //correct :定义的是p2这个指针指向字符串常量,字符串常量是位于静态存储区的,其内容是不能改变的,同时在堆动态分配n个字符的内存,返回的是指向这一内存的指针p。把p2的内容复制到p所指的堆区。这时可以对堆区的数据进行更改。

总结一下内存的分配方式

(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

更加深入的分析,来源:http://blog.csdn.net/yahohi/article/details/7427724

一、预备知识—程序的内存分配

一个由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"优化成一个

地方。

}

二、堆和栈的理论知识

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分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.

2.5  堆和栈中的存储内容

栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。

时间: 2024-10-28 21:46:53

从字符串常量起说内存分配的相关文章

string字符串常量池在内存中的位置

这里仅仅是举个简单的样例说明字符串常量池在内存中的位置. 闲言少叙,直接上代码. Java代码   <span style="font-size: large;">import java.util.ArrayList; public class Test { public static void main(String[] args) { String str = "abc"; char[] array = {'a', 'b', 'c'}; String

4-数组、指针与字符串1.4-动态内存分配

这种在程序运行过程中申请和释放的存储单元也称为堆对象,申请和释放过程过程一般称为建立和删除. 1.new运算和delete运算 运算符new的功能是动态分配内存,或者称为动态创建堆对象,语法形式为: new 类型名T(初值列表)://用于申请分配存放T类型数据的内存空间,并使用初值列表中给出的值进行初始化. 如果建立的对象是一个基本类型变量,初始化过程就是赋值,如: int *point; point =new int(2); 动态分配了用于存放int类型数据的内存空间,并将初值2存入该空间中,

[C] 语言字符串、文件及内存分配函数

一.字符串函数 1.gets() 头文件:#include <stdio.h> 函数原型:char *gets(char *string); 函数说明:从标准输入流(stdin)中读取整行,直至遇到换行符结束,然后丢弃换行符,储存其余字符,并在末尾加上空字符,表示一个字符串: 函数返回值:若成功则返回指向string的指针,否则返回NULL: 备注:与puts()函数配套使用:只知道string的开始出,不知道数组中有多少个元素,超出边界,容易造成缓冲区溢出. 2.fgets() 头文件:in

java内存分配

关于Java内存分配,很多问题都模模糊糊,不能全面贯通理解.今查阅资料,欲求深入挖掘,彻底理清java内存分配脉络,只因水平有限,没达到预期效果,仅以此文对所研究到之处作以记录,为以后学习提供参考,避免重头再来. 一.Java内存分配1. Java有几种存储区域?* 寄存器     -- 在CPU内部,开发人员不能通过代码来控制寄存器的分配,由编译器来管理* 栈     -- 在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域,即栈顶的地址和栈的最大容量是系统预先规定好的.

C++常量(C++数值常量、字符串常量、符号常量)

http://see.xidian.edu.cn/cpp/biancheng/view/104.html 字符串常量 用双撇号括起来的部分就是字符串常量,如"abc","Hello!","a+b","Li ping"都是字符串常量.字符串常量"abc"在内存中占4个字节(而不是3个字节),见图2.5. 图 2.5 编译系统会在字符串最后自动加一个'\0'作为字符串结束标志.但'\0'并不是字符串的一部分,它

字符串常量与字符串数组区别

在论坛上看到过有人说字符串常量存储在只读区域,不能写只能读: 而字符数组是栈上,可读可写. #include<stdio.h> #include<stdarg.h> int main(){ /*字符数组存储于动态内存中,可以进行赋值操作*/ char message[]={'h','e','l','l','\0'}; message[2]='a'; printf("%s\n",message); /*指向字符数组的指针也可以对内存进行操作*/ char *p=m

Java内存分配之堆、栈和常量池(转)

摘录自http://www.cnblogs.com/SaraMoring/p/5687466.html Java内存分配主要包括以下几个区域: 1. 寄存器:我们在程序中无法控制 2. 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 3. 堆:存放用new产生的数据 4. 静态域:存放在对象中用static定义的静态成员 5. 常量池:存放常量 6. 非RAM(随机存取存储器)存储:硬盘等永久存储空间 *********************************

C-数组, 字符串的输入输出, 内存分配, 三种内存分配函数

数组初始化 1.数组初始化的时候, 可以这样 1 int len = 3; 2 int arr[len]; 2.但是这样不可以: 1 int len = 3; 2 int arr[len] = {1, 2, 3}; 3.但是可以这样: 1 int arr[3] = {1, 2, 3}; 2不可以的原因: 编译器编译的时候 int arr[3] = {1, 2, 3}这种方式会转换成: 1 int arr[3]; 2 arr[0] = 1; 3 arr[1] = 2; 4 arr[2] = 3;

指针和字符串和字符串常量、用gdb来获取非法内存中的信息

例程1 #include<stdio.h> int main(void) { char *s="hello"; printf("%s\n", s); s[0]="H" //因为s指针指向的字符串"hello"是字符串常量,所以不能通过指针进行更改,所以这里会产生段错误 printf("%s\n", s); return 0; } 例程2 #include<stdio.h> #incl