C语言之内存管理与函数返回值的一些小问题

一、内存管理

在C语言中,定义了四个内存区间:代码区、全局变量与静态变量区、局部变量区即栈区、动态存储区即堆区。

1.代码区:代码区内存放程序代码,属性为只读的。

2.全局变量与静态变量区:该区域也称之为静态存储区域。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。主要保存全局变量,静态变量和字符串常量,分配在这个区域中的变量,只有当程序结束的时候才会将内存释放。因此,经常利用这样的变量在函数间传递信息。

3.栈区:在执行函数时,函数内部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。

4.堆区:有些操作对象只有在程序运行时才能确定,这样编译器就无法为他们预先分配内存空间,只能在程序运行时分配,所以被称之为动态分配。

二、动态内存的申请和释放

当程序运行到需要一个动态分配的变量时,必须向系统申请取得堆中的一块所需大小的存储空间,用于存储该变量。当不再使用该变量时,也就是它的生命结束时。这样做法的优点在于,当内存申请之后,变量就具有全局变量的特性,生命周期不会随着函数的结束而结束。需要我们自己手动释放才会结束变量的生命,并且释放变量的内存后,该变量不再占有之前的内存空间,节省内存空间。

内存申请函数:函数原型如下

#include <stdlib.h>

void *malloc(size_t size);

其中参数size代表需要动态申请的内存字节数。若申请成功则返回申请到的内存起始地址。失败则返回NULL。

注意要点:

1.函数的返回值是void型的指针,需要我们自己对其进行强制转换成所需类型;

2.堆区不会在自动分配时做初始化(包括清零),所以在动态申请内存后,需要我们自己对申请的内存进行初始化操作。

3.申请到的内存块必定是连续的,且申请到的实际内存有可能比需要申请的大,但是不可能比需要申请的小。

 

内存释放函数:函数原型如下

#include <stdlib.h>

void free(void *ptr);

函数的参数ptr为需要释放的内存的起始地址。该函数没有返回值。

注意要点:

1.释放内存的时候必须提供内存块的起始地址,不能提供部分地址,内存释放的时候是按块释放且是按照申请的块来释放的,不能局部释放。因此必须保存好malloc返回的指针,若丢失,则分配的对空间无法释放回收,称之为内存泄漏。

2.malloc和free需要配对使用,对同一个内存块多次调用free,可能会错误释放后面申请的内存块,导致后面申请的内存失效。

 

二、函数返回值的一些问题

下面介绍的一些内容,由于没有对C语言进行很全面的学习,所以具体的专业名词应该怎么称呼我也太明白,部分内容没看懂的,可以看看上一篇文章         C语言函数的参数传递

https://blog.csdn.net/qq_35600620/article/details/103835512

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

char *test1(char *p);
//函数功能,给传入函数的指针指向“hello world”
//并且返回一个指向 “hello world” 的指针

int main(int argc, const char *argv[])
{
	char s1[200] = "test 1";
	char s2[200] = "test 2";

	char *p = s1;
	char *q = NULL;

	
	q = test1(p);

	printf("p = %s  q = %s\n", p, q);


	return 0;
}

char *test1(char *p)
{
	char temp[] = "hello world \n";
	char *q = NULL;

	p = temp;
	q = temp;

	printf("p: %s \n", p);
	printf("q: %s \n", q);


	return q;

}

程序中,指针初始化后p指向“test  1”,q指向空,函数作用是将传入函数的指针p指向“hello world”,并且返回一个指向"hello world "的指针变量。

实际程序执行结果如下:

p: hello world 
q: hello world 
p = test 1  q = ��v�P�

我们可以看到,在函数体内,传入指针p,和函数内自定义的指针q,确实都指向了“hello world”,但是执行完调用函数以后,p依然指向 “test 1”,而函数返回的指针q,指向的确实乱码,这是怎么回事?

1.指针p依然指向 “test 1”是因为该调用函数参数传递方式本质是值传递,实参的值复制到形参上,因此形参怎么变化都不会影响到实参,若想形参能影响实参,参考上一篇博客。

2.返回指针变量q为什么指向的是乱码。这个是因为函数体内定义的temp数组,该数组的存储在栈当中,当函数结束调用后,存储单元会自动释放,所以q指针指向了乱码,需要注意的是,这样的调用情况偶尔会得到正确的结果。需要测试的话,在函数体内将temp数组定义改为下面这样即可。

char temp[200] = "hello world";

下面讲解怎么让函数q能正确返回需要的数据,关于指针p的在这里就不介绍,注意看指针q的值

1.将temp数组定义为静态变量

char *test1(char *p)
{
	static char temp[] = "hello world";
	char *q = temp;

	p = temp;
	q = temp;

	printf("p: %s \n", p);
	printf("q: %s \n", q);

	return q;
}

调用函数改为这样的话,程序执行后的结果如下:

p: hello world 
q: hello world 
p = test 1  q = hello world

这里能看到函数返回的指针变量q确实能正确的指向“hello world”。原因在于我们将temp定义为静态变量,这样数据temp的存储单元就是在全局变量与静态变量区里面。当程序运行以后,系统不会自动自动改变改区域内变量的值。

2.将temp定义为指向字符串常量的指针

char *test1(char *p)
{
 	char* temp = "hello world";
	char *q = temp;

	p = temp;
	q = temp;

	printf("p: %s \n", p);
	printf("q: %s \n", q);

	return q;
}

该种方法注意的是,temp是指向字符串常量的指针。字符串是常量,其存储空间也是在全局变量与静态变量区内。

3.利用malloc函数,申请内存块

char *test1(char *p)
{
 	char* temp = (char*)malloc(sizeof(20)); 

    mesemt(temp, 0, 20);

	strcpy(temp, "hello world");
	

	printf("temp: %s \n", temp);

	return temp;
}

程序执行结果为:

temp: hello world 
p = test 1  q = hello world

该种方法能实现的原因在于,我们使用malloc申请内存块去存在“hello world”,该存储单元处于堆区,当我们申请成功后,只有我们自己释放,该存储单元才会释放,系统不会自动释放。

个人总结一下,这些问题我们需要注意的是,你对变量的存储区要根据其功能规划好,以及参数的传递方式需要掌握。不然很容易出现bug。有返回值的函数需要利用返回值的话,则一定要使用return语句,否则同样容易出错,对于非void型函数无return语句将怎么返回数据,个人也不是很了解就不在这里讲解,须记住,记得写好return就没事。

仓促成文,不当之处,尚祈方家和读者批评指正。联系邮箱[email protected]

发布了12 篇原创文章 · 获赞 5 · 访问量 643

猜你喜欢

转载自blog.csdn.net/qq_35600620/article/details/103840579
今日推荐