c语言问题+答案
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.double free是什么问题?申请地址与释放地址不一致会有什么问题?
2.main函数最多有几个参数?各是什么作用?
标准C规定是两个参数,但是大多数的unix系统提供了三个参数,第三个参数用来向调用main函数的例程(一般的是一个shell例程exec)传递环境变量表。
在程序设计中,如果没有指定第三个参数,可以用getenv等函数存取或修改部分环境表中的变量。
一般的在系统头文件中已经将环境表神明为如下,因此在程序中使用不会提示没有引用的错误信息。
extern char **_environ;
3.crt是什么?编译器是怎么样连接crt的(描述cl或者gcc方式)
Crt是c语言的运行库,c runtime的缩写
4.c语言程序的入口函数是哪个?(绝对不是main)
程序最终生成的exe执行时,开始执行的是mainCRTStartup(或wmainCRTStartup......以及其他)函数之一,而不是我们程序所写的main或WinMain等。
连接器为什么要这样做?因为我们写的程序必须要使用到各种各样的运行时库函数才能正常工作,所有在执行我们自己写程序之前必须要先准备好所需要的一切库,之所以要连接它们是因为他们肩负着很重要的使命,就是初始化好运行时库,准备我们的程序执行时调用。
我们可以使用连接器的链接选择来设置我们的函数入口点,但是最好不要这样做,如果我们重新设置入口点函数,则必须要在入口点函数中自己写上有关的初始化工作,所以最好用默认的入口点函数。
修改入口点方法:proerties->Linker->Advanced->EntryPoint
如果函数与连接器的SubSystem的属性要一致的:
proerties->Linker->System->SubSystem
如果未指定/DLL 或/SUBSYSTEM 选项,则链接器将根据是否定义了main 或WinMain 来选择子系统和入口点。
函数main、WinMain 和DllMain 是三种用户定义的入口点形式。
通过上面的分析知道,在微软系统中原来操作系统中的加载器与连接器之间是有协议的,要不然在加载运行程序时不可能成功的,比如你将windows程序放到apple系统上运行,就会无法运行,因为apple的加载程序根本不知道加载windows的exe的协议。
5.请描述不使用main函数的情况先怎样设置程序入口函数?(描述cl或gcc中的一种)
入口地址不是main,而是在crt0.o中的一段代码,它会默认在执行了初始后调用main,所以gcc是改不了入口的。
最多可以让某个函数在main之前执行而已
6.C++调用C函数时为什么要将C函数包含在extern “C”中?ABI是什么?
7.赋值运算符与位与运算符哪个优先级高?
位与优先值
8.offset是怎样实现的?
在linux/include中,offset如下定义:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
第一个参数是结构体的名字, 第二个参数是结构体成员的名字,返回的是该成员在结构体中的偏移量.
先看如下代码:
struct TYPE
{
int member1;
char member2;
}
TYPE* p = NULL;
TYPE* p = malloc(sizeof(TYPE));
(&TYPE->member2)这个表示的便是TYPE结构中成员member2的内存地址,注意前面的是&取址符.
那么,欲求得成员相对于结构的偏移量,我们只需要将(&TYPE->member2)减去TYPE* p的内存地址即可获得.
这个宏的实现巧妙之处在于,将p的内存地址强制赋为0, 即是(TYPE*)0这里.那么返回的(TYPE*)0->MEMBER即是成员的偏移量了.
9.float型的数据怎么判断它是否等于0?
这个好像与float类型的精度有关,一般float型只能精确到小数到后六位即1e-6,将float 型的数a的绝对值abs(a)与1e-6比较,如果abs(a)比1e-6还要小的话就可以认为a 的值为零,因为小数六位以后是不精确的,是没有意义的。
比如数0.0000001虽然确实不等于零,但是第七位小数1是没有意义的就可以认为这个数等于0。
10.怎样声明函数可以写以下代码
fun()[0]();
11.如果有两个结构体要相互引用指针,应该怎么定义这两个结构体?
typedef struct A AAA;
typedef struct B BBB;先声明,才能互相引用
struct A
{
BBB *p;
int a;
int b;
};
struct B
{
AAA *p;
int aa;
int bb;
};
12.比较两个值是否相等时为什么要把常量写在前面?
防止==少一个的时候,不注意,写在前面=号少用一个会报错
13.请写出两种防止头文件重复引用的方法?
#progma once
在头文件(比如myhead.h)中加一句
#define MYHEAD
引用的时候写成
#ifndef MYHEAD
#include "myhead.h"
#endif
在.c文件中声明变量,然后建一个头文件(.h文件)在所有的变量声明前加上extern,注意这里不要对变量进行的初始化。
然后在其他需要使用全局变量的.c文件中包含.h文件。
编译
器会为.c生成目标文件,然后链接时,如果该.c文件使用了全局变量,链接器就会链接到此.c 文件。
test-2.0
# vi test.c
-------------------------------
#include <stdio.h>
#include "test.h"
int i = 10;
char add1[] = "char1";
char add2[] = "char2";
extern void test1();
extern void test2();
int main()
{
test1();
printf("ok/n");
test2();
printf("%d/n",i);
return 0;
}
# vi test.h
-------------------------------
#ifndef _TEST_H_
#define _TEST_H_
extern i;
extern char add1[];
extern char add2[];
void test1();
void test2();
#endif
# vi test1.c
-------------------------------
#include <stdio.h>
#include "test.h"
void test1()
{
printf(add1);
}
# vi test2.c
-------------------------------
#include <stdio.h>
#include "test.h"
void test2()
{
printf(add2);
for (; i > 0; i--)
printf("%d-", i);
}
14.extren时变量类型与定义是变量类型不一致会有什么问题?
例如:
file1.c char array[512] = { 0 };
file2.c extern char *array ;
15.以下程序的输出结果是:
int i = 0;
int j = sizeof(i++);
printf("i = %d j = %d\n", i, j);
0,4
16.请写一段可以反应volatile关键字作用的代码。
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。
如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
下面举例说明。
在DSP开发中,经常需要等待某个事件的触发,所以经常会写出这样的程序:
short flag;
void test()
{
do1();
while(flag==0);
do2();
}
这段程序等待内存变量flag的值变为1(怀疑此处是0,有点疑问,)之后才运行do2()。
变量flag的值由别的程序更改,这个程序可能是某个硬件中断服务程序。
例如:如果某个按钮按下的话,就会对DSP产生中断,在按键中断程序中修改flag为1,这样上面的程序就能够得以继续运行。
但是,编译器并不知道flag的值会被别的程序修改,因此在它进行优化的时候,可能会把flag的值先读入某个寄存器,然后等待那个寄存器变为1。
如果不幸进行了这样的优化,那么while循环就变成了死循环,因为寄存器的内容不可能被中断服务程序修改。
为了让程序每次都读取真正flag变量的值,就需要定义为如下形式:
volatile short flag;
需要注意的是,没有volatile也可能能正常运行,但是可能修改了编译器的优化级别之后就又不能正常运行了。
因此经常会出现debug版本正常,但是release版本却不能正常的问题。
所以为了安全起见,只要是等待别的程序修改某个变量的话,就加上volatile关键字。
volatile的本意是“易变的”
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的
优化。
比如:
static int i=0;
int main(void)
{
...
while (1)
{
if (i) do_something();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
程序的本意是希望ISR_2中断产生时,在main当中调用do_something函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致do_something永远也不会被调用。
如果变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。
此例中i也应该如此说明。
一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。
二、volatile 的含义
volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。
但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化,volatile的字面含义是易变的,它有下面的作用:
1 不会在两个操作之间把volatile变量缓存在寄存器中。
在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器自己无法知道,volatile就是告诉编译器这种情况。
2 不做常量合并、常量传播等优化,所以像下面的代码:
volatile int i = 1;
if (i > 0) ...
if的条件不会当作无条件真。
3 对volatile变量的读写不会被优化掉。
如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值操作,然而对Memory Mapped IO的处理是不能这样优化的。
前面有人说volatile可以保证对内存操作的原子性,这种说法不大准确,其一,x86需要LOCK前缀才能在SMP下保证原子性,其二,RISC根本不能对内存直接运算,要保证原子
性得用别的方法,如atomic_inc。
对于jiffies,它已经声明为volatile变量,我认为直接用jiffies++就可以了,没必要用那种复杂的形式,因为那样也不能保证原子性。
你可能不知道在Pentium及后续CPU中,下面两组指令
inc jiffies
;;
mov jiffies, %eax
inc %eax
mov %eax, jiffies
作用相同,但一条指令反而不如三条指令快。
三、编译器优化→ C关键字volatile → memory破坏描述符zz
“memory”比较特殊,可能是内嵌汇编中最难懂部分。
为解释清楚它,先介绍一下编译器的优化知识,再看C关键字volatile。
最后去看该描述符。
1、编译器优化介绍
内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,加速对内存的访问。
另外在现代CPU中指令的执行并不一定严格按照顺序执行,没有相关性的指令可以乱序执行,以充分利用CPU的指令流水线,提高执行速度。
以上是硬件级别的优化。
再看软件一级的优化:一种是在编写代码时由程序员优化,另一种是由编译器进行优化。
编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。
对常规内存进行优化的时候,这些优化是透明的,而且效率很好。
由编译器优化或者硬件重新排序引起的问题的解决办法是在从硬件(或者其他处理器)的角度看必须以特定顺序执行的操作之间设置内存屏障(memory barrier),linux 提供了一个宏解决编译器的执行顺序问题。
void Barrier(void)
这个函数通知编译器插入一个内存屏障,但对硬件无效,编译后的代码会把当前CPU 寄存器中的所有修改过的数值存入内存,需要这些数据的时候再重新从内存中读出。
2、C语言关键字volatile
C语言关键字volatile(注意它是用来修饰变量而不是上面介绍的__volatile__)表明某个变量的值可能在外部被改变,因此对这些变量的存取不能缓存到寄存器,每次使用时需要重新存取。
该关键字在多线程环境下经常使用,因为在编写多线程的程序时,同一个变量可能被多个线程修改,而程序通过该变量同步各个线程,例如:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterpret_cast<int*>(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
该线程启动时将intSignal 置为2,然后循环等待直到intSignal 为1 时退出。
显然intSignal的值必须在外部被改变,否则该线程不会退出。
但是实际运行的时候该线程却不会退出,即使在外部将它的值改为1,看一下对应的伪汇编代码就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
对于C编译器来说,它并不知道这个值会被其他线程修改。
自然就把它cache在寄存器里面。
记住,C 编译器是没有线程概念的!这时候就需要用到volatile。
volatile 的本意是指:这个值可能会在当前线程外部被改变。
也就是说,我们要在threadFunc中的intSignal 前面加上volatile关键字,这时候,编译器知道该变量的值会在外部改变,因此每次访问该变量时会重新读取,所作的循环变为如下面伪码所示:
label:
mov ax,signal
if(ax!=1)
goto label
3、Memory
有了上面的知识就不难理解Memory修改描述符了,Memory描述符告知GCC:1)不要将该段内嵌汇编指令与前面的指令重新排序;也就是在执行内嵌汇编代码之前,它前面的指令都执行完毕
2)不要将变量缓存到寄存器,因为这段代码可能会用到内存变量,而这些内存变量会以不可预知的方式发生改变,因此GCC插入必要的代码先将缓存到寄存器的变量值写回内存,如果后面又访问这些变量,需要重新访问内存。
如果汇编指令修改了内存,但是GCC 本身却察觉不到,因为在输出部分没有描述,此时就需要在修改描述部分增加“memory”,告诉GCC 内存已经被修改,GCC 得知这个信息后,就会在这段指令之前,插入必要的指令将前面因为优化Cache 到寄存器中的变量值先写回内存,如果以后又要使用这些变量再重新读取。
使用“volatile”也可以达到这个目的,但是我们在每个变量前增加该关键字,不如使用“memory”方便。
17.代码中还会对n进行加加吗?
fun()
{
int n = 0;。
return n++;
}
不会再加
18.请解释C语言中最重要的亮点:
符号的连接性与作用域;
全局变量具有外部链接性,静态全局变量具有内部链接性,静态局部变量没有链接性。
即在其他文件中可以引用当前文件定义的全局变量,但静态全局变量和静态局部变量只能在当前文件中使用。
变量的存储性与声明周期:
1. 只有局部自动变量和形式参数才可以定义为寄存器变量。
因为寄存器变量属于动态存储方式。
凡需要采用静态存储方式的量不能定义为寄存器变量。
2. 在Turbo C,MS C等微机上使用的C语言中,实际上是把寄存器变量当成自动变量处理的。
因此速度并不能提高。
而在程序中允许使用寄存器变量只是为了与标准C保持一致。
3. 即使能真正使用寄存器变量的机器,由于CPU
中寄存器的个数是有限的,因此使用寄存器变量的个数也是有限的。
1. static全局变量与普通的全局变量有什么区别?
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。
全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。
这两者在存储方式上并无不同。
这两者的区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。
而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。
由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
static全局变量只初使化一次,防止在其他文件单元中被引用;
2. static局部变量和普通局部变量有什么区别?
把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。
把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
static局部变量只被初始化一次,下一次依据上一次结果值;
3. static函数与普通函数有什么区别?
static函数与普通函数作用域不同,仅在本文件。
只在当前源文件中使用的函数应该说明为内部函数(static修饰的函数),内部函数应该在当前源文件中说明和定义。
对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
在C语言中,static的字面意思很容易把我们导入歧途,其实它的作用有三条。
(1)先来介绍它的第一条也是最重要的一条:隐藏。
当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。
为理解这句话,我举例来说明。
我们要同时编译两个源文件,一个是a.c,另一个是main.c。
下面是a.c的内容
char a = 'A'; // global variable
void msg()
{
printf("Hello\n");
}
下面是main.c的内容
int main(void)
{
extern char a; // extern variable must be declared before use
printf("%c ", a);
(void)msg();
return0;
}
程序的运行结果是:
A Hello
你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,
所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。
此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。
如果加了static,就会对其它源文件隐藏。
例如在a和msg的定义前加上static,main.c就看不到它们了。
利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。
Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。
(2)static的第二个作用是保持变量内容的持久。
存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。
共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。
虽然这种用法不常见,但我还是举一个例子。
#include<stdio.h>
int fun(void){
static int count = 10; // 事实上此赋值语句从来没有执行过
return count--;
}
int count = 1;
int main(void)
{
printf("global\t\tlocal static\n");
for(; count <= 10; ++count)
printf("%d\t\t%d\n", count, fun());
return0;
}
程序的运行结果是:
global local static
1 10
2 9
3 8
4 7
5 6
6 5
7 4
8 3
9 2
10 1
(3)static的第三个作用是默认初始化为0。
其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。
在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。
比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。
如果定义成静态的,就省去了一开始置0的操作。
再比
如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加’\0’太麻烦。
如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是’\0’。
不妨做个小实验验证一下。
#include<stdio.h>
int a;
int main(void)
{
int i;
static char str[10];
printf("integer: %d; string: (begin)%s(end)", a, str);
return0;
}
程序的运行结果如下
integer: 0; string: (begin)(end)
一、内存基本构成
可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。
他们的功能不同,对他们使用方式也就不同。
静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。
它主要存放静态数据、全局数据和常量。
栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆区:亦称动态内存分配。
程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。
动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。
但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。
代码区:存放函数体的二进制代码
文字常量区—常量字符串就是放在这里的。
程序结束后由系统释放
函数指针指向Code区,是程序运行的指令代码,数据指针指向Data,Heap,Stack区,是程序依赖以运行的各种数据
在文件作用域声明inline函数默认为static存储类型,const常量默认为static存储,如果加上extern,则为外部存储类型。
源文档</jack__h/article/details/5419856>
二、三者之间的区别(经典)
我们通过代码段来看看对这样的三部分内存需要怎样的操作和不同,以及应该注意怎样的地方。
例一:静态存储区与栈区
char* p = “Hello World1”;
char a[] = “Hello World2”;
p[2] = ‘A’;
a[2] = ‘A’;
char* p1 = “Hello World1;”
这个程序是有错误的,错误发生在p[2] = ‘A’这行代码处,为什么呢,是变量p和变量数组a都存在于栈区的(任何临时变量都是处于栈区的,包括在main()函数中定义的变量)。
但是,数据“Hello World1”和数据“Hello World2”是存储于不同的区域的。
因为数据“Hello World2”存在于数组中,所以,此数据存储于栈区,对它修改是没有任何问题的。
因为指针变量p仅仅能够存储某个存储空间的地址,数据“Hello World1”为字符串常量,所以存储在静态存储区。
虽然通过p[2]可以访问到静态存储区中的第三个数据单元,即字符‘l’所在的存储的单元。
但是因为数据“Hello World1”为字符串常量,不可以改变,所以在程序运行时,会报告内存错误。
并且,如果此时对p和p1输出的时候会发现p和p1里面保存的地址是完全相同的。
换句话说,在数据区只保留一份相同的数据例二:栈区与堆区
char* f1()
{
char* p = NULL;
char a;
p = &a;
return p;
}
char* f2()
{
char* p = NULL:
p =(char*) new char[4];
return p;
}
这两个函数都是将某个存储空间的地址返回,二者有何区别呢?f1()函数虽然返回的是一个存储空间,但是此空间为临时空间。
也就是说,此空间只有短暂的生命周期,它的生命周期在函数f1()调用结束时,也就失去了它的生命价值,即:此空间被释放掉。
所以,当调用f1()函数时,如果程序中有下面的语句:
char* p ;
p = f1();
*p = ‘a’;
此时,编译并不会报告错误,但是在程序运行时,会发生异常错误。
因为,你对不应该操作的内存(即,已经释放掉的存储空间)进行了操作。
但是,相比之下,f2()函数不会有任何问题。
因为,new这个命令是在堆中申请存储空间,一旦申请成功,除非你将其delete 或者程序终结,这块内存将一直存在。
也可以这样理解,堆内存是共享单元,能够被多个函数共同访问。
如果你需要有多个数据返回却苦无办法,堆内存将是一个很好的选择。
但是一定要避免下面的事情发生:
void f()
{
…
char * p;
p = (char*)new char[100];
…
}
这个程序做了一件很无意义并且会带来很大危害的事情。
因为,虽然申请了堆内存,p 保存了堆内存的首地址。
但是,此变量是临时变量,当函数调用结束时p变量消失。
也就是说,再也没有变量存储这块堆内存的首地址,我们将永远无法再使用那块堆内存了。
但是,这块堆内存却一直标识被你所使用(因为没有到程序结束,你也没有将其delete,所以这块堆内存一直被标识拥有者是当前您的程序),进而其他进程或程序无法使用。
我们将这种不道德的“流氓行为”(我们不用,却也不让别人使用)称为内存泄漏。
这是我们C++程序员的大忌!!请大家一定要避免这件事情的发生。
总之,对于堆区、栈区和静态存储区它们之间最大的不同在于,栈的生命周期很短暂。
但是堆区和静态存储区的生命周期相当于与程序的生命同时存在(如果您不在程序运行中间将堆内存delete的话),我们将这种变量或数据成为全局变量或数据。
但是,对于堆区的内存空间使用更加灵活,因为它允许你在不需要它的时候,随时将它释放掉,而静态存储区将一直存在于程序的整个生命周期中。
源文档</jack__h/article/details/5419856>
堆、栈、自由存储区、全局/静态存储区和常量存储区
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。
里面的变量通常是局部变量、函数参数等。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。
如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free 来结束自己的生命的。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多,在《const的思考》一文中,我给出了6种方法)明确区分堆与栈
在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。
首先,我们举一个例子:
void f() { int* p=new int[5]; }
这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。
在程序会先确定在堆中分配内存的大小,然后调用operator。