`

C语言内存管理-字符数组与字符指针

 
阅读更多

http://blog.csdn.net/yimu13/article/details/6418485

 

堆和栈的区别

一个由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分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。
2.5堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
2.6存取效率的比较
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#i nclude
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读取字符,显然慢了。
2.7小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
----------------------------------------------------------------------------------------------------------------------
全局变量或者静态变量,它们都放在堆里的
局部变量放在栈里的
堆区,也叫自由存储区.

为什么说在堆上分配内存比在栈上分配内存慢?堆空间的开辟需要用系统函数,栈上直接修改指针
堆空间的管理需要系统记帐,栈上的空间可以由编译器管理或是保存在某个处理器寄存器中。
堆空间的释放需要系统管理,栈上的释放可以直接丢弃。堆空间需要通过栈上的指针间接引用,所以访问会慢

记得在apue2上面看到关于线程中有这样一段话,大致意思是,一个线程有自己的堆栈,可以在堆栈上 分配内存,比如说一个结构体,如果这个线程调用了pthread_exit()返回这个结构体指针的时候之后要特别的小心,因为很有可能这个结构体里面的 成员值发生改变,这个可以理解,因为同一个进程所有线程的资源是共享的,当这个线程退出之后那部分以前用过的堆栈很可能被其它线程占用,但同时又说如果 malloc就不会出现这样的问题,

比如,在栈上分一个int,只要esp-4就可以了,
在堆上系统要记录被分配内存的信息,以便释放
BTW:
栈有序
堆无序

----------------------------------

内存分配方式有三种:   
    
   1.从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。   
    
   2.在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。   
    
   3.从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

----------------------------------------

一般所说的堆栈(stack)往往是指栈,先进后出, 它是一块内存区。用以存放程序的局部变量,临时变量,函数的参数,返回地址等。在这块区域中的变量的分配和释放由系统自动进行。不需要用户的参与。   
   而在堆(heap,先进先出) 上的空间则是由用户进行分配,并由用户负责释放。

===================================================================================

字符数组,字符指针,Sizeof总结
1.以字符串形式出现的,编译器都会为该字符串自动添加一个0作为结束符,如在代码中写 "abc",那么编译器帮你存储的是"abc/0"
2."abc"是常量吗?答案是有时是,有时不是。
不是常量的情况:"abc"作为字符数组初始值的时候就不是,如
     char str[] = "abc";
因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为
    char str[3] = {'a','b','c'};
又根据上面的总结1,所以 char str[] = "abc";的最终结果是 char str[4] = {'a','b','c','/0'};做一下扩展,如果char str[] = "abc";是在函数内部写的话,那么这里 的"abc/0"因为不是常量,所以应该被放在栈上。
是常量的情况: 把"abc"赋给一个字符指针变量时,如
     char* ptr = "abc";
   因为定义的是一个普通指针,并没有定义空间来存放"abc",所以编译器得帮我们找地方来放"abc",显然,把这里的"abc"当成常量并把它放到程序 的常量区是编译器 最合适的选择。所以尽管ptr的类型不是const char*,并且ptr[0] = 'x';也能编译 通过,但是执行ptr[0] = 'x';就会发生运行时异常,因为这个语句试图去修改程序 常量区中的东西。
记得哪本书中曾经说过char* ptr = "abc";这种写法原来在c++标准中是不允许的, 但是因为这种写法在c中实在是太多了,为了兼容c,不允许也得允许。虽然允许, 但是建议的写法应该是const char* ptr = "abc";这样如果后面写ptr[0] = 'x'的话编译器就不会让它编译通过,也就避免了上面说的运行时异常。
    又扩展一下,如果char* ptr = "abc";写在函数体内,那么虽然这里的"abc/0"被放在常量区中,但是ptr本身只是一个普通的指针变量,所以ptr是被放在栈上的, 只不过是它所指向的东西被放在常量区罢了。
3.数组的类型是由该数组所存放的东西的类型以及数组本身的大小决定的。 如char s1[3]和char s2[4],s1的类型就是char[3],s2的类型就是char[4], 也就是说尽管s1和s2都是字符数组,但两者的类型却是不同的。
4.字符串常量的类型可以理解为相应字符常量数组的类型, 如"abcdef"的类型就可以看成是const char[7]
5.sizeof是用来求类型的字节数的。如int a;那么无论sizeof(int)或者是sizeof(a)都 是等于4,因为sizeof(a)其实就是sizeof(type of a)
6.对于函数参数列表中的以数组类型书写的形式参数,编译器把其解释为普通 的指针类型,如对于void func(char sa[100],int ia[20],char *p) 则sa的类型为char*,ia的类型为int*,p的类型为char*
7.根据上面的总结,来实战一下:
     对于char str[] = "abcdef";就有sizeof(str) == 7,因为str的类型是char[7], 也有sizeof("abcdef") == 7,因为"abcdef"的类型是const char[7]。
      对于char *ptr = "abcdef";就有sizeof(ptr) == 4,因为ptr的类型是char*。
      对于char str2[10] = "abcdef";就有sizeof(str2) == 10,因为str2的类型是char[10]。
      对于void func(char sa[100],int ia[20],char *p); 就有sizeof(sa) == sizeof(ia) == sizeof(p) == 4, 因为sa的类型是char*,ia的类型是int*,p的类型是char*。
分享到:
评论

相关推荐

    C语言用指针将字符串数组位置颠倒

    C语言用指针将字符串数组位置颠倒,实现ABCDEFGHIJKLMNOPQRSTUVWXYZ转换成ZYXWVUTSRQPONNOPQRSTUVWXYZ

    单片机C语言实例--301-用指针数组作为函数的参数显示多个字符串.zip

    本实例"301-用指针数组作为函数的参数显示多个字符串"旨在教授如何利用指针数组在单片机环境中传递和处理字符串。在深入探讨这个实例之前,我们先来理解几个关键概念。 **指针**: 在C语言中,指针是变量的地址,...

    C语言中关于字符数组与字符指针

    本文将详细讲解字符数组与字符指针在C语言中的区别、用法,以及它们在实际编程中的应用场景。 首先,需要明确的是字符数组和字符指针在内存中存储的形式不同。字符数组在内存中是连续分配的,能够存储一系列字符,...

    单片机C语言实例-用指针数组作为函数的参数显示多个字符串.zip

    这个实例“单片机C语言实例-用指针数组作为函数的参数显示多个字符串”着重讲解了如何利用指针数组作为函数参数来实现字符串的动态显示。这种技术在单片机应用中十分常见,特别是在处理多条信息的显示时,能够提高...

    嵌入式系统开发人员C语言测试题-数组 指针 字符串.doc

    在嵌入式系统开发中,C语言是常用的语言之一,特别是在处理数组、指针和字符串时。本测试题集专注于这些关键概念,帮助开发者检验和巩固C语言的基础知识。 7.1 选择题中,第456题指出在C语言中引用数组元素时,数组...

    C语言中二维字符数组.pdf

    在动态内存分配中,二维字符数组可以使用new char*[128]来分配内存,但是需要注意内存的释放,以避免内存泄露。 二维字符数组是一种常见的技术,广泛应用于各种领域,了解其定义、初始化和使用方法对于C语言程序员...

    字符数组字符指针sizeofstrlen的一点总结

    ### 字符数组与字符指针详解 #### 一、字符数组与字符指针的基本概念 在C语言中,字符数组和字符指针是处理文本数据的两种常见方式。字符数组通常用于存储固定长度的字符串,而字符指针则用于指向字符串的首地址。...

    301-用指针数组作为函数的参数显示多个字符串(51单片机C语言实例Proteus仿真和代码)

    301-用指针数组作为函数的参数显示多个字符串(51单片机C语言实例Proteus仿真和代码)301-用指针数组作为函数的参数显示多个字符串(51单片机C语言实例Proteus仿真和代码)301-用指针数组作为函数的参数显示多个字符串...

    C语言教程---指针

    字符串常量的地址可以赋值给字符指针,如`char *str = "Hello"`。通过指针遍历字符串,可以实现字符串的操作。 10.5 指向函数的指针 函数也可以被视为对象,有其地址。指向函数的指针可以存储函数的地址,使得我们...

    第8章 指针-3字符数组和字符指针 - 字符串的输入和输出1

    在C语言中,字符数组和字符指针是处理字符串(以'\0'为结束符的字符序列)的重要工具。本章主要讨论了如何通过指针进行字符串的输入和输出。 首先,字符数组用于存储字符串。例如,定义了一个大小为81(包括一个...

    C语言程序设计-指针与字符串.pptx

    字符指针(char *)是一种特殊的指针类型,它专门用来存储字符类型的内存地址。例如,`char *pc` 定义了一个指向字符的指针变量pc。要给这个指针赋值,我们可以使用取址运算符`&`,如 `char c; pc = &c;` 这将使pc...

    C语言字符数组与字符串的使用详解[归类].pdf

    C语言字符数组与字符串的使用详解 C语言中,字符数组和字符串是两个紧密相关的概念。字符数组是指由多个字符组成的数组,而字符串则是指一个以`\0`结尾的字符序列。在C语言中,字符串可以看作是一个特殊的字符数组...

    C语言中二维字符数组[文].pdf

    ` 创建了一个指向字符指针的指针,然后可以逐个元素分配内存,如`str[i] = new char[ strlen(string) + 1 ];`。这种方式更灵活,但需要谨慎管理内存,避免内存泄漏。 3. 字符串分布: - 在C语言中,字符串是以空...

    C语言中的字符串数组和字符串指针

    本文档在某知名网站下载,要么浏览量最多,要么最新,保证精品,至少为你节约3RMB。格式为caj或者kdh需要专业阅读器打开。

    C语言第讲指针与字符串字符数组PPT课件.pptx

    1. **字符指针与字符数组的区别** - **存储格式**:字符指针(变量)存储的是字符串的首地址,即一个内存地址(通常为2字节)。而字符数组则是存储整个字符串,每个数组元素存储一个字符。 - **性质差异**:字符...

    C语言中的字符串数组和字符串指针.pdf

    在C语言中,字符串数组和字符串指针是处理字符串的两种常用方法,它们在定义、引用、数据空间、存储单元分配和赋值方面各有特点和区别。 首先,字符串数组是由多个字符型元素构成的集合,它在内存中占据连续的存储...

    字符数组与字符指针

    在C语言中,处理文本数据时,经常需要使用到字符数组与字符指针。本文将详细介绍这两者的基本概念、区别及其用法,并通过具体实例帮助读者更好地理解和掌握。 #### 二、基本概念 ##### 1. 字符数组 字符数组是一种...

    C语言程序设计实验答案数组指针与字符串.doc

    本文档提供了C语言程序设计实验答案数组指针与字符串的知识点总结,涵盖了数组、指针、字符串处理、动态内存分配、类的声明和使用等多个方面。 数组 * 数组是C语言中的一种数据结构,用于存储一组相同类型的元素。...

    程序设计-指针数组.pptx

    由于C语言中字符串的处理是通过字符指针完成的,因此利用指针数组可以非常方便地操作多个字符串。使用指针数组,我们可以轻松实现对字符串的排序、搜索等操作。在使用如选择排序这类算法时,我们并不是真正移动字符...

Global site tag (gtag.js) - Google Analytics