C语言进阶篇-01内存分配原理

2023-10-17 15:55:08 浏览数 (1)

C语言内存分配中,主要重点讲解栈区和堆区

栈区

栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。

栈区内容会在调用完自动释放

代码语言:javascript复制
int * myFunc()
{
	int a = 10;
	return &a;
}

void test01()
{
	//局部变量a早已被释放,因此我们没有权限操作这块内存空间
	int * p = myFunc();
	printf("*p = %dn", *p);
	printf("*p = %dn", *p);
}

 所以上面代码在打印结果的时候无法输出正确的地址,可能第一次会打印正确,那也只是系统优化了,第二次输出还是会表现错误

下面看另一种情况

在下面的代码中,变量 str 是在 getString() 函数的栈帧中分配的局部变量,其生命周期仅限于函数调用过程中。因此,当函数返回时,str 将被销毁,其内存地址也将被回收。

当我们在 test02() 函数中调用 getString() 函数并将其返回值赋给指针 p 时,p 指向的是一个已经被销毁的字符串。这样的指针被称为“悬挂指针”,使用它将导致未定义的行为。

str会指向常量区"hello world" 的地址,但是随着而后将会被销毁

代码语言:javascript复制
char* getString()
{
	char str[] = "hello world";
	return str;
}

void test02()
{
	char* p = NULL;
	p = getString();
	printf("%sn", p);
}

要解决这个问题,您可以将 str 定义成一个静态变量或动态分配内存,这样可以在函数返回后仍然存在。以下是一个使用静态变量的示例:

代码语言:javascript复制
char* getString()
{
    static char str[] = "hello world";
    return str;
}

void test02()
{
    char* p = NULL;
    p = getString();
    printf("%sn", p);
}

或者您也可以使用动态分配内存的方式:

代码语言:javascript复制
char* getString()
{
    char* str = (char*)malloc(sizeof(char) * 12);
    strcpy(str, "hello world");
    return str;
}

void test02()
{
    char* p = NULL;
    p = getString();
    printf("%sn", p);
    free(p); // 记得释放内存
}

堆区

堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

 下面用一个代码,给直观的感受:由于分配了内存,所以不会被自然释放。

代码语言:javascript复制
int * getSpace()
{
	int * p = malloc(sizeof(int)* 5);
	if (p == NULL)
	{
		return;
	}
	for (int i = 0; i < 5; i  )
	{
		p[i] = i   100;
	}
	return p;
}

void test01()
{
	int * p = getSpace();
	for (int i = 0; i < 5; i  )
	{
		printf("%dn", p[i]);
	}

	//手动开辟  手动释放
	free(p);
	p = NULL;

}
堆区注意事项

我们使用了堆的操作,但是不能打印出hello world,

代码语言:javascript复制
void allocateSpace(char * pp)
{
	char * temp  =  malloc(100);
	memset(temp, 0, 100);
	strcpy(temp, "hello world");
	pp = temp;
}

void test02()
{
	char * p = NULL;
	allocateSpace(p);
	printf("%sn", p);
}

allocateSpace() 函数中,我们在堆上分配了一段内存,并将 "hello world" 写入该内存。然后,我们将 temp 指向的内存地址赋给了 pp,但 pp 是一个指向 test02() 函数中局部变量 p 的指针,我们并没有改变 p 的指向,而是改变了 pp 的指向,因此 test02() 函数中的 p 指针仍然是 NULL。

test02() 函数中,我们尝试打印 p 指针所指向的字符串,但由于 p 仍然是 NULL,因此打印的结果也是不确定的,有可能是一个空字符串,也有可能是其他未定义的内容。

在pp = temp之后,pp的地址为hello world的地址,但是没有影响到char *p 

为了解决这个问题,我们应该使用指向指针的指针,这样可以修改 test02() 函数中 p 指针的指向,使其指向 allocateSpace() 函数中分配的内存。修改后的代码如下:

代码语言:javascript复制
void allocateSpace(char **pp)
{
    char *temp = malloc(100);
    memset(temp, 0, 100);
    strcpy(temp, "hello world");
    *pp = temp;
}

void test02()
{
    char *p = NULL;
    allocateSpace(&p);
    printf("%sn", p);
}

0 人点赞