程序分析-以下程序中主函数能否成功申请到内存空间?

138 阅读5分钟

程序解析

  1. 以下程序中,主函数能否成功申请到内存空间?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmemory(char *p){
    p = (char *)malloc(100);
    strcpy (p, "hello world");
}

int main()  
{  
    char *str = NULL;  
    getmemory(str);  
    printf("%s\n", str);  
    free(str);  
    return 0;  
} 

主函数不能成功申请到内存空间。

  • 在getmemory函数中,通过malloc函数申请了100字节的内存空间,并将其地址赋给p。然而,在主函数中调用getmemory函数时,传递的是str的值,而不是str的地址。因此,getmemory函数中的p只是一个局部变量,对其进行赋值并不会改变主函数中str的值。因此,在主函数中打印str时,仍然是NULL,而不是"hello world"。

  • 同时,在主函数中使用free函数释放str所指向的内存空间是不正确的,因为str的值为NULL,没有成功申请到内存空间。

  • 改正有如下俩种方法:

①传递的是二重指针,即str的指针
void getmemory(char **p)   
{  
    *p = (char *)malloc(100);  
    strcpy(*p, "hello world");  
}  
②传递的是指针别名,即str的别名,C++中
void getmemory(char * &p)   
{  
    p = (char *)malloc(100);  
    strcpy(p, "hello world");  
}  

  • 在第一个修改中,getmemory函数的参数是一个二重指针,即指向指针的指针。通过传递str的地址给getmemory函数,可以在函数内部修改str的值。在getmemory函数中,使用p来访问str指向的地址,并将malloc函数分配的内存空间的地址赋值给p,然后使用strcpy函数将字符串"hello world"复制到这个内存空间中。这样,主函数中的str指向的内存空间就被成功分配,并且包含了字符串"hello world"。因此,在主函数中打印str时,会输出"hello world"。

  • 在第二个修改中,getmemory函数的参数是指针别名,即传递str的别名给getmemory函数。在C++中支持指针别名,而在C语言中不支持。通过传递str的别名给getmemory函数,可以在函数内部直接修改str的值。在getmemory函数中,直接将malloc函数分配的内存空间的地址赋值给p,然后使用strcpy函数将字符串"hello world"复制到这个内存空间中。这样,主函数中的str指向的内存空间就被成功分配,并且包含了字符串"hello world"。因此,在主函数中打印str时,会输出"hello world"。

需要注意的是,第一个修改是在C语言中使用的,而第二个修改是在C++中使用的。在C语言中,不能直接传递指针的别名,只能通过二重指针的方式来修改指针的值。而在C++中,可以直接传递指针的别名来修改指针的值。

  1. 解释以下内容: 请问运行下面的Test()函数会有什么样的后果?
char *GetMemory(void){  
    char p[] = "hello world"return p;
 }   
void Test(void)   {   
    char *str = NULL;   
    str = GetMemory(); 
    printf("%s\n", str);   
 }
  • 运行上述代码会导致未定义的行为。原因是在函数GetMemory()中,定义了一个局部变量p,它是一个指向字符数组的指针。然后将p作为返回值返回给Test()函数中的str指针。但是,由于p是在GetMemory()函数的栈帧中分配的,当GetMemory()函数返回后,p所指向的内存将被释放,因此str指针将指向一个无效的内存地址。

  • 在Test()函数中,当尝试打印str指针指向的字符串时,由于str指向的内存已经被释放,将会导致未定义的行为,可能会输出乱码或者程序崩溃。

  • 为了解决这个问题,可以通过动态分配内存来存储字符串,并在使用完后手动释放内存。例如,可以使用malloc()函数来分配内存,并在使用完后使用free()函数释放内存。

请写出更改后的代码,并加以解释:

#include <stdio.h>
#include <stdlib.h>

char *GetMemory(void)
{
    char *p = (char *)malloc(sizeof(char) * 12);
    if (p != NULL) {
        strcpy(p, "hello world");
    }
    return p;
}

void Test(void)
{
    char *str = NULL;
    str = GetMemory();
    if (str != NULL) {
        printf("%s\n", str);
        free(str);
    }
}

以下是更改后的代码:

#include <stdio.h>
#include <stdlib.h>

char *GetMemory(void)
{
    char *p = (char *)malloc(sizeof(char) * 12);
    if (p != NULL) {
        strcpy(p, "hello world");
    }
    return p;
}

void Test(void)
{
    char *str = NULL;
    str = GetMemory();
    if (str != NULL) {
        printf("%s\n", str);
        free(str);
    }
}
  • 在修改后的代码中,我们使用了动态内存分配函数malloc()来分配足够的内存来存储字符串"hello world"。通过sizeof(char) * 12来分配12个字节的内存,包括字符串中的每个字符和一个空字符'\0'。

  • GetMemory()函数中,我们首先检查malloc()是否成功分配内存。如果成功,则使用strcpy()函数将字符串"hello world"复制到分配的内存中。然后将指向该内存的指针p返回给Test()函数。

  • Test()函数中,我们首先将str指针初始化为NULL,然后调用GetMemory()函数来获取分配的内存。接下来,我们检查str是否为NULL,以确保内存分配成功。如果成功,则打印str指向的字符串,并使用free()函数释放分配的内存。

通过这种方式,我们确保了在使用完动态分配的内存后,手动释放内存,避免了悬空指针和内存泄漏的问题。

  1. 请问运行下面的 Test() 函数会有什么样的后果?
void Test(void)  
{  
    char *str = (char *) malloc(100);  
    strcpy(str,"hello");  
    free(str);       
    if(str != NULL)  
    {  
        strcpy(str, "world");   
        printf("%s\n", str);  
    }  
} 

答案: 篡改堆区野指针指向的内容,后果难以预料,非常危险。

解读:

(1)free(str);之后,str成为野指针,没有置为NULL,if(str != NULL)语句不能阻止篡改操作。

(2)野指针不是NULL指针,是指向被释放的或者访问受限的内存的指针。

(3)造成野指针原因:

  • ①指针变量没有被初始化,任何刚创建的指针不会自动成为NULL;
  • ②指针被free或delete之后,没有置NULL;
  • ③指针操作超越了变量的作用范围,比如要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放