void getMemeory1(char *p) { p = (char *)malloc(100*sizeof(char)); } char * getMemeory2() { char p[100] = "Hello world"; return p; } int main() { //test1 char *str1 = NULL; getMemeory1(str1); strcpy(str1, "Hello world"); printf("str1 = %s\n", str1); //test2 char *str2 = NULL; str2 = getMemeory2(); printf("str2 = %s\n", str2); } 执行结果:test1程序报错,test2打印乱码或者“Hello world”(一般乱码) 结果分析: test1: 在函数中给指针分配空间,实际上是给指针的临时变量分配空间,函数结束后,这个临时变量也消亡,而str仍然为NULL,没有为其分配空间, 此时strcpy()是肯定会出错的。 test2: 1、可能是乱码,也有可能是正常输出,因为GetMemory返回的是指向“栈内存”的指针,该指针的地址 不是NULL,但其原来的内容已经被清除,新内容不可知,程序员面试宝典里有专门讲该部分知识的。 2、因为p的生命周期在GetMemory函数执行完了就被销毁了,str 指向的是个野指针。 //相关分析资料摘录 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. } 复制代码 示例1.1 试图用指针参数申请动态内存 毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把 _p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。 如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见示例1.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. } 复制代码 示例1.2用指向指针的指针申请动态内存 由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。这种方法更加简单,见示例1.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. } 复制代码 示例1.3 用函数返回值来传递动态内存 用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return语句用错了。这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡,见示例1.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. } 复制代码 示例1.4 return语句返回指向“栈内存”的指针 用调试器逐步跟踪Test4,发现执行str = GetString语句后str不再是NULL指针,但是str的内容不是“hello world”而是垃圾。 如果把示例1.4改写成示例1.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. } 复制代码 示例1.5 return语句返回常量字符串 函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。 2、杜绝“野指针” “野指针”不是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就成了“野指针”。但奇怪的是我运行这个程序时居然没有出错,这可能与编译器有关。 二 、面向对象七大原则 https://www.cnblogs.com/sunflower627/p/4718702.html
c++ 面试题
最新推荐文章于 2024-08-11 17:12:46 发布