C语言中指针对内存操作的若干情况

目录

题目

背景概念梳理

传值调用

指向数组的指针

一级指针的调用

sizeof不能求得动态分配的内存的大小

二级指针的方式进行调用

解题过程

正确答案

整体代码

参考链接


题目

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

void Test(void)
{
    char *str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}

问程序执行后的结果是:

A,hello world
B,乱码
C,程序崩溃
D,结果未知

背景概念梳理

        本题的解决思路并不难(只看题解的话,可直接跳过本项,看后面的解题过程),但真正想关注的是通过这个demo梳理下C语言中指针调用内存的若干种可能。

传值调用

int GetMemory1(char *p)
{
	if ( p == NULL )
	{	
		int ret = 1;
		return ret;
	}
	p = (char *)malloc(100);
}
void Test1(void)
{
	char *str = NULL;
	int ret = 0;
	ret = GetMemory1(str);
	if(ret == 0)
	{
		strcpy(str, "hello world");
		printf("str:%d\n" ,*str);
	} 
	else
	{
		printf("Error!  str为NULL\n");
	}
}

//输出结果:
//Error!  str为NULL

        这种情况多见于在调用函数传值调用,且该段程序存在多个值得细究的地方,

        1,str作为一个字符指针类型,被初始化为了NULL,然后,将其作为参数,传入GetMemory1,这样做恐怕不妥,str=NULL(NULL其实就是0x0),表示的是 str指向了一个地址为0的内存,这就提醒别人不要对这个指针进行解引用(返回内存地址中存放的对象,str是地址,*str就是其内存地址中存放的对象)的操作。str=NULL后,str指向0x0这个地址。而0x0这个地址,我们是没有访问权限的。这就造成一旦出现对"*str"的操作,程序直接就会报异常。

        稳妥的做法是,在存在传入指针类型的参数的函数中,对指针变量做是否为NULL的判断,是NULL直接返回,非NULL则继续原有流程的处理。

        2,就是关于题解描述的,直接参考下节即可。

指向数组的指针

char *GetMemory2(void)
{
	char p[] = "hello world";
	printf("In Getm2:%s\n",p);
	return p;
}

void Test2(void)
{
	char *str = NULL;
	str = GetMemory2();
	printf("Out Getm2:%s\n",str);
}

//运行输出结果:
//In Getm2:hello world
//Out Getm2:P?

        这种情况出现的运行结果是乱码,在GetMemory2中,创建了一个p数组,最后将p返回,这个数组的数组元素为char,为在栈区为其分配存储空间,为临时变量,当GetMemory2运行结束,p数组的内容将被析构,返回的p,是指向一个已经被析构了的数组的首地址,结果未知,可能乱码。

一级指针的调用

char *GetMemory5(char *p, int num)
{
	p = (char *)malloc(sizeof(char)*num);
	return p;
}

void Test5(void)
{
	char *str;
	str = GetMemory5(str, 100);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	return 0;
}

//运行结果:
hello world
--------------------------------
Process exited after 0.02738 seconds with return value 11
请按任意键继续. . .

        正确执行,是以上错误的正确版,该方式也证明了另一个问题

//把 src 所指向的字符串复制到 dest。
char *strcpy(char *dest, const char *src)

        strcpy将一个src(短的源字符串)复制给长的dest(目的字符串)时,并将dest打印,dest中多余的部分并 不会被打印出来,而是直接输出字符串了。

sizeof不能求得动态分配的内存的大小

        这里引申出一个小点,“sizeof不能求得动态分配的内存的大小!”,我用malloc创建的变量,我想去看下分配的内存的大小,结果:

char *GetMemory3(char *p, int num)
{
	printf("sizeof(p):%d\n",sizeof(p));
	p = (char *)malloc(sizeof(char)*100);
	printf("sizeof(p):%d\n",sizeof(p));
	return p;
}

void Test3(void)
{
	char *str;
	printf("char * sizeof:%d\n",sizeof(char *)); 
	printf("赋值前_sizeof(str):%d\n",sizeof(str));
	str = GetMemory3(str, 100);
	printf("赋值前_sizeof(str):%d\n",sizeof(str));
	strcpy(str, "hello world");
	printf("赋值后_sizeof(str):%d\n",sizeof(str));
	printf(str);
	free(str);
	return 0;
}

//运行输出:
char * sizeof:8
赋值前_sizeof(str):8
sizeof(p):8
sizeof(p):8
赋值前_sizeof(str):8
赋值后_sizeof(str):8
hello world
--------------------------------
Process exited after 0.03138 seconds with return value 1
请按任意键继续. . .

        结果和想象的差距很大!只所以是8,是因为str是一个“char *”的指针类型,指针类型默认的大小为8吧(应该是int类型)。

二级指针的方式进行调用

void GetMemory4(char **p, int num) 
{ 
	*p = (char *)malloc(sizeof(char) * num); 
} 

void Test4(void)
{ 
	char *str=NULL; 
	GetMemory4(&str,100); 
	strcpy(str,"hello world"); 
	printf(str); 
	free(str);
}

//输出结果:
hello world
--------------------------------
Process exited after 0.0358 seconds with return value 1
请按任意键继续. . .

        正确输出,通过一级指针,如果不进行return,就会出现“”传值调用“”的错误,但如果是二级指针就不会出现,究分析一下,创建一个一级指针,然后把该指针的指针(二级指针)传给GetMemory4,(*p)表示函数内对二级指针所指向的内容,即一级指针,进行分配堆空间(先分配一个堆空间,后将该堆空间的首地址赋给一级指针),GetMemory4调用结束,所创建的变量析构,析构的是p,p是二级指针,但对应的一级指针并未受影响,而str就是一级指针,这个是在 传进GetMemory4之前,就已经确定的,故而这种情况下虽然同样是传值且没有返回,但依然可以生效。

解题过程

        过程很简单了,本题相关的函数规则:

        1,函数的形参和实参非同一个,需要通过return或引用(C++中的概念)将函数的处理结果返回到被调用的函数中;

        2,被调用的函数运行完后会析构调函数自身所创建的变量;

        3,malloc所创建的变量是在堆区,要有配套的free函数"手工"析构,

        依据这三点,就可以判断,GetMemory被Test调用,Test传入GetMemory的实参str,与GetMemory中实际运行的形参p,并非同一个变量,且当GetMemory运行完毕后没有任何参数返回,故当GetMemory运行完毕后,*str依旧是NULL,而指针变量p被新建且p得到新分配的空间地址,另外,由于p是malloc创建的,未对起进行free析构,存在内存泄露(但并不会引起程序的崩溃)。

        真正出问题的是"strcpy(str, "hello world");",因为*str是NULL,没有空间去承接字符串"hello world"的内容,导致异常,程序崩溃。

正确答案

        选C,程序崩溃

整体代码

#include<stdio.h>

int GetMemory1(char *p)
{
	if ( p == NULL )
	{	
		int ret = 1;
		return ret;
	}
	
	p = (char *)malloc(100);
	printf("getmem over!");
	int ret = 0;
	return ret;
}
void Test1(void)
{
	char *str = NULL;
	int ret = 0;
	ret = GetMemory1(str);

	if(ret == 0)
	{
		strcpy(str, "hello world");
		printf("str:%d\n" ,*str);
	} 
	else
	{
		printf("Error!  str为NULL\n");
	}
}

char *GetMemory2(void)
{
	char p[] = "hello world";
	printf("In Getm2:%s\n",p);
	return p;
}
void Test2(void)
{
	char *str = NULL;
	str = GetMemory2();
	printf("Out Getm2:%s\n",str);
}

char *GetMemory3(char *p, int num)
{
	printf("sizeof(p):%d\n",sizeof(p));
	p = (char *)malloc(sizeof(char)*100);
	printf("sizeof(p):%d\n",sizeof(p));
	return p;
}
void Test3(void)
{
	char *str;
	printf("char * sizeof:%d\n",sizeof(char *)); 
	printf("赋值前_sizeof(str):%d\n",sizeof(str));
	str = GetMemory3(str, 100);
	printf("赋值前_sizeof(str):%d\n",sizeof(str));
	strcpy(str, "hello world");
	printf("赋值后_sizeof(str):%d\n",sizeof(str));
	printf(str);
	free(str);
	return 0;
}


void GetMemory4(char **p, int num) 
{ 
	*p = (char *)malloc(sizeof(char) * num); 
} 
void Test4(void)
{ 
	char *str=NULL; 
	GetMemory4(&str,100); 
	strcpy(str,"hello world"); 
	printf(str); 
	free(str);
}

char *GetMemory5(char *p, int num)
{
	p = (char *)malloc(sizeof(char)*num);
	return p;
}
void Test5(void)
{
	char *str;
	str = GetMemory5(str, 100);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	return 0;
}

int main()
{
	//Test1(); //程序崩溃 
	//Test2(); //乱码 
	//Test3(); //正确 ,返回 
	Test4(); //使用二级指针,正确
	//Test5();	
} 

参考链接

Getmemory函数详解--内存操作的理解_jacky的博客-CSDN博客_getmemory题目:void GetMemory(char *p){p = (char *)malloc(100);}void Test(void){char *str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);}请问运行Test 函数会有什么样的结果?分析:程序崩溃。因为GetMemhttps://blog.csdn.net/u011217649/article/details/52853059C语言指针(关于定义指针为NULL)深入了解_大C的博客-CSDN博客_指针为nullC语言指针中:指针是C语言最重要的概念之一,用于储存变量的地址。&amp;是取地址运算符,*是间接运算符。(C语言中:%p是输出地址的转换说明)。“*号在定义变量时,表示类型是指针,如 int *p = NULL 表示这是一个叫p的指针; *号在运算时,表示取指针指向地址的内容。首先要说的是:非堆分配的内存是不需要free的。再说p=NULL;指针的变化是 p指向了一个地址为0的内存,这...https://blog.csdn.net/henu1710252658/article/details/83046649C/C++刁钻问题各个击破之细说sizeof_w57w57w57的专栏-CSDN博客摘要:Sizeof的作用非常简单:求对象或者类型的大小。然而sizeof又非常复杂,它涉及到很多特殊情况,本篇把这些情况分门别类,总结出了sizeof的10个特性:(0)sizeof是运算符,不是函数;(1)sizeof不能求得void类型的长度;(2)sizeof能求得voidhttps://blog.csdn.net/w57w57w57/article/details/6626840

Guess you like

Origin blog.csdn.net/qq_17846375/article/details/121904094