manbetx官方网站

opebet网址如出一辙道华为笔试题 ,内存相关。内存分配。

十月 1st, 2018  |  manbet体育登录

脚一截先后,请说明出口结果:

从今正点原子论坛里看看底等同篇稿子,mak 一下,感触颇挺!

int _tmain(int argc, _TCHAR* argv[])
{
  int *p1,*p2,value;
  p1=(int*)0x500;
  p2=(int*)0x518;
  value=p2-p1;
  printf(“%d\n”, value);
  return 0;
}

地址:http://www.openedv.com/forum.php?mod=viewthread&tid=26805&highlight=%C4%DA%B4%E6%B9%DC%C0%ED

 结果为6.

  程序员们常常编写内存管理程序,往往提心吊胆。如果非思触雷,唯一的解决办法就是发现持有潜伏的地雷并且消除它们,躲是隐形不了的。本文的内容比较一般教科书之只要深入得几近,读者需要密切阅读,做到真地会内存管理。

(0x518-0x500)/sizeof(int) =6

内存分配办法

0x0018/4=6

  内存分配方式有三种植:

 

  (1)从静态存储区域分配。内存在程序编译的下便都分配好,这块内存在程序的全体运行期间还有。例如全局变量,static变量。

void GetMemory(char *p)
{
p=(char*)malloc(100);
}

  (2)在栈上创建。在执行函数时,函数内片变量的存储单元都得于栈上创建,函数执行完毕时这些存储单元自动为放。栈内存分配运算内置于处理器的一声令下集中,效率非常高,但是分配的内存容量有限。

void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str,”helloworld”);
printf(str);
}
借问运行Test函数会来怎样的结果?
答:程序崩溃。因为GetMemory并无可知传递动态内存,Test函数中的str一直都是NULL。strcpy(str,”helloworld”);将使程序崩溃。

  (3)
从堆积如山上分红,亦如动态内存分配。程序于运行的时用malloc或new申请任意多少的内存,程序员自己背在何时用free或delete释放内存。动态内存的生存期由咱们决定,使用非常灵活,但问题吗不过多。

char *GetMemory(void)
{
char p[]=”helloworld”;
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);

大的内存错误连同对策

试问运行Test函数会生哪些的结果?
报经:可能是乱码。因为GetMemory返回的凡据于“栈内存”的指针,该指针的地方不是NULL,但该原的始末就于免去,新内容无克。 

  发生内存不当是起很麻烦的事情。编译器不能自动发现这些错,通常是以程序运行时才会捕捉到。而这些错误大多没有明显的病症,时隐时现,增加了纠错的难度。有时用户怒气冲冲地拿您摸来,程序却不曾发出其他问题,你一样走,错误又犯了。
常见的内存错误连同对策如下:

 

  * 内存分配未成功,却以了她。

 

  编程新手常犯这种似是而非,因为他们从来不察觉及内存分配会无成事。常因此解决办法是,在以内存之前检查指针是否也NULL。如果指针p是函数的参数,那么当函数的入口处因此assert(p!=NULL)进行

#include   <iostream.h>  
   
  class   A  
  {  
  unsigned   char   i;  
  virtual   f()   {};  
  };  
   
  class   B   :   public   A  
  {  
  };  
   
  class   C   :   virtual   public   A  
  {  
  };  
   
  class   D   :   public   B,   public   C  
  {  
  };  
   
  void   main()  
  {  
  cout   <<   “A:   ”   <<   sizeof(A)   <<   endl;
 
  cout   <<   “B:   ”   <<   sizeof(B)   <<   endl;
 
  cout   <<   “C:   ”   <<   sizeof(C)   <<   endl;
 
  cout   <<   “D:   ”   <<   sizeof(D)   <<   endl;
 
  }  
  结果为  
  A:   8  
  B:   8  
  C:   12  
  D:   20  
   
  A:1   char+3bit(补位)=4bit+1   ptr=8bit  
  B:sizeof(B)=0+sizeof(A)=8bit  
  C:sizeof(A)+1   ptr=12bit  
  D:sizeof(B)+sizeof(C)=20bit    

  检查。如果是为此malloc或new来申请内存,应该为此if(p==NULL)
或if(p!=NULL)进行防错处理。

 

  * 内存分配虽然打响,但是没有初始化就引述它。

 

  犯这种错误主要有零星单起因:一凡尚未初始化的价值观;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。内存的缺省初值究竟是呀并没统一之正儿八经,尽管有些时候吧零值,我们宁可信其无不可信其发。所以无论是用何种方法开创数组,都变忘了授予初值,即便是赋予零值也不行省略,不要烦麻烦。

  * 内存分配成功而都初始化,但操作越过了内存的边界。

  例如当使用数组时经常发出下标“多1”或者“少1”的操作。特别是在for循环语句被,循环次数很爱下手错,导致数组操作越界。

  * 忘记了释放内存,造成内存泄露。

  含有这种错误的函数每于调用一不成就不见一块内存。刚开头经常系统的内存充足,你看不到错误。终有平等差程序突然死掉,系统出现提示:内存耗尽。

  动态内存的提请以及自由得配对,程序中malloc与free的使次数一定要一如既往,否则势必起误(new/delete同理)。

  * 释放了外存却继续应用它。
 
  有三栽状态:

  (1)程序中之目标调用关系过度复杂,实在麻烦将懂有对象究竟是否业已放出了内存,此时应有再设计数据结构,从根本上解决对象管理的杂乱局面。

  (2)函数的return语词写错了,注意不要回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时让机关销毁。

  (3)使用free或delete释放了内存后,没有用指针设置也NULL。导致有“野指针”。

  【规则1】用malloc或new申请内存之后,应该就检查指针值是否也NULL。防止利用指针值为NULL的内存。

  【规则2】不要忘记为数组和动态内存赋初值。防止将非受初始化的内存作为右值使用。

  【规则3】避免数组或指针的下标越界,特别而当心发生“多1”或者“少1”操作。

  【规则4】动态内存的申请和释放要配对,防止内存泄漏。

  【规则5】用free或delete释放了内存之后,立即用指针设置为NULL,防止有“野指针”。

指南针与数组的相比

  C
/C程序中,指针和数组在不少地方得相互替换着用,让人闹同样栽错觉,以为两者是相等价格的。

  数组要么在静态存储区被创造(如全局数组),要么在栈上被创造。数组名对承诺着(而非是据为)一片内存,其地址与容量在生命期内保障不转换,只发反复组的情节可以改。

  指针可以随时对任意档次的外存块,它的特点是“可转换”,所以我们常用指针来操作动态内存。指针远较数组灵活,但为还危急。

  下面为字符串为条例比较指针与高频组的特征。

  3.1 修改内容

  示例3-1面临,字符数组a的容量是6个字符,其情也hello。a的内容可以转,如a[0]=
‘X’。指针p指向常量字符串“world”(位于静态存储区,内容也world),常量字符串的情节是未得以为改动的。从语法上看,编译器并无看言
p[0]=
‘X’有什么不妥,但是该语句企图修改常量字符串的内容如果致运行错误。

  1. char a[] = “hello”;
  2. a[0] = ‘X’;
  3. cout << a << endl;
  4. char *p = “world”; // 注意p指向常量字符串
  5. p[0] = ‘X’; // 编译器不能够觉察该错误
  6. cout << p << endl;

复制代码

演示3.1 修改数组和指针的情节

  3.2 内容复制与比

  不克针对数组名为进行直接复制与比较。示例7-3-2负,若想管数组a的始末复制给数组b,不克因此言语
b = a
,否则用发出编译错误。应该用规范库函数strcpy进行复制。同理,比较b和a的情是否一致,不可知就此if(b==a)
来判断,应该用规范库函数strcmp进行比较。

  语句p = a
并无可知将a的情节复制指针p,而是把a的地点给给了p。要想复制a的始末,可以优先用库函数malloc为p申请一片容量也strlen(a)
1只字符的内存,再用strcpy进行字符串复制。同理,语句if(p==a)
比较的无是内容而是地址,应该用库函数strcmp来比较。

  1. // 数组…
  2. char a[] = “hello”;
  3. char b[10];
  4. strcpy(b, a); // 不能用 b = a;
  5. if(strcmp(b, a) == 0) // 不能用 if (b == a)
  6. // 指针…
  7. int len = strlen(a);
  8. char *p = (char *)malloc(sizeof(char)*(len 1));
  9. strcpy(p,a); // 不要用 p = a;
  10. if(strcmp(p, a) == 0) // 不要用 if (p == a)

复制代码

演示3.2 数组和指针的始末复制与于

  3.3 计算内存容量

  用运算符sizeof可以测算出数组的容量(字节数)。示例7-3-3(a)中,sizeof(a)的价值是12(注意别忘了’’)。指针p指向a,但是
sizeof(p)的价值也是4。这是因sizeof(p)得到的是一个指南针变量的字节数,相当给sizeof(char*),而休是p所指的内存容量。
C /C语言没有艺术知道指针所倚的内存容量,除非在报名外存时记住它们。

  注意当数组作为函数的参数进行传递时,该数组自动退化为和种类的指针。示例7-3-3(b)中,不论数组a的容量是多少,sizeof(a)始终当sizeof(char
*)。

  1. char a[] = “hello world”;
  2. char *p = a;
  3. cout<< sizeof(a) << endl; // 12字节
  4. cout<< sizeof(p) << endl; // 4字节

复制代码

以身作则3.3(a) 计算数组和指针的内存容量

  1. void Func(char a[100])
  2. {
  3.  cout<< sizeof(a) << endl; // 4字节而休是100字节
  4. }

复制代码

演示3.3(b) 数组退化为指针

指南针参数是哪些传递内存的?

  如果函数的参数是一个指针,不要期待用该指针去报名动态内存。示例7-4-1挨,Test函数的报告句GetMemory(str,
200)并从未使str获得期望的内存,str依旧是NULL,为什么?

  1. void GetMemory(char *p, int num)
  2. {
  3.  p = (char *)malloc(sizeof(char) * num);
  4. }
  5. void Test(void)
  6. {
  7.  char *str = NULL;
  8.  GetMemory(str, 100); // str 仍然为 NULL
  9.  strcpy(str, “hello”); // 运行错误
  10. }

复制代码

示范4.1 试图用指针参数申请动态内存

  毛病来在函数GetMemory中。编译器总是要呢函数的每个参数制作临时副本,指针参数p的副本是
_p,编译器使 _p =
p。如果函数体内的次第修改了_p的始末,就造成参数p的内容作相应的改。这就是指针可以当做输出参数的原由。在本例中,_p申请了初的内存,只是把
_p所指的内存地址改变了,但是p丝毫未移。所以函数GetMemory并无克出口任何事物。事实上,每执行同一坏GetMemory就见面泄露一片内存,因为没用free释放内存。

  如果非得而用指针参数去申请内存,那么当改用“指向指针的指针”,见示例4.2。

  1. void GetMemory2(char **p, int num)
  2. {
  3.  *p = (char *)malloc(sizeof(char) * num);
  4. }
  5. void Test2(void)
  6. {
  7.  char *str = NULL;
  8.  GetMemory2(&str, 100); // 注意参数是 &str,而无是str
  9.  strcpy(str, “hello”);
  10.  cout<< str << endl;
  11.  free(str);
  12. }

复制代码

示范4.2据此对指针的指针申请动态内存

  由于“指向指针的指针”这个概念不爱掌握,我们好用函数返回值来传递动态内存。这种方式更加简约,见示例4.3。

  1. char *GetMemory3(int num)
  2. {
  3.  char *p = (char *)malloc(sizeof(char) * num);
  4.  return p;
  5. }
  6. void Test3(void)
  7. {
  8.  char *str = NULL;
  9.  str = GetMemory3(100);
  10.  strcpy(str, “hello”);
  11.  cout<< str << endl;
  12.  free(str);
  13. }

复制代码

演示4.3 用函数返回值来传递动态内存

  用函数返回值来传递动态内存这种办法虽然好用,但是经常有人将return语句子用擦了。这里强调并非为此return语词返回指向“栈内存”的指针,因为该内存在函数结束时自动消失,见示例4.4。

  1. char *GetString(void)
  2. {
  3.  char p[] = “hello world”;
  4.  return p; // 编译器将提出警告
  5. }
  6. void Test4(void)
  7. {
  8.  char *str = NULL;
  9.  str = GetString(); // str 的始末是废品
  10.  cout<< str << endl;
  11. }

复制代码

示范4.4 return语句子返回指向“栈内存”的指针

  用调试器逐步跟踪Test4,发现执行str =
GetString语句后str不再是NULL指针,但是str的情节未是“hello
world”而是垃圾。
倘若拿示例4.4改写成示例4.5,会怎么样?

  1. char *GetString2(void)
  2. {
  3.  char *p = “hello world”;
  4.  return p;
  5. }
  6. void Test5(void)
  7. {
  8.  char *str = NULL;
  9.  str = GetString2();
  10.  cout<< str << endl;
  11. }

复制代码

示范4.5 return语句子返回常量字符串

  函数Test5运行虽然非见面拧,但是函数GetString2的规划概念倒是是错误的。因为GetString2外的“hello
world”是常量字符串,位于静态存储区,它当程序生命期内固定不更换。无论什么时调用GetString2,它回到的总是暨一个“只念”的外存块。

杜绝“野指针”

  “野指针”不是NULL指针,是凭借于“垃圾”内存的指针。人们一般不会见错用NULL指针,因为用if语句很易看清。但是“野指针”是特别凶险的,if语句对其不起作用。
“野指针”的成因主要出星星点点栽:

  (1)指针变量没有吃初始化。任何指针变量刚让创造时无会见活动成为NULL指针,它的短省值是不管三七二十一的,它会乱靠一气。所以,指针变量在创立的同时应当给初始化,要么拿指针设置为NULL,要么为它们对合法的内存。例如

  1. char *p = NULL;
  2. char *str = (char *) malloc(100);

复制代码

(2)指针p被free或者delete之后,没有置为NULL,让丁误以为p是个官方的指针。

  (3)指针操作逾了变量的来意范围。这种状况被人口防不胜防,示例程序如下:

  1. class A
  2. {
  3.  public:
  4.   void Func(void){ cout << “Func of class A” << endl;
    }
  5. };
  6. void Test(void)
  7. {
  8.  A *p;
  9.  {
  10.   A a;
  11.   p = &a; // 注意 a 的生命期
  12.  }
  13.  p->Func(); // p是“野指针”
  14. }

复制代码

函数Test以实行报告句p->Func()时,对象a已经没有,而p是乘向a的,所以p就改成了“野指针”。但奇怪的是我运行此程序时竟然没错,这也许同编译器有关。

内存耗尽怎么收拾?

  如果以提请动态内存时寻找不至足够深之外存块,malloc和new将回NULL指针,宣告内存申请破产。通常有三种植办法处理“内存耗尽”问题。

  (1)判断指针是否为NULL,如果是虽然就用return语句子终止本函数。例如:

  1. void Func(void)
  2. {
  3.  A *a = new A;
  4.  if(a == NULL)
  5.  {
  6.   return;
  7.  }
  8.  …
  9. }

复制代码

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

  1. void Func(void)
  2. {
  3.  A *a = new A;
  4.  if(a == NULL)
  5.  {
  6.   cout << “Memory Exhausted” << endl;
  7.   exit(1);
  8.  }
  9.  …
  10. }

复制代码

(3)为new和malloc设置非常处理函数。例如Visual C
可以据此_set_new_hander函数为new设置用户自己定义之大处理函数,也可让malloc享用与new相同的雅处理函数。详细内容请参考C
使用手册。

  上述(1)(2)方式采取最广。如果一个函数内发生多处得申请动态内存,那么方式(1)就显示心有余而力不足(释放内存很辛苦),应该为此智(2)来处理。

  很多总人口未忍心用exit(1),问:“不修出错处理程序,让操作系统自己解决执行不行?”

  不行。如果有“内存耗尽”这样的事体,一般说来应用程序已经无药品可救。如果无用exit(1)
把大程序杀死,它恐怕会见害死操作系统。道理如同:如果非将歹徒击毙,歹徒于老死之前见面作下重多之罪。

  有一个深要紧的景象如果告知大家。对于32位以上之应用程序而言,无论怎样使用malloc与new,几乎不可能造成“内存耗尽”。我在Windows
98下用Visual C
编写了测试程序,见示例7。这个顺序会无终止地运行下去,根本未会见告一段落。因为32员操作系统支持“虚存”,内存用完了,自动用硬盘空间顶替。我光闻硬盘嘎吱嘎吱地响,Window
98已经累得对键盘、鼠标毫无反应。

  我可以得出这样一个结论:对于32位以上之应用程序,“内存耗尽”错误处理程序毫无用处。这生而将Unix和Windows程序员们笑坏了:反正错误处理程序不起作用,我就算未写了,省了过多劳动。

  我无思量误导读者,必须强调:不加错误处理将导致程序的色非常不同,千万不可因为有些去好。

  1. void main(void)
  2. {
  3.  float *p = NULL;
  4.  while(TRUE)
  5.  {
  6.   p = new float[1000000];
  7.   cout << “eat memory” << endl;
  8.   if(p==NULL)
  9.    exit(1);
  10.  }
  11. }

复制代码

演示7试图耗尽操作系统的内存

malloc/free 的采用要

  函数malloc的原型如下:

  1. void * malloc(size_t size);

复制代码

为此malloc申请一片长为length的平头类型的内存,程序如下:

  1. int *p = (int *) malloc(sizeof(int) * length);

复制代码

咱该将注意力集中在点滴单要素上:“类型转换”和“sizeof”。

  * malloc返回值的型是void
*,所以于调用malloc时如果显式地进行类型转换,将void *
转换成所用的指针类型。

  *
malloc函数本身并无辨而申请的内存是什么种,它只关心内存的总字节数。我们普通记不住int,
float等数据类型的变量的确切字节数。例如int变量在16位系统下是2个字节,在32号生是4单字节;而float变量在16各类系统下是4只字节,在32各生呢是4独字节。最好用以下顺序作同样不好测试:

  1. cout << sizeof(char) << endl;
  2. cout << sizeof(int) << endl;
  3. cout << sizeof(unsigned int) << endl;
  4. cout << sizeof(long) << endl;
  5. cout << sizeof(unsigned long) << endl;
  6. cout << sizeof(float) << endl;
  7. cout << sizeof(double) << endl;
  8. cout << sizeof(void *) << endl;

复制代码

每当malloc的“()”中行使sizeof运算符是良好的风格,但要是当心有时我们会眩晕了条,写起
p = malloc(sizeof(p))这样的程序来。

  * 函数free的原型如下:

  1. void free( void * memblock );

复制代码

怎么free
函数不象malloc函数那样复杂呢?这是因指针p的品类以及它们所据的内存的容量事先都是知情的,语句free(p)能对地放内存。如果p是
NULL指针,那么free对p无论操作多少坏还未会见发问题。如果p不是NULL指针,那么free对p连续操作简单不行就见面促成程序运行错误。

局部心得体会

  不少技术不错的C
/C++程序员,很少有人能够碰上拍胸脯说会指针与内存管理(包括自家自己)。我早期学习C语言时特别恐惧指针,导致我出第一单下软件(约1万实施C代码)时尚未运用一个指南针,全用数组来替指针,实在蠢笨得过于。躲避指针不是道,后来本人改写了之软件,代码量缩小到本的一半。

  (1)越是怕指针,就越是设运指针。不见面是使用指针,肯定算不达到是合格的程序员。

  (2)必须养成“使用调试器逐步跟踪程序”的惯,只有如此才会窥见题目的实质。

相关文章

标签:,

Your Comments

近期评论

    功能


    网站地图xml地图