面经——C语言——指针大小,内存分配,野指针,大小端
文章目录
- 指针大小
- 分段
- C语言内存分配
- 1. 静态内存分配
- 1.1 栈内存分配
- 1.2 数据段内存分配
- 2. 动态内存分配
- 2.1 `malloc`(Memory Allocation)
- 2.2 `free`(Free Memory)
- 动态内存分配的流程
- 例子
- 野指针
- 野指针的原因:
- 防止野指针的做法:
- 大小端
- 如何检测当前机器的字节序?
- 指针法
- 联合体法
指针大小
在C++中,指针的大小是与编译器平台相关的,通常与操作系统和架构的位数有关。具体来说,指针的大小通常取决于计算机的字长(即操作系统和处理器的位数),常见的情况如下:
32位系统:指针大小通常为4字节(32位)。
64位系统:指针大小通常为8字节(64位)。
分段
内存区域 | 描述 | 特点 |
---|---|---|
代码段 | 存储程序的可执行指令(代码)。 | 只读,防止程序修改自身指令;多个进程共享,节省内存空间。 |
数据段 | 存储已初始化的全局变量和静态变量。 | 可读写,程序启动时加载到内存,存储已初始化的全局和静态变量。 |
BSS段 | 存储未初始化的全局变量和静态变量,启动时初始化为零。 | 不存储数据,只分配空间;程序启动时操作系统将其初始化为零。 |
堆 | 用于动态分配内存,程序员在运行时手动管理。 | 动态分配和释放内存;大小可随程序需求变化;需使用malloc 和free 等函数。 |
栈 | 存储函数的局部变量、参数和函数调用时的上下文信息。 | 后进先出(LIFO);每次函数调用时分配栈帧,函数返回时弹出栈帧。 |
C语言内存分配
在C语言中,内存分配通常分为两种方式:静态内存分配和动态内存分配。这两种方式的主要区别在于内存分配的时间和方式。
1. 静态内存分配
静态内存分配是指在程序编译时就已经确定了内存的大小和位置,通常在栈和数据段中进行。
1.1 栈内存分配
栈内存分配用于存储函数的局部变量。栈内存分配是自动的,内存大小在编译时已确定。当函数调用时,局部变量会在栈上分配内存,函数返回时,栈上的内存会被自动释放。
void example() {
int local_var = 10; // local_var在栈上分配内存
}
1.2 数据段内存分配
数据段用于存储全局变量、静态变量和常量。数据段内存的大小和位置在程序编译时已确定,且内存通常在程序的生命周期内存在。
int global_var = 100; // 全局变量,在数据段分配内存
void example() {
static int static_var = 200; // 静态变量,在数据段分配内存
}
2. 动态内存分配
动态内存分配是指在程序运行时,根据需要动态地分配和释放内存。动态内存分配主要通过标准库中的malloc
、calloc
、realloc
和free
函数来进行。
2.1 malloc
(Memory Allocation)
malloc
用于分配指定大小的内存块,返回一个指向该内存块的指针。该内存块的内容未初始化,可能包含随机数据。
int *ptr = (int *)malloc(sizeof(int) * 10); // 分配10个整数的内存
if (ptr == NULL) {
// 内存分配失败,处理错误
}
2.2 free
(Free Memory)
free
用于释放之前分配的动态内存。释放后,指针仍然存在,但它不再指向有效的内存块。因此,释放后应该将指针设置为NULL
,以避免悬空指针的错误。
free(ptr); // 释放之前分配的内存
ptr = NULL; // 防止悬空指针
动态内存分配的流程
- 使用
malloc
或calloc
分配内存。 - 检查返回的指针是否为
NULL
,以确认内存是否分配成功。 - 使用动态分配的内存进行计算或存储数据。
- 使用完毕后,通过
free
释放内存,以避免内存泄漏。
例子
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr;
int n = 5;
// 动态分配内存
arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
printf("内存分配失败\n");
return 1;
}
// 使用动态分配的内存
for (int i = 0; i < n; i++) {
arr[i] = i * 10;
}
// 打印内容
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 释放内存
free(arr);
arr = NULL; // 防止悬空指针
return 0;
}
野指针
野指针是指向一个已经释放或未初始化的内存区域的指针。
野指针的原因:
-
未初始化的指针:
在使用指针之前没有给它分配有效的内存地址,指针的值是随机的,可能指向一个无效的位置。int *ptr; // ptr未初始化 *ptr = 10; // 错误,野指针
-
释放内存后继续使用指针:
释放指针所指向的内存后,如果继续访问该内存,指针就变成了野指针。int *ptr = malloc(sizeof(int)); // 动态分配内存 free(ptr); // 释放内存 *ptr = 10; // 错误,ptr变为野指针
-
指针指向的内存已被回收:
当一个局部变量的作用域结束时,其内存会被释放。如果指针指向该局部变量的地址,它就成为了野指针。int *ptr; { int x = 10; ptr = &x; // ptr指向局部变量x } // x的内存被回收 *ptr = 20; // 错误,ptr成为野指针
防止野指针的做法:
-
初始化指针:
指针在声明时应该初始化,避免指向随机内存。int *ptr = NULL; // 初始化为NULL
-
避免使用已释放的内存:
在调用free
释放内存后,应将指针设置为NULL
,防止指针继续访问无效内存。free(ptr); ptr = NULL; // 防止野指针
-
检查指针的有效性:
在使用指针之前,检查其是否指向有效的内存地址,特别是动态分配内存时。if (ptr != NULL) { *ptr = 10; }
通过这些方法,可以有效地避免和处理野指针问题,提升程序的稳定性和安全性。
大小端
大端是指数据的高字节存储在低地址处,低字节存储在高地址处。即,数据的高位字节在前,低位字节在后。
小端是指数据的低字节存储在低地址处,高字节存储在高地址处。即,数据的低位字节在前,高位字节在后。
如何检测当前机器的字节序?
指针法
在C++中,可以通过以下方法检测当前机器是大端还是小端:
#include <iostream>
int main() {
int num = 1;
char *ptr = (char *)#
if (*ptr == 1) {
std::cout << "小端\n";
} else {
std::cout << "大端\n";
}
return 0;
}
联合体法
#include <stdio.h>
union EndianTest {
int num;
unsigned char byte;
};
int main() {
union EndianTest test;
test.num = 0x12345678; // 设置一个整数值
// 查看bytes数组的第一个字节
if (test.byte == 0x78) {
printf("小端 (Little Endian)\n");
} else if (test.byte== 0x12) {
printf("大端 (Big Endian)\n");
}
return 0;
}