文档库 最新最全的文档下载
当前位置:文档库 › 内存出错-内存泄露-数组越界-悬空指针-错误分配

内存出错-内存泄露-数组越界-悬空指针-错误分配

内存出错-内存泄露-数组越界-悬空指针-错误分配
内存出错-内存泄露-数组越界-悬空指针-错误分配

内存出错,内存泄露,数组越界,悬空指针,错误分配

2010-06-01 10:43 879人阅读评论(3) 收藏举报

内存出错(包括4个)

. 数组越界;

. 内存泄露;

. 悬空指针(野指针);

. 错误分配。

1.数组越界:

程序反应:

跳出“内存不足。”提示框。

调试的时候无法定位到准确的出错点,也就是程序崩溃的地方不确定。

但是大致出错会在数组所在的函数里面或调用了数组的函数里,如果总弹出以上对话框,就检查出错代码附近是否有用到数组,特别是边界处要仔细检查。

应用vector的时候也要注意越界问题。

2. 内存泄露

new后一定要delete,malloc之后一定要free。

程序反应:

一是可能像上面一样弹出对话框提示。//一般不会出现。

二是可能在Debug版下编译运行均不会崩溃,但是Release版下会导致程序崩溃,这一般是内存泄露的问题。

2.1:1. delete的时候为什么会报错?2.在哪里delete 比较合适?

BOOL Test(char*& ptStr)

{

CString temp;

temp = m_pListCtrl->GetItemText(iRow, iCol);

ilen = temp.GetLength()+1; //必须加1

ptStr = new char[ilen];

strcpy(ptStr,temp);

// delete ptStr;

return TRUE;

}

void CallTest()

{

char* cstemp = NULL;

for( i=0;i

{

Test(cstemp);

delete cstemp;

}

}

2.2:构造函数和析构函数对指针的操作

class CFM3Dlg

{

public:

CFM3Dlg();

~CFM3Dlg();

Cs2 * cs2;

}

CFM3Dlg::CFM3Dlg()

{

cs2 = NULL;

}

CFM3Dlg::~CFM3Dlg()

{

if (cs2)

{

delete cs2;

cs2 = NULL;

}

}

// 用cs2指针之前最好先判断cs2指针是否为空。

2.3:来自资源错误管理的潜在堆内存丢失

int getkey(char *filename)

{

FILE *fp;

int key;

fp = fopen(filename, "r");

fscanf(fp, "%d", &key);

return key;

}

“资源”不一定仅仅指“内存”,如FILE句柄可能与内存块不同,但是必须对它们给予同等关注。

fopen的语义需要补充性的 fclose。在没有 fclose()的情况下,C 标准不能指定发生的情况时,很可能是内存泄漏。其他资源(如信号量、网络句柄、数据库连接等)同样值得考虑,像excel中的一些应用,用完后也都要release。

2.4:new时用了[],delete时也要用[]。new时没有用[],delete时也不要用[]。

string *stringarray = new string[100];

...

delete stringarray;

stringarray指向的100个string对象中的99个不会被正确地摧毁,因为他们的析构函数永远不会被调用。

string *stringptr1 = new string;

string *stringptr2 = new string[100];

...

delete stringptr1;// 删除一个对象

delete [] stringptr2;// 删除对象数组

2.5:内存耗尽怎么办?

如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。

(1)判断指针是否为NULL,如果是则马上用return语句终止本函数。例如:

void Func(void)

{

A *a = new A;//int

if(a == NULL)//对于内存有限的小机最好作此判断

{

return;

}

}

(2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。例如:

void Func(void)

{

A *a = new A;

if(a == NULL)

{

std::cout << “Memory Exhausted” << std::endl;

exit(1);

}

}

(3)为new和malloc设置异常处理函数。例如Visual C++可以用set_new_hander 函数为new设置用户自己定义的异常处理函数。

文件中有:

typedef void (*new_handler)();

new_handler set_new_handler(new_handler p) throw();

用户自定义:

void nomorememory()

{

std::cerr << "unable to satisfy request for memory/n";

abort();

}

void OnCancel()

{

set_new_handler(nomorememory); //报错

//set_new_handler(NULL); //调用std::bad_alloc异常, bad_alloc是operator new不能满足内存分配请求时抛出的异常类型。

int *pbigdataarray = new int[1000000000];

}

3.悬空指针(野指针)

“调试”难以识别悬空指针。“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。“野指针”的原因主要有如下几种:

(1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如

char *ip = NULL;

char *ip = new char;

(2)指针ip被free或者delete之后,没有置为NULL,让人误以为ip是个合法的指针。

Delete后ip所指的对象清空,但指针仍然还有值。

若内存在释放后立即被覆盖,并且新指向的值不同于预期值,但是却很难识别出新值是错误值。

(3)指针操作超越了变量的作用范围。这种情况让人防不胜防,示例程序如下:

class A

{

public:

void Func(void){ std::cout << “Func of class A” << std::endl; } };

void Test(void)

{

A *p;

{

A a;

p = &a; // 注意 a 的生命期

}

p->Func(); // p是“野指针”

}

函数Test在执行语句p->Func()时,对象a已经消失,而p是指向a的,所以p就成了“野指针”。但奇怪的是有些编译器运行这个程序时居然没有出错,这可能与编译器有关。

4.错误分配

错误分配的管理不是很困难, 此类错误都会被快速地检测到。

4.1两个错误的内存释放

/* Allocate once, free twice. */

void f3()

{

char *p;

p = malloc(10);

...

free(p);

...

free(p);

}

/* Allocate zero times, free once. */

void f4()

{

char *p;

/* Note that p remains uninitialized here. */ free(p);

}

4.2未初始化的指针

void f2(int datum)

{

int *p2;

/* Uh-oh! No one has initialized p2. */ *p2 =datum;

...

}

指针数组及指向一维数组的指针讲解

一、指针数组及指向一维数组的指针(数组指针)讲解 1、数组指针(也称行指针) 定义 int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。 如要将二维数组赋给一指针,应这样赋值:int a[3][4];int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。 p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][] 所以数组指针也称指向一维数组的指针,亦称行指针。 2、指针数组 定义 int *p[n]; []优先级高,先与p结合成为一个数组,再由int *说明这是一个整型指针数组,它有n个指针类型的数组元素。这样赋值是错误的:p=a;只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。 如要将二维数组赋给一指针数组: int *p[3]; int a[3][4]; for(i=0;i<3;i++) p[i]=a[i]; 这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p [1]、p[2]所以要分别赋值。 这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。 还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。 比如要表示数组中i行j列一个元素: *(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j] 优先级:()>[]>* 例1、下列给定程序中,函数fun()的功能是:从N个字符串中找出最长的那个串,并将其地址作为函数值返回。 #include #include #define N 4

操作系统内存动态分配模拟算法

实验四存分配算法 1.实验目的 一个好的计算机系统不仅要有一个足够容量的、存取速度高的、稳定可靠的主存储器,而且要能合理地分配和使用这些存储空间。当用户提出申请主存储器空间时,存储管理必须根据申请者的要求,按一定的策略分析主存空间的使用情况,找出足够的空闲区域分配给申请者。当作业撤离或主动归还主存资源时,则存储管理要收回作业占用的主存空间或归还部分主存空间。主存的分配和回收的实现是与主存储器的管理方式有关的,通过本实验帮助学生理解在动态分区管理方式下应怎样实现主存空间的分配和回收。 背景知识: 可变分区方式是按作业需要的主存空间大小来分割分区的。当要装入一个作业时,根据作业需要的主存量查看是否有足够的空闲空间,若有,则按需要量分割一个分区分配给该作业;若无,则作业不能装入。随着作业的装入、撤离、主存空间被分成许多个分区,有的分区被作业占用,而有的分区是空闲的。 2.实验容 采用首次适应算法或循环首次算法或最佳适应算法分配主存空间。 由于本实验是模拟主存的分配,所以当把主存区分配给作业后并不实际启动装入程序装入作业,而用输出“分配情况”来代替。(即输出当时的空闲区说明表及其存分配表) 利用VC++6.0实现上述程序设计和调试操作。 3.实验代码 #include #include using namespace std; //定义存的大小 const int SIZE=64; //作业结构体,保存作业信息 struct Project{ int number; int length; }; //存块结构体,保存存块信息 struct Block{

指针与数组练习题

指针与数组练习题 1、下面程序实现如下功能:输入一个整数字符串转换为一个整数值,如”1234”转换为1234,”-1234”转换为-1234。读懂main函数,编写转换函数chnum #include #include void main() { char s[6]; int n; int chnum(char *p); gets(s); if (*s=='-') n=-chnum(s+1); else n=chnum(s); printf("%d\n",n); } int chnum(char*p) { int sum=0; while(*p!='\0') { if(*p>='0'&&*p<='9') sum=sum*10+*p-'0'; p++; } return sum; } 2、从键盘输入一个字符串,去掉所有非十六进制字符后转换成十进制数输出。读懂以下main函数,编写相应的函数del16和htod。 #include #include void main() { char s1[10],s2[10]; void del16(char*,char*); int htod(char*); gets(s1); //读入一字符串 del16(s1,s2); //去掉所有非十六进制字符到s2 printf("%d\n",htod(s2)); //把s2转换为10进制 }

void del16(char*s1,char*s2){ for(;*s1!='\0';s1++) if(*s1>='0'&&*s1<='9'||*s1>='a'&&*s1<='f'||*s1>='A'&&*s1<='F'){ *s2 = *s1; s2++;} *s2='\0'; } int htod(char*s2){ int sum=0; for(;*s2!='\0';s2++){ if(*s2>='0'&&*s2<='9') sum=sum*16+*s2-'0'; else if(*s2>='a'&&*s2<='f') sum=sum*16+*s2-'a'+10; else if(*s2>='A'&&*s2<='F') sum=sum*16+*s2-'A'+10; } return sum; } 3、编写函数insert(char *s1,char *s2,int pos),实现在字符串s1中的指定位置pos处插入字符串s2。 Happy Year New 7 Happy New Year #include #include int main(void) { void insert(char *s1,char *s2,int pos); char s1[80],s2[80]; int pos; gets(s1); gets(s2); scanf("%d",&pos); insert(s1,s2,pos); puts(s1); return 0; } void insert(char *s1,char *s2,int pos) {

动态内存分配

浅析动态内存分配及Malloc/free的实现2011-03-18 22:47一、概述: 动态内存分配,特别是开发者经常接触的Malloc/Free接口的实现,对许多开发者来说,是一个永远的话题,而且有时候也是一个比较迷惑的问题,本文根据自己的理解,尝试简单的探究一下在嵌入式系统中,两类典型系统中动态内存分配以及Malloc/Free的实现机制。 二、内存分配方式 Malloc/Free主要实现的是动态内存分配,要理解它们的工作机制,就必须先了解操作系统内存分配的基本原理。 在操作系统中,内存分配主要以下面三种方式存在: (1)静态存储区域分配。内存在程序编译的时候或者在操作系统初始化的时候就已经分配好,这块内存在程序的整个运行期间都存在,而且其大小不会改变,也不会被重新分配。例如全局变量,static变量等。 (2)栈上的内存分配。栈是系统数据结构,对于进程/线程是唯一的,它的分配与释放由操作系统来维护,不需要开发者来 [url=javascript:;]管理[/url] 。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时,这些存储单元会被自动释放。栈内存分配运算内置于处理器的指令集中,效率很高,不同的操作系统对栈都有一定的限制。 (3)堆上的内存分配,亦称动态内存分配。程序在运行的期间用malloc申请的内存,这部分内存由程序员自己负责管理,其生存期由开发者决定:在何时分配,分配多少,并在何时用free来释放该内存。这是唯一可以由开发者参与管理的内存。使用的好坏直接决定系统的性能和稳定。 三、动态内存分配概述 首先,对于支持虚拟内存的操作系统,动态内存分配(包括内核加载,用户进程加载,动态库加载等等)都是建立在操作系统的虚拟内存分配之上的,虚拟内存分配主要包括: 1、进程使用的内存地址是虚拟的(每个进程感觉自己拥有所有的内存资源),需要经过页表的映射才能最终指向系统实际的物理地址。 2、主内存和磁盘采用页交换的方式加载进程和相关数据,而且数据何时加载到主内存,何时缓存到磁盘是OS调度的,对应用程序是透明的。 3、虚拟存储器给用户程序提供了一个基于页面的内存大小,在32位系统中,用户可以页面大小为单位,分配到最大可以到4G(内核要使用1G或2G等内存地址)字节的虚拟内存。 4、对于虚拟内存的分配,操作系统一般先分配出应用要求大小的虚拟内存,只有当应用实际使用时,才会调用相应的操作系统接口,为此应用程序分配大小以页面为单位的实际物理内存。 5、不是所有计算机系统都有虚拟内存机制,一般在有MMU硬件支持的系统中才有虚拟内存的实现。许多嵌入式操作系统中是没有虚拟内存机制的,程序的动态分配实际是直接针对物理内存进行操作的。许多典型的实时嵌入式系统如Vxworks、Uc/OS 等就是这样。 四、动态内存分配的实现 由于频繁的进行动态内存分配会造成内存碎片的产生,影响系统性能,所以在不同的系统中,对于动态内存管理,开发了许多不同的算法(具体的算法实现不想在这里做详细的介绍,有兴趣的读者可以参考Glib C 的源代码和附录中的资料)。不同的操作系统有不同的实现方式,为了程序的可移植性,一般在开发语言的库中都提供了统一接口。对于C语言,在标准C库和Glib 中,都实现了以malloc/free为接口的动态内存分配功能。也就是说,malloc/free库函索包装了不同操作系统对动态内存管理的不同实现,为开发者提供了一个统一的开发环境。对于我们前面提到的一些嵌入式操作系统,因为实时系统的特殊要求(实

数组名和指针的区别(小结)

在Win 32 中: 1. (1) 对数组名进行sizeof运算时,结果是整个数组占用空间的大小; (2) 但是数组作为函数参数时,对数组名进sizeof 运算,结果为4; (2) 对指针进行sizeof运算得到的值是编译器分配给指针(也就是一个地址)的内存空间,即为4。 2. (1) 对数组名作&运算,得到的还是数组第一个元素的地址; (2) 对指针取地址时得到的结果是指针所在的地址,也就是指向这个指针的指针,与指针的值不同。 BOOL mytest(char param[100]) { //参数数组名:paramBytes int paramBytes = sizeof(param); printf("paramBytes \t%d\n",paramBytes); //数组:mych1[] char mych1[] = "abcdefg123"; //若定义为mych1[100] int mych1Bytes = sizeof(mych1); printf("mych1Bytes \t%d\n",mych1Bytes);//输出:100 //数组:mych[200] char mych[200] = {0}; int myBytes = sizeof(mych); printf("myBytes \t%d\n",myBytes); printf("---addr \t%d\n",mych); printf("---addr \t%d\n",&mych); //指针:pch char* pch = "12345abc"; int pchBytes = sizeof(pch); printf("pchBytes \t%d\n",pchBytes); //pch = mych; printf("---addr \t%d\n",pch); printf("---addr \t%d\n",&pch); return TRUE; } 运行:

动态内存管理知识总结

1.标准链接库提供四个函数实现动态内存管理: (1)分配新的内存区域: void * malloc(size_t size); void *calloc(size_t count , size_t size); (2)调整以前分配的内存区域: void *realloc(void *ptr , size_t size); (3)释放以前分配的内存区域: void free(void *ptr); 2.void * malloc(size_t size); 该函数分配连续的内存空间,空间大小不小于size 个字节。但分配的空间中的内容是未知的。该函数空间分配失败则返回NULL。 3.void *calloc(size_t count , size_t size); 该函数也可以分配连续的内存空间,分配不少于count*size个字节的内存空间。即可以为一个数组分配空间,该数组有count个元素,每个元素占size个字节。而且该函数会将分配来的内存空间中的内容全部初始化为0 。该函数空间分配失败则返回NULL。 4. 以上两个分配内存空间的函数都返回void * (空类型指针或无类型指针)返回的指针值是“分配的内存区域中”第一个字节的地址。当存取分配的内存位置时,你所使用的指针类型决定如何翻译该位置的数据。以上两种分配内存空间的方法相比较,calloc()函数的效果更好。原因是它将分配得来的内存空间按位全部置0 。 5. 若使用上述两种分配内存的函数分配一个空间大小为0 的内存,函数会返回一个空指针或返回一个没有定义的不寻常指针。因此绝不可以使用“指向0 字节区域”的指针。 6. void *realloc(void *ptr , size_t size); 该函数释放ptr所指向的内存区域,并分配一个大小为size字节的内存区域,并返回该区域的地址。新的内存区域可以和旧的内存区域一样,开始于相同的地址。且此函数也会保留原始内存内容。如果新的内存区域没有从原始区域的地址开始,那么此函数会将原始的内容复制到新的内存区域。如果新的内存区域比较大,那么多出来部分的值是没有意义的。 7. 可以把空指针传给realloc()函数,这样的话此函数类似于malloc()函数,并得到一块内存空间。如果内存空间不足以满足内存区域分配的请求,那么realloc()函数返回一个空指针,这种情况下,不会释放原始的内存区域,也不会改变它的内容。 8. void free(void *ptr); 该函数释放动态分配的内存区域,开始地址是ptr,ptr的值可以是空指针。若在调用此函数时传入空指针,则此函数不起任何作用。 9. 传入free() 和realloc()函数的指针(若不为空指针时)必须是“尚未被释放的动态分配内存区域的起始地址”。否则函数的行为未定义。Realloc()函数也可以释放内存空间,例如:Char *Ptr = (char *)malloc(20); 如只需要10个字节的内存空间,且保留前十个字节的内容,则可以使用realloc()函数。 Ptr = Realloc(ptr,10); // 后十个字节的内存空间便被释放

《动态分配内存与数据结构》课后习题

《动态分配内存与数据结构》习题 学号姓名 一、选择题 1、是一种限制存取位置的线性表,元素的存取必须服从先进先出的规则。 A.顺序表B.链表C.栈D.队列 2、是一种限制存取位置的线性表,元素的存取必须服从先进后出的规则。 A.顺序表B.链表C.栈D.队列 3、与顺序表相比,链表不具有的特点是。 A.能够分散存储数据,无需连续内存空间 B.插入和删除无需移动数据 C.能够根据下标随机访问 D.只要内存足够,没有最大长度的限制 4、如果通过new运算符动态分配失败,返回结果是。 A.-1 B.0 C.1D.不确定 5、实现深复制中,不是必须自定义的。 A.构造函数B.复制构造函数 C.析构函数D.复制赋值操作符函数 6、分析下列代码是否存在问题,选择合适的选项:。 int main(void) { int *p = new int [10]; p = new int [10]; delete [] p; p = NULL; return 0; } A.没有问题 B.有内存泄漏 C.存在空悬指针 D.存在重复释放同一空间 7、通过new运算符动态分配的对象,存储于内存中的。 A.全局变量与静态变量区 B.代码区 C.栈区 D.堆区 8、下列函数中,可以是虚函数。 A.构造函数 B.析构函数 C.静态成员函数 D.友元函数 9、关于通过new运算符动态创建的对象数组,下列判断中是错误的。 A. 动态创建的对象数组只能调用默认构造函数 B. 动态创建的对象数组必须调用delete []动态撤销 C. 动态创建的对象数组的大小必须是常数或常变量 D. 动态创建的对象数组没有数组名 10、顺序表不具有的特点是 A. 元素的存储地址连续 B. 存储空间根据需要动态开辟,不会溢出 C. 可以直接随机访问元素 D. 插入和删除元素的时间开销与位置有关 11、假设一个对象Ob1的数据成员是指向动态对象的指针,如果采用浅复制的方式复制该对象得到对象Ob2,那么在析构对象Ob1和对象Ob2时会的问题。 A. 有重复释放 B. 没有 C. 内存泄漏 D. 动态分配失败 12、假设对5个元素A、B、C、D、E进行压栈或出栈的操作,压栈的先后顺序是ABCDE,则出栈的先后顺序不可能是。 A. ABCDE B. EDCBA C. EDBCA D. BCADE 13、假设对4个元素A、B、C、D、E进行压栈或出栈的操作,压栈的先后顺序是ABCD,则出栈的先后顺序不可能是。 A. ABCD B. DCBA C. BCAD D. DCAB 14、通过new运算符动态创建的对象的存放在中。 A. 代码区 B. 栈区 C. 自由存储区 D. 全局数据区 15、链表不具有的特点是。 A. 元素的存储地址可以不连续 B. 存储空间根据需要动态开辟,不会溢出 C. 可以直接随机访问元素 D. 插入和删除元素的时间开销与位置无关 16、有关内存分配和释放的说法,下面当中错误的是 A.new运算符的结果只能赋值给指针变量 B.动态创建的对象数组必须调用delete []动态撤销 C.用new分配的空间位置是在内存的栈区 D.动态创建的对象数组没有数组名 17、关于栈,下列哪项不是基本操作 A.删除栈顶元素 B.删除栈底元素 C.判断栈是否为空 D.把栈置空 18、关于链表,说法错误的是

C语言指针数组函数练习(含参考答案)

作业(使用指针、数组、函数完成) 1. 编写一个通用函数,该函数可以实现判断:一个含有五位数字的整数是否是回文数。回文数的含义是从左向右与从右向左看,数是相同的。如:23732是回文数,而23564则不是。编写主程序调用该函数实现求所有5位数字中满足条件的数的个数。 #include int Judge(int num) { int w,q,b,s,g; w=num/10000; q=num%10000/1000; s=(num%100)/10; g=num%10; if((w==g)&&(q==s)) return 1; else return 0; } void main() { int count=0; int i; for(i=10000;i<=99999;i++) if(Judge(i)) count++; printf("%d\n",count); } 2.编写一个通用函数,该函数可以实现对数值型数组的倒序。倒序的含义是把数组的元素值前后颠倒。例数组:20,19,18,15,13,10倒序的结果为:10,13,15,18,19,20。编写主程序,数组初始化方式不限,并输出,然后调用该函数实现倒序后再输出倒序的结果。#include #define N 6 void Transfer(double *b,int n) { double temp; double *i=b; double *j=b+n-1; while(j>i) { temp=*i; *i=*j; *j=temp; i++;

j--; } } void main() { double array[N]={20,19,18,15,13,10}; int i; for(i=0;i #include double Cal(double *p,int n) { int i,j; double sum=0; for(i=0;i

C语言 数组和指针练习题

若当堂没做完,下周四之前上交也可。 至ftp://211.64.82.253/ 用户名和密码:stu C语言程序设计练习题——数组 一、选择题 77、以下对一维数组a的正确说明是_d ___ A、char a(10); B、int a[ ]; C、int k=5,a[k]; D、char a[ ]={'a' , 'b' , 'c'}; 78、若有说明语句:int a[2][4];,则对a数组元素的正确引用是_a___ A、a[0][3] B、a[0][4] C、a[2][2] D、a[2][2+1] 79、以下能对二维数组y进行初始化的语句是_b__ A、static int y[2][ ]={{1,0,1}, {5,2,3}}; B、static int y[ ][3]={{1,2,3}, {4,5,6}}; C、static int y[2][4]={{1,2,3}, {4,5} , {6}}; D、static int y[ ][3]={{1,0,1,0}, { }, {1,1}}; 80、若有说明语句:int y[ ][4]={0,0};则下面叙述不正确的是_d___ A、数组y的每个元素都可得初值0 B、二维数组y的行数为1 C、该说明等价于int y[ ][4]={0}; D、只有元素y[0][0]和y[0][1]可得到初值0,其余元素均得不到初值0 81、以下各组选项中,均能正确定义二维实型数组s的选项是_c___ A、float s[3][4]; B、float s(3,4); float s[ ][4]; float s[ ][ ]={{0};{0}}; float s[3][ ]={{1},{0}}; float s[3][4]; C、 float s[3][4]; D、float s[3][4]; static float s[ ][4]={{0},{0}}; float s[3][ ]; auto float s[ ][4]={{0},{0},{0}}; float s[ ][4]; 82、若有说明语句:int a[ ][3]={1,2,3,4,5,6,7,8}; ,则a数组的行数为__a__ A、3 B、2 C、无确定值 D、1 83、若二维数组y有m列,则在y[i][j]前的元素个数为_B___ A、j*m+i B、i*m+j C、i*m+j-1 D、i*m+j+1 84、下面程序中有错误的行是__D__ 1、 main( ) 2、 { 3、 int x[3]={1}; 4、 int k; 5、 scanf("%d", &x); 6、 for (k=1; k<3; k++) 7、 x[0]=x[0]+x[i]; 8、 printf("x[0]=%d\n", x[0]); 9、 } A、3 B、6 C、7 D、5 85、若有以下语句,则正确的描述是__b__ char x[ ]="12345"; char y[ ]={'1', '2', '3', '4', '5'}; A、x数组与y数组的长度相同 B、x数组长度大于y数组长度 C、x数组长度小于y数组长度

指针与内存详解.

在计算机领域,堆栈是一个不容忽视的概念,但是很多人甚至是计算机专业的人也没有明确堆栈其实是两种数据结构。 堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top对数据项进行插入和删除。 要点: 堆:顺序随意 栈:后进先出(Last-In/First-Out [编辑本段]堆和栈的区别 一、预备知识—程序的内存分配 一个由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"优化成一个地方。 [编辑本段]堆和栈的理论知识 1.申请方式 stack: 由系统自动分配。例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 heap: 需要程序员自己申请,并指明大小,在c中malloc函数 如p1 = (char *malloc(10; 在C++中用new运算符 如p2 = new char[20];//(char *malloc(10; 但是注意p1、p2本身是在栈中的。 2.申请后系统的响应

DELPHI和C指针与内存分配比较

delphi和c指针与内存分配比较 1.动态变量 对于动态分内存的变量,使用System单元的下面两个函数: procedure New(var P:Pointer);//为变更分配内存 procedure Dispose(var P:Pointer);//释放内存 其中P为变量地址 示例: type Str18=string[18]; var P:^Str18; begin New(P); P^:='Now you see it...'; Dispose(P);{Now you don't...} end; ///////////////////////////// Delphi里自己管理内存的两对函数new(),dispose()和getmem(),freemem() delphi的指针 大家都认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是C语言的灵魂,一点都不为过。同时,这种说法也让很多人产生误解,似乎只有C语言的指针才能算指针。Basic不支持指针,在此不论。其实,Pascal语言本身也是支持指针的。从最初的Pascal发展至今的Object Pascal,可以说在指针运用上,丝毫不会逊色于C 语言的指针。 以下内容分为八部分,分别是 一、类型指针的定义 二、无类型指针的定义 三、指针的解除引用 四、取地址(指针赋值) 五、指针运算 六、动态内存分配 七、字符数组的运算 八、函数指针 一、类型指针的定义。对于指向特定类型的指针,在C中是这样定义的: int*ptr; char*ptr; 与之等价的Object Pascal是如何定义的呢? var ptr:^Integer; ptr:^char; 其实也就是符号的差别而已。 二、无类型指针的定义。C中有void*类型,也就是可以指向任何类型数据的指针。Object Pascal为其定义了一个专门的类型:Pointer。于是, ptr:Pointer; 就与C中的 void*ptr; 等价了。 三、指针的解除引用。要解除指针引用(即取出指针所指区域的值),C的语法是(*ptr),ObjectPascal则是ptr^。 四、取地址(指针赋值)。取某对象的地址并将其赋值给指针变量,C的语法是 ptr=&Object; Object Pascal则是 ptr:=@Object; 也只是符号的差别而已。

c语言中动态内存申请与释放的简单理解

c语言中动态内存申请与释放的简单理解 在C里,内存管理是通过专门的函数来实现的。与c++不同,在c++中是通过new、delete函数动态申请、释放内存的。 1、分配内存 malloc 函数 需要包含头文件: #include 或 #include 函数声明(函数原型): void *malloc(int size); 说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。 从函数声明上可以看出。malloc 和 new 至少有两个不同: new 返回指定类型的指针,并且可以自动计算所需要大小。比如: int *p; p = new int; //返回类型为int* 类型(整数型指针),分配大小为 sizeof(int); 或: int* parr; parr = new int [100]; //返回类型为 int* 类型(整数型指针),分配大小为sizeof(int) * 100; 而 malloc 则必须由我们计算需要的字节数,并且在返回后强行转换为实际类型的指针。 int* p; p = (int *) malloc (sizeof(int)); 第一、malloc 函数返回的是 void * 类型,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。 第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成:

C语言指针数组和数组指针

C语言指针数组和数组指针 一、指针数组和数组指针的内存布局 初学者总是分不出指针数组与数组指针的区别。其实很好理解: 指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。 数组指针:首先它是一个指针,它指向一个数组。在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。 下面到底哪个是数组指针,哪个是指针数组呢: A) int *p1[10]; B) int (*p2)[10]; 每次上课问这个问题,总有弄不清楚的。这里需要明白一个符号之间的优先级问题。 “[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。至于p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。我们可以借助下面的图加深理解:

二、int (*)[10] p2-----也许应该这么定义数组指针 这里有个有意思的话题值得探讨一下:平时我们定义指针不都是在数据类型后面加上指针变量名么?这个指针p2 的定义怎么不是按照这个语法来定义的呢?也许我们应该这样来定义p2: int (*)[10] p2; int (*)[10]是指针类型,p2 是指针变量。这样看起来的确不错,不过就是样子有些别扭。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量p2 前移了而已。你私下完全可以这么理解这点。虽然编译器不这么想。^_^ 三、再论a 和&a 之间的区别 既然这样,那问题就来了。前面我们讲过a 和&a 之间的区别,现在再来看看下面的代码: int main() { char a[5]={'A','B','C','D'}; char (*p3)[5] = &a; char (*p4)[5] = a; return 0;

C++指针与动态分配内存new关键字专题

本文作者:黄邦勇帅 本文是学习C++的基础内容,指针是C或C++所特有的,因此应熟练掌握指针的使用,本文集中介绍C或C++中的各种指针,包括指针数组,数组指针,常量(const)指针,指向指针的指针,尤其是对二维数组和指针进行了详细精辟的解释,在读完本文的二维数组和指针的讲解之后,相信你就会对指针有一个车底的了解了。 本文内容完全属于个人见解与参考文现的作者无关,其中难免有误解之处,望指出更正。 声明:禁止抄袭本文,若需要转载本文请注明转载的网址,或者注明转载自“黄邦勇帅”。 主要参考文献: 1、C++.Primer.Plus.第五版.中文版[美]Stephen Prata著孙建春韦强译人民邮电出版社2005年5月 2、C++.Primer.Plus.第四版.中文版Stanley B.Lippman、Barbara E.Moo著李师贤等译人民邮电出版社2006年3月 3、C++.Primer.Plus.第三版.中文版Stanley B.Lippman等著潘爱民张丽译中国电力出版社2002年5月 4、C++入门经典第三版[美]Ivor Horton著李予敏译清华大学出版社2006年1月 5、C++参考大全第四版[美]Herbert Schidt著周志荣朱德芳于秀山等译电子工业出版社2003年9月 6、21天学通第四版C++ [美]Jesse Liberty著康博创作室译人民邮电出版社2002年3月 第一部分:指针 11.1 基础 1.指针是一个变量,它存储着另一个变量或函数的地址,也就是说可以通过指针间接地引用变量。指针变量包含一个地址,而且可以存储任何数据类型的内存地址,但指针变量却被声明为特定的数据类型,一个指向整型数据类型的指针不能存储一个浮点型的变量地址。 2.指针声明的形式为,数据类型*指针变量名;其中*星号是指针运算符,例如int *x;声明x为int型指针.11.2 指针运算符*和&地址运算符 1.&地址运算符是一元运算符,能反回它的操作数的内存地址.如y=&x;把变量x的地址输入到y中,它与x的值无关,比如x的值为1000,而x的地址为55则,y将接收到地址55. 2.*指针运算符是一元运算符,它是&运算符的相反形式,*运算符能反回位于其操作数所指定的地址的变量的值.例如y = &x;z = *y;假设x的值为1000,地址为55,则第二条语句说明z的值为1000,*y把由y所指向的内存的地址的变量x的值赋给z。*运算符可理解为“在地址中”,则z=*y可描术为“z接收了在址址y中的值。”,3.其实可以把*y当成一个变量来使用,即可以为*y赋值等,例如*y=100;(*y)++;等,但要注意的是对*y的操作相当于是对此指针指向的地址中的变量的操作,即对*y=100的赋值语句,相当于是x=100,而(*y)++则相当于x++。11.3 指针的运算 0.指针只支持4种算术运算符:++,――,+,-.指针只能与整数加减.指针运算的原则是:每当指针的值增加时,它将指向其基本类型的下一个元素的存储单元.减少时则指向上一个元素的存储单元. 1.++,――运算符,假设int型x的地址为200,且int型占4个字节,定义int *p;p=&x;则p++的地址将是204,而不是201,因为当指针p的值增加时,它都将指向下一个int型数据.减少时也是这样,如p――则,p的地址将是196.2.+,-,运算符,注意两个指针不能相加.例int *p;p=&x;假设x的地址为200,则p+9将的指针地址将是200+4*9=236,即p指向了从当前正指向的元素向下的第9个元素. 3.两指针相减,同类型的一个指针减去另一个指针的值将是两个指针分开的基本类型的元素的个数. 11.4 指针和数组 1.在C++语言中使用没有下标的数组名会产生一个指向数组中第一个元素的指针.如char x[20];char *p;p=x;此语句说明将x数组的第一个元素的地址赋给指针p. 2.*(p+4)和x[4]两句都可以访问数组中第5个元素,这里假设int x[33];int *p;p=x;因为p是指向数组x的第一个元素地址的指针,而p+4就是指向第五个元素的指针,而*(p+4)就是第五的个元素了. 3.p[i]语句相当于*(p+i)或x[i]即数组中第i+1个元素的值,假设char x[20];char *p;p=x; 11.5 字符串常量

动态内存分配(C语言)

实验报告 实验课程名称:动态内存分配算法 年12月1日

实验报告 一、实验内容与要求 动态分区分配又称为可变分区分配,它是根据进程的实际需要,动态地为之分配内存空间。在实验中运用了三种基于顺序搜索的动态分区分配算法,分别是1.首次适应算法2.循环首次适应算法3.最佳适应法3.最坏适应法分配主存空间。 二、需求分析 本次实验通过C语言进行编程并调试、运行,显示出动态分区的分配方式,直观的展示了首次适应算法循环首次适应算法、最佳适应算法和最坏适应算法对内存的释放和回收方式之间的区别。 首次适应算法 要求空闲分区链以地址递增的次序链接,在分配内存时,从链首开始顺序查找,直至找到一个大小能满足要求的空闲分区为止,然后在按照作业的大小,从该分区中划出一块内存空间,分配给请求者,余下的空余分区仍留在空链中。 优点:优先利用内存中低址部分的空闲分区,从而保留了高址部分的大空闲区,为以后到达的大作业分配大的内存空间创造了条件。 缺点:低址部分不断被划分,会留下许多难以利用的、很小的空闲分区即碎片。而每次查找又都是从低址部分开始的,这无疑又会增加查找可用空闲分区时的开

循环首次适应算法 在为进程分配内存空间时,不是每次都从链首开始查找,而是从上次找到的空闲分区的下一个空闲分区开始查找,直到找到一个能满足要求的空闲分区。 优点:该算法能使内存中的空闲分区分布得更均匀,从而减少了查找空闲分区时的开销。 最佳适应算法 该算法总是把能满足要求、又是最小的空闲分区分配给作业,避免大材小用,该算法要求将所有的空闲分区按其容量以从小到大的顺序形成一空闲分区链。 缺点:每次分配后所切割下来的剩余部分总是最小的,这样,在存储器中会留下许多难以利用的碎片。 最坏适应算法 最坏适应算法选择空闲分区的策略正好与最佳适应算法相反:它在扫描整个空闲分区或链表时,总会挑选一个最大的空闲区,从中切割一部分存储空间给作业使用。该算法要求,将所有的空闲分区,按其容量以大到小的顺序形成一空闲分区链。查找时,只要看第一个分区能否满足作业要求即可。 优点:可使剩下的空闲区不至于太小,产生碎片的可能性最小,对中小作业有利,同时,最坏适应算法查找效率很高。 缺点:导致存储器中缺乏大的空闲分区 三、数据结构 为了实现动态分区分配算法,系统中配置了相应的数据结构,用以描述空闲分区和已分配分区的情况,常用的数据结构有空闲分区表和空闲分区链

C++指针与动态分配内存new关键字专题

第一部分:指针 11.1 基础 1.指针是一个变量,它存储着另一个变量或函数的地址,也就是说可以通过指针间接地引用变量。指针变量包含一个地址,而且可以存储任何数据类型的内存地址,但指针变量却被声明为特定的数据类型,一个指向整型数据类型的指针不能存储一个浮点型的变量地址。 2.指针声明的形式为,数据类型*指针变量名;其中*星号是指针运算符,例如int *x;声明x为int型指针.11.2 指针运算符*和&地址运算符 1.&地址运算符是一元运算符,能反回它的操作数的内存地址.如y=&x;把变量x的地址输入到y中,它与x的值无关,比如x的值为1000,而x的地址为55则,y将接收到地址55. 2.*指针运算符是一元运算符,它是&运算符的相反形式,*运算符能反回位于其操作数所指定的地址的变量的值.例如y = &x;z = *y;假设x的值为1000,地址为55,则第二条语句说明z的值为1000,*y把由y所指向的内存的地址的变量x的值赋给z。*运算符可理解为“在地址中”,则z=*y可描术为“z接收了在址址y中的值。”,3.其实可以把*y当成一个变量来使用,即可以为*y赋值等,例如*y=100;(*y)++;等,但要注意的是对*y的操作相当于是对此指针指向的地址中的变量的操作,即对*y=100的赋值语句,相当于是x=100,而(*y)++则相当于x++。11.3 指针的运算 0.指针只支持4种算术运算符:++,――,+,-.指针只能与整数加减.指针运算的原则是:每当指针的值增加时,它将指向其基本类型的下一个元素的存储单元.减少时则指向上一个元素的存储单元. 1.++,――运算符,假设int型x的地址为200,且int型占4个字节,定义int *p;p=&x;则p++的地址将是204,而不是201,因为当指针p的值增加时,它都将指向下一个int型数据.减少时也是这样,如p――则,p的地址将是196.2.+,-,运算符,注意两个指针不能相加.例int *p;p=&x;假设x的地址为200,则p+9将的指针地址将是200+4*9=236,即p指向了从当前正指向的元素向下的第9个元素. 3.两指针相减,同类型的一个指针减去另一个指针的值将是两个指针分开的基本类型的元素的个数. 11.4 指针和数组 1.在C++语言中使用没有下标的数组名会产生一个指向数组中第一个元素的指针.如char x[20];char *p;p=x;此语句说明将x数组的第一个元素的地址赋给指针p. 2.*(p+4)和x[4]两句都可以访问数组中第5个元素,这里假设int x[33];int *p;p=x;因为p是指向数组x的第一个元素地址的指针,而p+4就是指向第五个元素的指针,而*(p+4)就是第五的个元素了. 3.p[i]语句相当于*(p+i)或x[i]即数组中第i+1个元素的值,假设char x[20];char *p;p=x; 11.5 字符串常量

相关文档
相关文档 最新文档