核心考点
getmemory 相关的题目几乎总是围绕以下几个核心考点展开:

(图片来源网络,侵删)
- 函数参数传递机制:C 语言中,函数参数是“值传递”(Pass-by-Value),这意味着传递给函数的是变量的副本,而不是变量本身。
- 指针的本质:指针本身就是一个变量,它存储的是一个内存地址,对指针的修改(改变它指向的地址)不会影响到指针外部的变量。
- 动态内存分配:使用
malloc(或calloc) 在堆上分配内存,这块内存的生命周期从malloc开始,直到free结束,与函数的生命周期无关。 - 内存泄漏:如果动态分配的内存没有被
free,它将一直存在,直到程序结束,造成内存泄漏。 - 野指针:指向一块“已释放”或“未初始化”内存的指针,使用野指针会导致未定义行为,通常是程序崩溃。
经典笔试题及解析
下面我们通过几个层层递进的经典题目来理解这些考点。 一:最经典的“陷阱”题
代码:
#include <stdio.h>
#include <stdlib.h>
void getmemory(char *p) {
p = (char *)malloc(sizeof(char) * 100);
// 假设 malloc 成功
}
int main() {
char *str = NULL;
getmemory(str);
strcpy(str, "hello world"); // 危险!
printf("%s\n", str);
free(str); // 会崩溃吗?
return 0;
}
问题:
strcpy(str, "hello world");这行代码会成功吗?为什么?free(str);会引发问题吗?整个程序的行为是怎样的?
解析:

(图片来源网络,侵删)
-
strcpy会失败,导致程序崩溃。- 原因: C 语言是值传递,在
main函数中调用getmemory(str)时,传递的是指针str的值,也就是NULL的副本。 - 在
getmemory函数内部,p被初始化为这个副本,即p也等于NULL。 p = (char *)malloc(...);这行代码,它修改的是getmemory函数内部的局部变量p,它让p指向了新分配的内存地址。- 这个修改完全不会影响到
main函数中的str。main函数中的str依然是NULL。 strcpy(str, "hello world");相当于strcpy(NULL, "hello world");,这会导致段错误,程序崩溃。
- 原因: C 语言是值传递,在
-
free(str);不会执行,或者执行后没有意义。- 由于程序已经在
strcpy处崩溃了,free(str);这行代码根本不会被执行。 - 即使程序没有崩溃(如果
str初始不是NULL),free(str)释放的也是main函数中的str所指向的内存(可能是NULL,也可能是一块未知的内存),而不是getmemory中malloc出来的那块内存,这会导致严重的内存泄漏。
- 由于程序已经在
这段代码存在双重问题:既使用了空指针导致崩溃,又存在内存泄漏。
如何修正?(使用指针的指针)

(图片来源网络,侵删)
问题: 如何修改 getmemory 函数,使其能够真正地修改 main 函数中的 str 指针,让它指向新分配的内存?
修正方法一:传递指针的指针
代码:
#include <stdio.h>
#include <stdlib.h>
// 参数改为 char **p,即一个指向指针的指针
void getmemory_fixed(char **p) {
*p = (char *)malloc(sizeof(char) * 100); // 解引用 p,修改 main 函数中的 str
}
int main() {
char *str = NULL;
getmemory_fixed(&str); // 传递 str 的地址
if (str != NULL) {
strcpy(str, "hello world");
printf("%s\n", str); // 输出: hello world
free(str);
str = NULL; // 良好习惯,将指针置为 NULL
}
return 0;
}
解析:
main函数: 我们传递的是&str(str的地址)。str是一个char*,&str的类型是char**。getmemory_fixed函数: 它的参数现在是char **p。p本身是一个指针,它指向main函数中的str。- *`p = ...
** 这行代码的含义是“获取p所指向的内容(也就是main中的str),然后将这个内容(str)赋值为新分配的内存地址”,这样就成功地修改了main函数中的str`。 - 内存管理: 在
main函数中,我们使用完内存后,必须free(str),并且将str置为NULL,以避免成为野指针。
另一种修正方法(返回指针)
修正方法二:让函数返回动态分配的内存地址
代码:
#include <stdio.h>
#include <stdlib.h>
// 函数返回一个 char* 指针
char *getmemory_return() {
char *p = (char *)malloc(sizeof(char) * 100);
return p; // 返回新分配的内存地址
}
int main() {
char *str = NULL;
str = getmemory_return(); // 直接接收返回值
if (str != NULL) {
strcpy(str, "hello world");
printf("%s\n", str); // 输出: hello world
free(str);
str = NULL;
}
return 0;
}
解析:
- 这种方法更简洁、更符合 C 语言的常见实践。
getmemory_return函数负责分配内存,并通过return语句将这块内存的地址“带”出来。main函数通过赋值操作str = getmemory_return();来接收这个地址。- 同样,
main函数也承担着free的责任。
更隐蔽的陷阱(返回栈地址)
代码:
#include <stdio.h>
char *getmemory_stack() {
char p[] = "hello world"; // p 是一个在栈上的数组
return p; // 返回栈变量的地址
}
int main() {
char *str = NULL;
str = getmemory_stack();
printf("%s\n", str); // 可能输出 "hello world",但结果是未定义的!
return 0;
}
问题: 这段代码有什么问题?
解析:
- 致命错误:返回栈地址。
- 在
getmemory_stack函数中,char p[]是一个局部数组,存储在栈上。 - 当
getmemory_stack函数执行完毕返回时,它的栈帧(包括局部变量p)会被销毁。 - 虽然有些编译器和操作系统在特定情况下可能让你“侥幸”看到正确的输出(因为栈内容还没被覆盖),但这完全是未定义行为。
str成了一个野指针,指向了一块无效的内存,任何对str的操作(如printf或strcpy)都可能导致程序崩溃或输出乱码。
永远不要返回指向栈内存(局部变量)的指针!
总结与最佳实践
| 场景 | 错误做法 | 正确做法 | 核心原因 |
|---|---|---|---|
| 修改外部指针 | void func(char *p)p = malloc(...); |
void func(char **p)*p = malloc(...); |
C 是值传递,需要通过指针的指针来间接修改外部变量。 |
| 获取内存地址 | 传递指针指针 | char* func()return malloc(...); |
返回指针是更简洁、更通用的 C 语言风格。 |
| 返回内存 | char* func()char p[] = "...";return p; |
绝对禁止 | 栈内存随函数结束而销毁,返回其地址会造成未定义行为。 |
| 内存管理 | 分配后不 free |
malloc 和 free 成对出现 |
避免内存泄漏。free 后最好将指针置为 NULL。 |
在面试或笔试中遇到 getmemory 相关的问题,请务必从“指针是如何传递的?”和“这块内存的生命周期是什么?”这两个角度去分析,通常就能抓住问题的关键。
