delphi和c指针与内存操作指南

时间:2019-05-14 02:12:13下载本文作者:会员上传
简介:写写帮文库小编为你整理了多篇相关的《delphi和c指针与内存操作指南》,但愿对你工作学习有帮助,当然你在写写帮文库还可以找到更多《delphi和c指针与内存操作指南》。

第一篇: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中,可以对指针进行移动的运算,如: char a[20];char *ptr=a;ptr++;ptr+=2;当执行ptr++;时,编译器会产生让ptr前进sizeof(char)步长的代码,之后,ptr将指向a[1]。ptr+=2;这句使得ptr前进两个sizeof(char)大小的步长。同样,我们来看一下Object Pascal中如何实现: var a: array [1..20] of Char;ptr: PChar;//PChar可以看作^Char begin ptr:= @a;Inc(ptr);// 这句等价于C的ptr++;Inc(ptr, 2);//这句等价于C的ptr+=2;end;只是,Pascal中,只允许对有类型的指针进行这样的运算,对于无类型指针是不行的。

六、动态内存分配。C中,使用malloc()库函数分配内存,free()函数释放内存。如这样的代码: int *ptr, *ptr2;int i;ptr=(int*)malloc(sizeof(int)* 20);ptr2 = ptr;for(i=0;i<20;i++){ *ptr =i;ptr++;} free(ptr2);Object Pascal中,动态分配内存的函数是GetMem(),与之对应的释放函数为FreeMem()(传统Pascal中获取内存的函数是New()和 Dispose(),但New()只能获得对象的单个实体的内存大小,无法取得连续的存放多个对象的内存块)。因此,与上面那段C的代码等价的Object Pascal的代码为: var ptr, ptr2 : ^integer;i: integer;begin GetMem(ptr, sizeof(integer)*20);//这句等价于C的ptr=(int*)malloc(sizeof(int)*20);ptr2:= ptr;//保留原始指针位置 for i:= 0 to 19 do begin ptr^ := i;Inc(ptr);end;FreeMem(ptr2);end;对于以上这个例子(无论是C版本的,还是Object Pascal版本的),都要注意一个问题,就是分配内存的单位是字节(BYTE),因此在使用GetMem时,其第二个参数如果想当然的写成20,那么就会出问题了(内存访问越界)。因为GetMem(ptr, 20);实际只分配了20个字节的内存空间,而一个整形的大小是四个字节,那么访问第五个之后的所有元素都是非法的了(对于malloc()的参数同样)。

七、字符数组的运算。C语言中,是没有字符串类型的,因此,字符串都是用字符数组来实现,于是也有一套str打头的库函数以进行字符数组的运算,如以下代码: char str[15];char *pstr;strcpy(str “teststr”);strcat(str, “_testok”);pstr =(char*)malloc(sizeof(char)* 15);strcpy(pstr, str);printf(pstr);free(pstr);而在Object Pascal中,有了String类型,因此可以很方便的对字符串进行各种运算。但是,有时我们的Pascal代码需要与C的代码交互(比如:用Object Pascal的代码调用C写的DLL或者用ObjectPascal写的DLL准备允许用C写客户端的代码)的话,就不能使用String类型了,而必须使用两种语言通用的字符数组。其实,Object Pascal提供了完全相似C的一整套字符数组的运算函数,以上那段代码的Object Pascal版本是这样的: var str : array [1..15] ofchar;pstr: PChar;//Pchar 也就是 ^Char begin StrCopy(@str, 'teststr');//在C中,数组的名称可以直接作为数组首地址指针来用 //但Pascal不是这样的,因此str前要加上取地址的运算符 StrCat(@str, '_testok');GetMem(pstr, sizeof(char)* 15);StrCopy(pstr, @str);Write(pstr);FreeMem(pstr);end;

八、函数指针。在动态调用DLL中的函数时,就会用到函数指针。假设用C写的一段代码如下: typedef int(*PVFN)(int);//定义函数指针类型 int main(){ HMODULE hModule =LoadLibrary(“test.dll”);PVFN pvfn = NULL;pvfn =(PVFN)GetProcAddress(hModule, “Function1”);pvfn(2);FreeLibrary(hModule);} 就我个人感觉来说,C语言中定义函数指针类型的typedef代码的语法有些晦涩,而同样的代码在ObjectPascal中却非常易懂:

type PVFN =Function(para: Integer): Integer;var fn : PVFN;//也可以直接在此处定义,如:fn: function(para:Integer):Integer;hm: HMODULE;begin hm:= LoadLibrary('test.dll');fn:= GetProcAddress(hm, 'Function1');fn(2);FreeLibrary(hm);end;Delphi中指针功能非常强大,所有c中能实现的指针Delphi中都能实现.上面认为Delphi指针不是强项的只是一种误解(或者对指针的机制一知半解).由于Pascal语言的限制, 用Delphi的指针时很多情况下需要强制类型转换.Delphi中提供了很多指针类型, 而且非常方便的是你可以自定义自己的指针类型.一个经验: 要掌握一种数据类型并且能够灵活应用,一个比较好的办法是别考虑什么类型是什么名字, 而只需要考虑这种类型的变量将占用多少字节.凡是字节数相同的类型都可以认为是同一类型 :-), 提供不同类型只是为了编译器能够更方便的查找错误而已.比如: Integer, Pointer, PChar, TSmallPoint甚至 array [0..4] of Char 你都可以把他们当成是同一类型加以使用(有了这种思路, 可以实现很大的程序灵活性和代码高效性).所以我很不理解的是JAVA中不支持指针(因此我也认为用JAVA绝对不可能写出很高效的程序, 而且会有很多C/C++/DELPHI中用一句话可以完成的工作在JAVA中需要用一个复杂过程, 消耗很多额外内存才能达到相同目的).就事论事, 根据你的问题在Delphi中和C中的解决方案没什么两样.delphi指针简单入门:

看一个指针用法的例子: 1 var 2 X, Y: Integer;// X and Y 整数类型 3 P: ^Integer;// P 指向整数类型的指针 4 begin 5 X :=17;// 给 X 赋值 6 P := @X;// 把 x的地址赋给p 7 Y := P^;// 取出p所指向的数值赋给y 8 end;

第二行定义了两个变量X,y.第三行声明了p是指向整数类型的指针;意味着p能够指向x或者y的地址.第五行赋给x值,第六行把x的地址赋给p.最

后通过p指向的变量赋值给y.此时,x和y有相同的值.操作符@用来取出变量的地址,也可以取出过程和函数的地址.而符号^有两个目标, 当它出现在类型定义的前面时如 ^typename 表示指向这种类型的指针;当它出现在指针变量后边时 如 point^ 返回指针指向的变量的值;

理解指针比较容易理解面向对象的pascal语言,因为指针经常在幕后操作.任何要求动态分配大的内存空间的类型可以用指针类型.例如 ,long-string变量,实际在使用指针进行操作.另外一些高级的编程技术需要使用指针类型.有时指针是适应object pascal严格的类型限制的唯一方法.同过一个通用的指针类型,通过类型转换成不同的指针类型,如下面的例子: type

PInteger = ^Integer;var R: Single;I: Integer;P: Pointer;//通用的指针 PI: PInteger;begin P := @R;//取出R的内存地址

PI := PInteger(P);//把通用类型转换成指向整数类型的指针 I := PI^;end;

当然了,实数和整数的存储格式不同.这种赋值是把原始的二进制数据从R拷贝到I,而不进行转换.保留字nil是一个特殊的常量可以赋给任何指针类型,当nil赋給一个指针时,指针什么也不指向,是一个空指针.@操作符返回变量的内存中的存储地址,或者是过程函数方法;1.如果变量,@X返回的是x的地址。如果编译选项{$T-}没有打开,着返回的事一个通用的指针,如果编译选项打开了,着返回的是x的类型对应的指

针.2.如果是例程(过程函数),@F返回的是F的入口点,@F的类型是一个指针。

3.当@用在类的方法中时,则方法的名称必须有类名,例如@TMyclass.Dosomething 指针指向TMyclass的dosomething方法。

当一个过程变量在赋值语句的左边时,编译器期望一个过程值在赋值语句的右边。这种赋值使得左边的变量可以指向右边定义的过程或者函数

入口点。换句话说,可以通过该变量来引用声明的过程或者函数,可以直接使用参数的引用。

var

F: function(X: Integer): Integer;I: Integer;function SomeFunction(X: Integer): Integer;...F := SomeFunction;// 给f赋值 I := F(4);// 调用所指向的函数

在赋值语句中,左边变量的类型决定了右边的过程或者方法指针解释。

var

F, G: function: Integer;I: Integer;function SomeFunction: Integer;...F := SomeFunction;// 给f赋值 G := F;// 把F的值拷贝给G I := G;// 调用函数

第一句获得函数的入口,第二句将指针复制,第三句获得函数的返回值。

有时候还可以这样使用

if F = MyFunction then...;在这里,F的出现导致一个函数调用;编译器调用F指向的函数,然后调用Myfunction,比较结果。这个规则是无论何时一个过程变量(procedural variable)出现在一个表达式中,它表示调用所指向的函数或者过程。有时F指向一个过程(没有返回值),或者f指向一个需要参

数的函数,则前面的语句会产生一个编译错误。要比较F和Myfunction需要用 if @F = @MyFunction then...;@F把F转换成一个包含地址的无类型的指针变量,@myfunction返回myfunction的地址。

获得一个过程变量的内存地址使用@@。例如,@@F返回F的地址。

@操作符通常把一个无类型的指针值赋给一个过程变量,例如: var StrComp: function(Str1, Str2: PChar): Integer;...@StrComp := GetProcAddress(KernelHandle, 'lstrcmpi');调用GetProcAddres函数,用strcomp指向这个值

任何过程变量可以赋成nil,表示指证什么也不指向。但是试图调用一个nil值的过程变量导致一个错误,为了测试一个过程变量是否可以赋值,用标准的赋值函数Assigned if Assigned(OnClick)then OnClick(X);

////////////////////////////// procedure Move(const Source;var Dest;Count: Integer);

procedure CopyMemory(Destination:Pointer;Source:Pointer;Length:Cardinal);

第二篇:C与C 经典面试题(内存泄露)汇总

C、C++语言面试题2007-07-15 18:57 1.已知strcpy 函数的原型是:

char *strcpy(char *strDest, const char *strSrc);其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请编写函数 strcpy 答案:

char *strcpy(char *strDest, const char *strSrc){ if(strDest == NULL || strSrc == NULL)return NULL;if(strDest == strSrc)return strDest;char *tempptr = strDest;while((*strDest++ = *strSrc++)!= ‘’);return tempptr;}

2.已知类String 的原型为: class String { public: String(const char *str = NULL);// 普通构造函数 String(const String &other);// 拷贝构造函数 ~ String(void);// 析构函数

String & operate =(const String &other);// 赋值函数 private: char *m_data;// 用于保存字符串 };请编写String 的上述4 个函数。答案:

String::String(const char *str){ if(str == NULL)//strlen在参数为NULL时会抛异常才会有这步判断 { m_data = new char[1];m_data[0] = '';} else { m_data = new char[strlen(str)+ 1];strcpy(m_data,str);} }

String::String(const String &other){ m_data = new char[strlen(other.m_data)+ 1];strcpy(m_data,other.m_data);} String & String::operator =(const String &other){ if(this == &other)return *this;delete []m_data;m_data = new char[strlen(other.m_data)+ 1];strcpy(m_data,other.m_data);return *this;} String::~ String(void){ delete []m_data;}

3.简答

3.1 头文件中的ifndef/define/endif 干什么用? 答:防止该头文件被重复引用。

3.2#include 和#include “filename.h” 有什么区别?

答:对于#include ,编译器从标准库路径开始搜索filename.h 对于#include “filename.h”,编译器从用户的工作路径开始搜索filename.h 3.3 在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?

答:C++语言支持函数重载,C 语言不支持函数重载。函数被C++编译后在库中的名字与C 语言的不同。假设某个函数的原型为: void foo(int x, int y);该函数被C 编译器编译后在库中的名字为_foo,而C++ 编译器则会产生像_foo_int_int 之类的名字。

C++提供了C 连接交换指定符号extern“C”来解决名字匹配问题。

3.4 一个类有基类、内部有一个其他类的成员对象,构造函数的执行顺序是怎样的。(Autodesk)

答:先执行基类的(如果基类当中有虚基类,要先执行虚基类的,其他基类则按照声明派生类时的顺序依次执行),再执行成员对象的,最后执行自己的。3.5 请描述一个你熟悉的设计模式(Autodesk)3.6 在UML 中,聚合(aggregation)和组合(composition)有什么区别 Autodesk)答案:聚合关系更强,类似于pages 和book 的关系;组合关系要弱,类似于books和bookshelf 的关系。

3.7C#和C++除了语法上的差别以外,有什么不同的地方?(Autodesk,Microsoft)答案:(C#我只是了解,不是很精通)

(1)c#有垃圾自动回收机制,程序员不用担心对象的回收。(2)c#严禁使用指针,只能处理对象。如果希望使用指针,则仅可在unsafe 程序块中能使用指针。(3)c#只能单继承。(4)必须通过类名访问静态成员。不能像C++中那样,通过对象访问静态成员。(5)在子类中覆盖父

类的虚函数时必须用关键字override,覆盖父类的方法要用关键字new 3.8ADO.net 和ADO 的区别?

答案:实际上除了“能够让应用程序处理存储于DBMS 中的数据“这一基本相似点外,两者没有太多共同之处。但是ADO 使用OLE DB 接口并基于微软的COM 技术,而ADO.NET 拥有自己的ADO.NET 接口并且基于微软的.NET 体系架构。众所周知.NET 体系不同于COM 体系,ADO.NET 接口也就完全不同于ADO和OLE DB 接口,这也就是说ADO.NET 和ADO是两种数据访问方式。ADO.net 提供对XML 的支持。3.9 New delete 与malloc free 的区别(Autodesk)区别: 1.new 自动计算需要分配的空间,而malloc要手动计算分配的空间。2.new 是类型安全的,而malloc不是。

如: int * p = new double[3];//编译时能够检查出错误

int* p = malloc(n*sizeof(double));//编译时不能够检查出错误 3.malloc/free需要库文件支持,而new/delete不用。4.operator new 对应于malloc, 但operator new 可以重载,可以自定义内存分配策略,甚至不做内存分配。但malloc做不到。5.new 能为非内部数据分配动态内存,而malloc不能。

3.9.2那为什么有了new/delete,还要malloc/free呢?

3.10 #define DOUBLE(x)x+x(Autodesk)i = 5*DOUBLE(10); i 是多少?正确的声明是什么? 答案:i 为60。正确的声明是#define DOUBLE(x)(x+x)3.11 有哪几种情况只能用intialization list 而不能用assignment?(Autodesk)答案:当类中含有const、reference 成员变量;基类的构造函数都需要参数;类中含有其他类的成员对象,而该类的构造函数都需要参数。3.11 C++是不是类型安全的?(Autodesk)答案:不是。两个不同类型的指针之间可以强制转换。C#是类型安全的。3.12 main 函数执行以前,还会执行什么代码?(Autodesk)答案:全局对象的构造函数会在main 函数之前执行。

3.13 描述内存分配方式以及它们的区别。(Autodesk , Microsoft)答案:1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。

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

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

3.14 什么是虚拟存储器?virtual memory 怎样映射到physical memory?页面替换算法有哪些?(Microsoft)见操作系统 p238 页。掌握的页面替换算法NRU(最近不用),FIFO,第二次机会页面替换算法,LRU(最近最少使用算法)

3.15 有四个同样的容器,里面装满了粒数相同的药丸,正常药丸的质量为m,变质药丸的质量为m+1,现在已知这四个容器中,有一个装的全是变质药丸,用电子秤只称一次,找出哪个容器装的是变质药丸(Microsoft)

答案:把四个容器依次编号为1、2、3、4,然后从中分别取出1、2、3、4 粒药丸,称这10 粒药丸的质量,如果质量为10m+1,则说明第一个容器装的是变质药丸,如果为10m+2 则说明第二个装的变质药丸,依次类推。

3.16 比较一下C++中static_cast 和 dynamic_cast 的区别。(Autodesk)

dynamic_casts在帮助你浏览继承层次上是有限制的。它不能被用于缺乏虚函数的类型上,它被用于安全地沿着类的继承关系向下进行类型转换。如你想在没有继承关系的类型中进行转换,你可能想到static_cast 3.17 Struct 和class 的区别(Autodesk)答案:struct 中成员变量和成员函数默认访问权限是public,class 是private 3.18 当一个类A 中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少,如果不是零,请解释一下编译器为什么没有让它为零。(Autodesk)

答案:肯定不是零。我举个反例,如果是零的话,声明一个class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了 3.18 这道题我又找到答案了,为了确保每个对象都拥有唯一的地址!可查阅http://blog.csdn.net/smonster/articles/432767.aspx 3.19 在8086 汇编下,逻辑地址和物理地址是怎样转换的?(Intel)

答案:通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地址,就得到了真正要访问的地址。

3.20 描述一下C++的多态(microsoft)

答案:C++的多态表现在两个部分,一个是静态连编下的函数重载,运算符重载;动态连编下的虚函数、纯虚函数(抽象类)

4.写出BOOL,int,float,指针类型的变量a 与零的比较语句。答案:

BOOL : if(!a)int : if(a == 0)float : const EXPRESSION EXP = 0.000001 if(a < EXP && a >-EXP)pointer : if(a!= NULL)

5.请说出const 与#define 相比优点 答案:

(1)const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

(2)有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

6.简述数组与指针的区别

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

(1)修改内容上的差别 char a[] = “hello”;a[0] = ‘X’;

char *p = “world”;// 注意p 指向常量字符串

p[0] = ‘X’;// 编译器不能发现该错误,运行时错误(2)用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为

同类型的指针。

char a[] = “hello world”;char *p = a;cout<< sizeof(a)<< endl;// 12 字节 cout<< sizeof(p)<< endl;// 4 字节 计算数组和指针的内存容量 void Func(char a[100]){ cout<< sizeof(a)<< endl;// 4 字节而不是100 字节 }

7.类成员函数的重载、覆盖和隐藏区别 答案:

成员函数被重载的特征:

(1)相同的范围(在同一个类中);(2)函数名字相同;(3)参数不同;

(4)virtual 关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:(1)不同的范围(分别位于派生类与基类);(2)函数名字相同;(3)参数相同;

(4)基类函数必须有virtual 关键字。

“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

8.There are two int variables: a and b, don’t use “if”, “? :”, “switch” or other judgement statements, find out the biggest one of the two numbers.答案:((a + b)+ abs(a – b))/ 2

9.如何打印出当前源文件的文件名以及源文件的当前行号? 答案:

cout << __FILE__;cout<<__LINE__;__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

10.main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?

答案:可以,可以用_onexit 注册一个函数,它会在main 之后执行int fn1(void), fn2(void), fn3(void), fn4(void);

void main(void){ String str(“zhanglin”);_onexit(fn1);_onexit(fn2);_onexit(fn3);_onexit(fn4);printf(“This is executed first.n”);} int fn1(){ printf(“next.n”);return 0;} int fn2(){ printf(“executed ”);return 0;} int fn3(){ printf(“is ”);return 0;} int fn4(){ printf(“This ”);return 0;} The _onexit function is passed the address of a function(func)to be called when the program terminates normally.Successive calls to _onexit create a register of functions that are executed in LIFO(last-in-first-out)order.The functions passed to _onexit cannot take parameters.11.如何判断一段程序是由C 编译程序还是由C++编译程序编译的? 答案:

#ifdef __cplusplus cout<<“c++”;#else cout<<“c”;#endif

12.文件中有一组整数,要求排序后输出到另一个文件中 答案:

void Order(vector &data)//起泡排序

{ int count = data.size();int tag = false;for(int i = 0;i < count;i++){ for(int j = 0;j < count1;j++){ if(data[j] > data[j+1]){ tag = true;int temp = data[j];data[j] = data[j+1];data[j+1] = temp;} } if(!tag)break;} } void main(void){ vectordata;ifstream in(“c:data.txt”);if(!in){ cout<<“file error!”;exit(1);} int temp;while(!in.eof()){ in>>temp;data.push_back(temp);} in.close();Order(data);ofstream out(“c:result.txt”);if(!out){ cout<<“file error!”;exit(1);} for(i = 0;i < data.size();i++)out<

out.close();}

13.排序方法比较(intel)

排序方法平均时间 最坏时间 辅助存储

1:直接插入排序:插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。

2:起泡排序:依次比较相邻的两个数,将小数放在前面,大数放在后面(时间复杂度为O(n^2)

n2/2-n/2,)

3:选择排序:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法(4)快速排序;通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序

n log n(5)堆排序; 二叉树 堆排序的最坏时间复杂度为O(nlog2n)。(6)归并排序;

14.一个链表的结点结构 struct Node { int data;Node *next;};typedef struct Node Node;(1)已知链表的头结点head,写一个函数把这个链表逆序(Intel)Node * ReverseList(Node *head)//链表逆序 { if(head == NULL || head->next == NULL)return head;Node *p1 = head;Node *p2 = p1->next;Node *p3 = p2->next;p1->next = NULL;while(p3!= NULL){ p2->next = p1;p1 = p2;p2 = p3;p3 = p3->next;} p2->next = p1;head = p2;

return head;}(2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序。Node * Merge(Node *head1 , Node *head2){ if(head1 == NULL)return head2;if(head2 == NULL)return head1;Node *head = NULL;Node *p1 = NULL;Node *p2 = NULL;if(head1->data < head2->data){ head = head1;p1 = head1->next;p2 = head2;} else { head = head2;p2 = head2->next;p1 = head1;} Node *pcurrent = head;while(p1!= NULL && p2!= NULL){ if(p1->data <= p2->data){ pcurrent->next = p1;pcurrent = p1;p1 = p1->next;} else { pcurrent->next = p2;pcurrent = p2;p2 = p2->next;} } if(p1!= NULL)pcurrent->next = p1;if(p2!= NULL)pcurrent->next = p2;

return head;}(2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序,这次要求用递归方法进行。(Autodesk)答案:

Node * MergeRecursive(Node *head1 , Node *head2){ if(head1 == NULL)return head2;if(head2 == NULL)return head1;Node *head = NULL;if(head1->data < head2->data){ head = head1;head->next = MergeRecursive(head1->next,head2);} else { head = head2;head->next = MergeRecursive(head1,head2->next);} return head;}

15.分析一下这段程序的输出(Autodesk)class B { public: B(){ cout<<“default constructor”<

B Play(B b){ return b;} int main(int argc, char* argv[]){ B temp = Play(5);return 0;} 请自己执行一下看看。

16.写一个函数找出一个整数数组中,第二大的数(microsoft)答案:

const int MINNUMBER =-32767;int find_sec_max(int data[] , int count)//类似于1 4 4 4这样的序列将认为1是第二大数 { int maxnumber = data[0];int sec_max = MINNUMBER;for(int i = 1;i < count;i++){ if(data[i] > maxnumber){ sec_max = maxnumber;maxnumber = data[i];} else { if(data[i] > sec_max)sec_max = data[i];} } return sec_max;} 写一个在一个字符串中寻找一个子串第一个位置的函数

这个题目的一般算法比较简单我就不给出了,如果要求高效率的话请参见数据结构中的KMP 算法,不过在笔试时间有限情况下,写出那个算法还是挺难的。

一、#include “filename.h”和#include 的区别

#include “filename.h”是指编译器将从当前工作目录上开始查找此文件

#include 是指编译器将从标准库目录中开始查找此文件

二、头文件的作用

加强安全检测

通过头文件可能方便地调用库功能,而不必关心其实现方式

三、* , &修饰符的位置

对于*和&修饰符,为了避免误解,最好将修饰符紧靠变量名

四、if语句

不要将布尔变量与任何值进行比较,那会很容易出错的。

整形变量必须要有类型相同的值进行比较

浮点变量最好少比点,就算要比也要有值进行限制

指针变量要和NULL进行比较,不要和布尔型和整形比较

五、const和#define的比较

const有数据类型,#define没有数据类型

个别编译器中const可以进行调试,#define不可以进行调试

在类中定义常量有两种方式

1、在类在声明常量,但不赋值,在构造函数初始化表中进行赋值;

2、用枚举代替const常量。

六、C++函数中值的传递方式

有三种方式:值传递(Pass by value)、指针传递(Pass by pointer)、引用传递(Pass by reference)

void fun(char c)//pass by value

void fun(char *str)//pass by pointer

void fun(char &str)//pass by reference

如果输入参数是以值传递的话,最好使用引用传递代替,因为引用传递省去了临时对象的构造和析构

函数的类型不能省略,就算没有也要加个void

七、函数体中的指针或引用常量不能被返回

Char *func(void)

{

char str[]=”Hello Word”;

//这个是不能被返回的,因为str是个指定变量,不是一般的值,函数结束后会被注销掉

return str;

}

函数体内的指针变量并不会随着函数的消亡而自动释放八、一个内存拷贝函数的实现体

void *memcpy(void *pvTo,const void *pvFrom,size_t size)

{

assert((pvTo!=NULL)&&(pvFrom!=NULL));

byte *pbTo=(byte*)pvTo;//防止地址被改变

byte *pbFrom=(byte*)pvFrom;

while(size-->0)

*pbTo++ = *pbForm++;

return pvTo;

}

九、内存的分配方式

分配方式有三种,请记住,说不定那天去面试的时候就会有人问你这问题

1、静态存储区,是在程序编译时就已经分配好的,在整个运行期间都存在,如全局变量、常量。

2、栈上分配,函数内的局部变量就是从这分配的,但分配的内存容易有限。

3、堆上分配,也称动态分配,如我们用new,malloc分配内存,用delete,free来释放的内存。

十、内存分配的注意事项

用new或malloc分配内存时,必须要对此指针赋初值。

用delete 或free释放内存后,必须要将指针指向NULL

不能修改指向常量的指针数据

十一、内容复制与比较

//数组……

char a[]=”Hello Word!”;

char b[10];

strcpy(b,a);

if(strcmp(a,b)==0)

{}

//指针……

char a[]=”Hello Word!”;

char *p;

p=new char[strlen(a)+1];

strcpy(p,a);

if(strcmp(p,a)==0)

{}

十二、sizeof的问题

记住一点,C++无法知道指针所指对象的大小,指针的大小永远为4字节

char a[]=”Hello World!”

char *p=a;

count< count<

而且,在函数中,数组参数退化为指针,所以下面的内容永远输出为4

void fun(char a[1000])

{

count< }

十三、关于指针

1、指针创建时必须被初始化

2、指针在free 或delete后必须置为NULL

3、指针的长度都为4字节

4、释放内存时,如果是数组指针,必须要释放掉所有的内存,如

char *p=new char[100];

strcpy(p,”Hello World”);

delete []p;//注意前面的[]号

p=NULL;

5、数组指针的内容不能超过数组指针的最大容易。

如:

char *p=new char[5];

strcpy(p,”Hello World”);//报错 目标容易不够大

delete []p;//注意前面的[]号

p=NULL;

十四、关于malloc/free 和new /delete

l malloc/free 是C/C+的内存分配符,new /delete是C++的内存分配符。

l 注意:malloc/free是库函数,new/delete是运算符

l malloc/free不能执行构造函数与析构函数,而new/delete可以

l new/delete不能在C上运行,所以malloc/free不能被淘汰

l 两者都必须要成对使用

l C++中可以使用_set_new_hander函数来定义内存分配异常的处理

如何查出内存泄漏和非法操作的BUG(在Release版本下)? 检查window(release)下的内存泄漏

1、放置关键字 assert()

2、生成map 文件。它并不往可执行文件exe 中添加任何东西,只是在编译的时候将各个函数入口地址记录在后缀为.map的文件中,程序崩溃的时候可以得到一个EIP地址,通过地址知道崩溃所在函数

3、可以设置断点,在希望设置断点的地方加入 _ASM int 3

4、可以通过编译时的汇编程序看出

5、采用第三方工具

十五、C++的特性

C++新增加有重载(overload),内联(inline),Const,Virtual四种机制

重载和内联:即可用于全局函数,也可用于类的成员函数;

Const和Virtual:只可用于类的成员函数;

重载:在同一类中,函数名相同的函数。由不同的参数决定调用那个函数。函数可要不可要Virtual关键字。和全局函数同名的函数不叫重载。如果在类中调用同名的全局函数,必须用全局引用符号::引用。

覆盖是指派生类函数覆盖基类函数

函数名相同;

参数相同;

基类函数必须有Virtual关键字;

不同的范围(派生类和基类)。

隐藏是指派生类屏蔽了基类的同名函数相同

1、函数名相同,但参数不同,此时不论基类有无Virtual关键字,基类函数将被隐藏。

2、函数名相同,参数也相同,但基类无Virtual关键字(有就是覆盖),基类函数将被隐藏。

内联:inline关键字必须与定义体放在一起,而不是单单放在声明中。

Const:const是constant的缩写,“恒定不变”的意思。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

1、参数做输入用的指针型参数,加上const可防止被意外改动。

2、按值引用的用户类型做输入参数时,最好将按值传递的改为引用传递,并加上const关键字,目的是为了提高效率。数据类型为内部类型的就没必要做这件事情;如:

将void Func(A a)改为void Func(const A &a)。

而void func(int a)就没必要改成void func(const int &a);

3、给返回值为指针类型的函数加上const,会使函数返回值不能被修改,赋给的变量也只能是const型变量。如:函数const char*GetString(void);char *str=GetString()将会出错。而const char *str=GetString()将是正确的。

4、Const成员函数是指此函数体内只能调用Const成员变量,提高程序的键壮性。如声明函数 int GetCount(void)const;此函数体内就只能调用Const成员变量。

Virtual:虚函数:派生类可以覆盖掉的函数,纯虚函数:只是个空函数,没有函数实现体;

十六、extern“C”有什么作用?

Extern “C”是由C++提供的一个连接交换指定符号,用于告诉C++这段代码是C函数。这是因为C++编译后库中函数名会变得很长,与C生成的不一致,造成C++不能直接调用C函数,加上extren “c”后,C++就能直接调用C函数了。

Extern “C”主要使用正规DLL函数的引用和导出 和 在C++包含C函数或C头文件时使用。使用时在前面加上extern “c” 关键字即可。

十七、构造函数与析构函数

派生类的构造函数应在初始化表里调用基类的构造函数;

派生类和基类的析构函数应加Virtual关键字。

不要小看构造函数和析构函数,其实编起来还是不容易。

#include

class Base

{

public:

virtual ~Base(){ cout<< “~Base” << endl;}

};

class Derived : public Base

{

public:

virtual ~Derived(){ cout<< “~Derived” << endl;}

};

void main(void)

{

Base * pB = new Derived;// upcast

delete pB;

}

输出结果为:

~Derived

~Base

如果析构函数不为虚,那么输出结果为

~Base

十八、#IFNDEF/#DEFINE/#ENDIF有什么作用

仿止该头文件被重复引用

转http://bbs.csai.cn/bbs/view.asp?Id={8DB2582C-97E1-428A-AD9C-358BCD02C506

第三篇:C语言数据结构与指针

数据结构【第四次】实验报告

学院:

班级:

学号:

姓名:

实验四

(一)实验名称:C语言数据结构与指针

(二)实验目的:巩固复习前期所学C语言的函数参数传递、指针和结构体等知识点,加强学习数据结构语言基础。

(三)实验内容:

1)学生信息的显示,具体要求如下:

定义一个结构体描述学生信息(学号,姓名,性别,年龄,住址);

设计一个函数,用于显示单个学生信息,函数的参数为前面定义的结构体类型;

设计一个主函数,在主函数中输入学生的信息,并调用前面定义的函数进行显示(学生人数不少于5人)。

2)输入若干个整数作为数组元素值,然后按输入时顺序的就地逆置排序,最后打印出逆置后的元素值。要求用指针和动态内存分配方法实现。例如 输入:10 2 30 4 5,逆置后显示为:5 4 30 2 10。

(四)源代码:

#define MAXSIZE 100

#include #include typedef int ElemType;typedef struct {

ElemType data[MAXSIZE];int length;

} SqList;SqList l;

void InitList(SqList &L)

{

L.length = 0;} void CreatSqlist(SqList &L,int n)

{

printf(“请输入节点”);int i;for(i=0;i

} void Output(SqList &L)

{ int i;for(i=0;i

printf(“n”);} int chazhao(SqList &L,int x){ int i,k;printf(“n请输入你要查找的元素 x=?”);scanf(“%d”,&x);for(i=0;i<=(L.length+1);i++){

if(x==L.data[i])

{printf(“要查找的元素%d位于线性表第%d位上nn”,x,i+1);

k=0;

break;

} } if(k!=0)printf(“所要查找的元素%d不在线性表中”,x);return 0;} int GET(SqList &L,int i){ int m;if((i<0)||(i>L.length)){printf(“所查找范围超出线性表长度”);return 1;} else if((i>=1)&&(i<=L.length)){

m=L.data[i-1];}printf(“%d ”,m);return 0;} int DELETE(SqList &L,int i){ int j;if(i<1||i>L.length){printf(“删除错误”);return 0;} else {

for(j=i;j

L.data[j-1]=L.data[j];

L.length--;

} return 1;} int INSERT(SqList &L,int x,int i){ int j;if(L.length>=MAXSIZE-1){printf(“over flow”);return 1;} else if((i<1)||(i>L.length+1)){printf(“插入错误”);return 1;} else

{for(j=L.length;j>=i-1;j--)L.data[j+1]=L.data[j];L.data[i-1]=x;L.length=L.length+1;} return 0;} int main(){int n,i,k,x;InitList(l);printf(“请输入线性表的长度 ”);scanf(“%d”,&n);CreatSqlist(l,n);Output(l);

printf(“请输入你要查找的数所在的节点位置”);scanf(“%d”,&i);GET(l,i);chazhao(l,x);printf(“请输入你要删除元素的位置=?”);scanf(“%d”,&k);DELETE(l,k);Output(l);printf(“请输入你要插入的数和位置x,i=?”);scanf(“%d,%d”,&x,&i);INSERT(l,x,i);Output(l);return 0;}

(五)代码运行结果:

(六)需求分析

1、输入的形式和输出值的范围:1)输入10个整数。2)输出整个顺序线性表。

2、输出的形式:完成各种功能后的线性表。

3、程序所能达到的功能:1)所存储顺序线性表的显示、元素的查找、删除和插入。

(七)所用到的函数:

void CreatSqlist void Output Int chazhao int GET int INSERT int DELETE

(八)心得体会:

此次实验的过程中还是遇到了很多意想不到的问题,让我再一次深刻的体会到了理论和实践的差距。使我清楚的知道技术上的东西,细节更显得尤为重要和值得重视。困难虽有,但在我的努力下,最后还是成功完成了实验。总而言之,这次实验又增长了我不好知识。

第四篇:C语言内存分配

C语言变量声明及内存分配

一个由c/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。程序结束时由编译器自动释放。

2、堆区(heap)— 在内存开辟另一块存储区域。一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static)—编译器编译时即分配内存。全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(BSS)。-程序结束后由系统释放

4、文字常量区 —常量字符串就是放在这里的,程序结束后由系统释放。

5、程序代码区 —存放函数体的二进制代码。例子程序

这是一个前辈写的,非常详细 #include

#include //main.cpp

int a = 0;

//全局初始化区 char *p1;

//全局未初始化区 void main(){

int b=1;// 栈

char s[] = “abc”;//栈

char *p2;//栈

char *p3 = “123456”;//“123456”在常量区,p3在栈上。

static int c =0;//全局(静态)初始化区

p1 =(char *)malloc(10);

p2 =(char *)malloc(20);//分配得来得10和20字节的区域就在堆区。

strcpy(p1, “123456”);//123456放在常量区,编译器可能会将它与p3所指向的“123456”优化成一个地方

system(“pause”);

}

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

C语言程序的内存分配方式 1.内存分配方式

内存分配方式有三种:

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

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

[3]从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。

2.程序的内存空间

一个程序将操作系统分配给其运行的内存块分为4个区域,如下图所示。

一个由C/C++编译的程序占用的内存分为以下几个部分,1、栈区(stack)—

由编译器自动分配释放,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。其操作方式类似于数据结构中的栈。

2、堆区(heap)—

一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表。

3、全局区(静态区)(static)—存放全局变量、静态数据、常量。程序结束后由系统释放。

4、文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放。

5、程序代码区—存放函数体(类成员函数和全局函数)的二进制代码。

下面给出例子程序,int a = 0;//全局初始化区

char *p1;//全局未初始化区

int main(){

int b;//栈

char s[] = “abc”;//栈

char *p2;//栈

char *p3 = “123456”;//123456在常量区,p3在栈上。

static int c =0;//全局(静态)初始化区

p1 = new char[10];

p2 = new char[20];

//分配得来得和字节的区域就在堆区。

strcpy(p1, “123456”);//123456放在常量区,编译器可能会将它与p3所指向的“123456”优化成一个地方。

}

3.堆与栈的比较

3.1申请方式

stack: 由系统自动分配。例如,声明在函数中一个局部变量 int b;系统自动在栈中为b开辟空间。

heap: 需要程序员自己申请,并指明大小,在C中malloc函数,C++中是new运算符。

如p1 =(char *)malloc(10);p1 = new char[10];

如p2 =(char *)malloc(10);p2 = new char[20];

但是注意p1、p2本身是在栈中的。

3.2申请后系统的响应

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。

对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。

由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

3.3申请大小的限制

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

3.4申请效率的比较

栈由系统自动分配,速度较快。但程序员是无法控制的。

堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是栈,而是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。

3.5堆和栈中的存储内容

栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

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

3.6存取效率的比较

char s1[] = “a”;

char *s2 = “b”;

a是在运行时刻赋值的;而b是在编译时就确定的;但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。比如:

int main(){

char a = 1;

char c[] = “1234567890”;

char *p =“1234567890”;

a = c[1];

a = p[1];

return 0;

}

对应的汇编代码

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读取字符,显然慢了。3.7小结

堆和栈的主要区别由以下几点:

1、管理方式不同;

2、空间大小不同;

3、能否产生碎片不同;

4、生长方向不同;

5、分配方式不同;

6、分配效率不同;

管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。当然,这个值可以修改。

碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构。

生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由mallo函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果。

4.new/delete与malloc/free比较

从C++角度上说,使用new分配堆空间可以调用类的构造函数,而malloc()函数仅仅是一个函数调用,它不会调用构造函数,它所接受的参数是一个unsigned long类型。同样,delete在释放堆空间之前会调用析构函数,而free函数则不会。

class Time{

public:

Time(int,int,int,string);

~Time(){

cout<<“call Time’s destructor by:”<

}

private:

int hour;

int min;

int sec;

string name;

};

Time::Time(int h,int m,int s,string n){

hour=h;

min=m;

sec=s;

name=n;

cout<<“call Time’s constructor by:”<

}

int main(){

Time *t1;

t1=(Time*)malloc(sizeof(Time));

free(t1);

Time *t2;

t2=new Time(0,0,0,“t2”);

delete t2;

system(“PAUSE”);

return EXIT_SUCCESS;

}

结果:

call Time’s constructor by:t2

call Time’s destructor by:t2

从结果可以看出,使用new/delete可以调用对象的构造函数与析构函数,并且示例中调用的是一个非默认构造函数。但在堆上分配对象数组时,只能调用默认构造函数,不能调用其他任何构造函数。

第五篇:C语言指针实习

实习七:指针实习

一、实习目的

姓名:尹思智

学号:2012014413

完成日期:2013年4月

1.由键盘输入10个整数,将它们从小到大排序 2.将10个字符串(设其长度小于30)排序。

3.找出二维数组(设4行5列)中的最大数及其位置。

4.从键盘输入一串字符,从下标为m的字符开始,取出n个字符(m和n从键盘输入),形成一个新字符串 5.实现字符串的拷贝

6.编写一程序,将一个字符串反序存放。

二、实习步骤

1.由键盘输入10个整数,将它们从小到大排序 #include void main(){ int a[10],t,i;int *p;printf(“输入十个数:n”);for(p=a;p

scanf(“%d”,p);for(i=0;i<10;i++)for(p=a;p

if(*p>*(p+1))

{

t=*p;

*p=*(p+1);

*(p+1)=t;}

for(p=a;p

printf(“%d ”,*p);}

2、将10个字符串(设其长度小于30)排序 #include #include void main(){ char cty[10][30],*str[10],*temp;int i,j,k;for(i=0;i<10;i++)

str[i]=cty[i];printf(“输入十个字符串:n”);for(i=0;i<10;i++)

gets(cty[i]);for(i=0;i<9;i++){

k=i;

for(j=i+1;j<10;j++)

if(strcmp(str[k],str[j])>0)

k=j;

temp=str[k];

str[k]=str[i];

str[i]=temp;} printf(“排序后:n”);for(i=0;i<10;i++)

printf(“%sn”,str[i]);}

2.将10个字符串(设其长度小于30)排序。

3.找出二维数组(设4行5列)中的最大数及其位置。

4.从键盘输入一串字符,从下标为m的字符开始,取出n个字符(m和n从键盘输入),形成一个新字符串 5.实现字符串的拷贝

6.编写一程序,将一个字符串反序存放。

三、实习总结

下载delphi和c指针与内存操作指南word格式文档
下载delphi和c指针与内存操作指南.doc
将本文档下载到自己电脑,方便修改和收藏,请勿使用迅雷等下载。
点此处下载文档

文档为doc格式


声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:645879355@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。

相关范文推荐

    C语言实验报告《指针》

    学号:__________ 姓名:__________ 班级:__________ 日期:__________ 指导教师:__________ 成绩:__________实验五 指针一、实验目的1、掌握指针的概念、会定义和使用指针变量2、掌......

    c语言 二维数组与指针 教案

    26 讲授 9.6二维数组和指针 2课时掌握二维数组的地址表示方法 掌握指向数组元素的指针变量 掌握指向数组元素的指针变量 掌握用指向由m个元素组成的一维数组的指针变量 指......

    C语言指针的理解

    C_C++指针指针应用详解 前言:复杂类型说明 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里......

    C语言函数指针变量

    C语言函数指针变量 在C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针......

    C语言实验报告《指针》(5篇材料)

    学号:__________ 姓名:__________ 班级:__________ 日期:__________ 指导教师:__________ 成绩:__________实验五 指针一、 实验目的1、掌握指针的概念、会定义和使用指针变量2、......

    C语言结构体与指针实验(精选5篇)

    实验一 C语言结构体与指针 一、实验内容 1) 学生信息的显示,具体要求如下: 定义一个结构体描述学生信息(学号,姓名,性别,年龄,住址); 设计一个函数,用于显示单个学生信息,函数的参数......

    C语言指针经验总结(经典_非常详细_精品)

    C_C++指针指针应用详解 一、简单类型分析: int p; //这是一个普通的整型变量 int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的......

    史上最全C语言指针总结

    C语言中的精华是什么,答曰指针,这也是C语言中唯一的难点。 C是对底层操作非常方便的语言,而底层操作中用到最多的就是指针,以后从事嵌入式开发的朋友们,指针将陪伴我们终身。 本......