这是C/C++中程序内存区域划分图:

数据段:也叫静态数据段或初始化数据段,用于存储程序中的全局变量和静态变量,这些变量在程序启动时就已经分配好内存空间并初始化。 代码段:也叫文本段或指令段,用于存储程序的可执行指令代码。 这部分内存区域通常是只读的,程序在运行时不能修改代码段中的内容。
我们先来看下面的一段代码和相关问题
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}char2在哪里?A *char2在哪里?_A pChar3在哪里?A *pChar3在哪里?D ptr1在哪里?A *ptr1在哪里?B
globalVar 和 staticGlobalVar 都存储在数据段(静态区)中。全局变量globalVar 的生命周期贯穿整个程序的执行过程,直到程序结束,静态全局变量 staticGlobalVar 的作用域仅限于当前源文件,其生命周期也贯穿整个程序的执行过程。
staticVar 是静态局部变量,也存储在数据段(静态区)中。
localVar 是普通的局部变量,存储在栈中,栈是一种后进先出(LIFO)的数据结构,用于存储函数调用时的局部变量和返回地址等信息,当函数调用结束时,栈中分配给该函数的内存空间会被自动释放。
num1 存储在栈中,数组在内存中是连续分布的,因此 num1 占用了一块连续的栈空间。
*char2 和 char2 在栈中,
*char2:char2[] 是一个局部字符数组,存储在栈上。当你使用字符串字面量初始化它时,编译器会在栈上分配足够的内存空间,并将字符串字面量的内容(包括结尾的 \0)复制到这块内存中,所以 *char2 指向的是存储在栈上的可修改的字符数组。
*pChar3:const char* pChar3 = "abcd"; 中的字符串字面量 "abcd" 存储在只读的数据段(常量区)中。而pChar3 本身是一个指针变量,存储在栈上,它指向常量区中的字符串。由于字符串字面量是只读的,所以通过 *pChar3 我们只能读取字符串的内容,而不能修改它。

*pChar3 在栈中, pChar3 在代码段(常量区),指针变量 pChar3 存储在栈中,*pChar3 指向一个字符串常量,该字符串常量存储在代码段(常量区)中,代码段(常量区)用于存储程序中的常量数据,如字符串常量、枚举常量等。这些常量在程序执行期间不会被修改。
ptr1 是局部指针变量,存储在栈上
*ptr1 指向的内容,就是malloc分配的内存,该内存在堆上
总结:
填空题: sizeof(num1) = ____; sizeof(char2) = ____; strlen(char2) = ____; sizeof(pChar3) = ____; strlen(pChar3) = ____; sizeof(ptr1) = ____;
sizeof(num1) = 40;
num1 是一个包含 10 个 int 类型元素的数组,每个 int 类型占 4 个字节,所以数组大小为 10 * 4 = 40 字节。sizeof(char2) = 5; strlen(char2) = 4; char2 是一个包含 5 个字符(包括结尾的 '\0')的字符数组,所以 sizeof(char2) 为 5 字节。strlen(char2) 返回字符串的长度,不包括结尾的 '\0',所以为 4。sizeof(pChar3) = 8; strlen(pChar3) = 4; pChar3 是一个指向字符串常量 "abcd" 的指针,在 32 位系统上,指针大小为 4 字节。在 64 位系统上,指针大小为 8 字节。strlen(pChar3) 返回字符串的长度,不包括结尾的 '\0',所以为 4。sizeof(ptr1) = 8; ptr1 是一个指向动态分配的 int 类型数组的指针,在 32 位系统上,指针大小为 4 字节。在 64 位系统上,指针大小为 8 字节。sizeof 和 strlen 区别?
sizeof和strlen是两个不同的操作符/函数,sizeof是一个编译时操作,返回变量或数据类型的大小;而strlen是一个运行时函数,返回字符串的长度。
sizeof 是一个操作符,用于获取变量或数据类型的大小(以字节为单位),它在编译时就确定了返回值,不需要在运行时计算,对于数组,sizeof 返回整个数组的大小,而不是单个元素的大小,对于指针,sizeof 返回指针本身的大小,而不是它所指向的对象的大小。示例:
char str[] = "hello";
printf("Size of str: %zu\n", sizeof(str)); // 输出: 6 (包括'\0')
printf("Size of char: %zu\n", sizeof(char)); // 输出: 1strlen 是一个函数,用于计算字符串的长度(不包括结尾的 '\0' 字符),它在运行时计算字符串的长度,需要遍历整个字符串,对于数组,strlen 只能用于字符数组(字符串),不能用于其他类型的数组,对于指针,strlen 可以计算指针所指向的字符串的长度。
示例:char str[] = "hello";
printf("Length of str: %zu\n", strlen(str)); // 输出: 5void* malloc (size_t size);
功能:动态分配指定大小的内存块,并返回指向该内存块的指针, 分配的内存块内容是未初始化的。
使用方法:int* ptr = (int*)malloc(sizeof(int) * 4);
if (ptr == NULL)
{
// 内存分配失败,处理错误
return;
}
// 使用分配的内存
// ...
free(ptr); // 释放内存void* calloc (size_t num, size_t size);
功能:动态分配指定数量和大小的内存块,并返回指向该内存块的指针,分配的内存块内容会被初始化为0。
使用方法:// 分配 4 个 int 型元素的内存,并初始化为 0
int *ptr = (int *)calloc(4, sizeof(int));
if (ptr == NULL) {
// 内存分配失败,处理错误
return;
}
// 使用分配的内存,所有元素都被初始化为 0
// ...
free(ptr); // 释放内存void* realloc (void* ptr, size_t size);
功能:调整已分配内存块的大小,并返回指向新内存块的指针。 ptr为NULL,则等同于malloc(size)。
使用方法:// 先分配 4 个 int 型元素的内存
int *ptr = (int *)malloc(4 * sizeof(int));
if (ptr == NULL)
{
// 内存分配失败,处理错误
return;
}
// 使用分配的内存
// ...
// 重新分配为 8 个 int 型元素的内存
int *new_ptr = (int *)realloc(ptr, 8 * sizeof(int));
if (new_ptr == NULL)
{
// 内存重新分配失败,处理错误
free(ptr); // 释放原有内存
return;
}
ptr = new_ptr; // 更新指针
// 使用新分配的内存
// ...
free(ptr); // 释放内存void free (void* ptr);
功能:释放动态分配的内存块,将其返回给操作系统。注意:必须确保释放的内存块是之前使用malloc/calloc/realloc动态分配的。 ptr为NULL,则该函数不执行任何操作。
使用方法:int *ptr = (int *)malloc(4 * sizeof(int));
if (ptr == NULL)
{
// 内存分配失败,处理错误
return;
}
// 使用分配的内存
// ...
free(ptr); // 释放内存
// 不能再访问已释放的内存常见注意要点:
NULL指针,需要进行错误处理。C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
#include<stdlib.h>
int main()
{
int* ptr = (int*)malloc(4 * sizeof(int));
free(ptr);
int* ptr2 = (int*)calloc(4, sizeof(int));
//判断是否成功开辟空间,每个还需要检查
int* ptr3 = (int*)realloc(ptr, 8 * sizeof(int));
free(ptr3);
return 0;
}在 C++ 中,new 和 delete 操作符用于动态内存分配和释放。当使用这些操作符时,需要注意以下几点:
内置类型:
int、double、char 等),使用 new 和 delete 操作符与使用 malloc 和 free 函数的效果是相同的。 int* ptr = new int; // 分配一个 int 类型的内存空间
delete ptr; // 释放 ptr 指向的内存空间分配内存,但没有初始化

// 动态申请一个int类型的空间并初始化为10
int* ptr2 = new int(10);
delete ptr2;
动态申请10个int类型的空间,并释放
int* arr = new int[10]; // 动态申请10个int类型的空间
delete[] arr; // 释放 arr 指向的数组内存空间当然,我们也可以开辟空间的时候,又进行初始化
#include<iostream>
using namespace std;
int main()
{
// 动态申请一个int类型的空间并初始化为10
int* ptr3 = new int[10]{ 2,3,4,5,5 };
delete[] ptr3;
return 0;
}这样一部分初始化想要的值,后面默认初始化为0

new和 delete操作符时,编译器会自动调用构造函数和析构函数,但对于内置类型来说,这些函数是空操作。

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。

C语言构造链表节点的方式:
struct ListNode
{
ListNode* _next;
int _data;
};
struct ListNode* LTCreateNode(int x)
{
struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->_data = x;
newnode->_next = NULL;
return newnode;
}这是C++的实现:
struct ListNode
{
ListNode* _next;
int _data;
ListNode(int data)
:_next(nullptr)
, _data(data)
{}
};
前面我们知道new不仅会开空间,还会调用构造函数,析构函数的目的是初始化,delete会调用析构函数,因此即使是自定义类型,也可以使用new开空间并初始化。 因此,只要我们写好构造函数,new的使用是真香啊