C语言getmemory笔试题常见陷阱有哪些?

99ANYc3cd6
预计阅读时长 15 分钟
位置: 首页 C语言 正文

核心考点

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

c语言 getmemory 笔试
(图片来源网络,侵删)
  1. 函数参数传递机制:C 语言中,函数参数是“值传递”(Pass-by-Value),这意味着传递给函数的是变量的副本,而不是变量本身。
  2. 指针的本质:指针本身就是一个变量,它存储的是一个内存地址,对指针的修改(改变它指向的地址)不会影响到指针外部的变量。
  3. 动态内存分配:使用 malloc (或 calloc) 在堆上分配内存,这块内存的生命周期从 malloc 开始,直到 free 结束,与函数的生命周期无关。
  4. 内存泄漏:如果动态分配的内存没有被 free,它将一直存在,直到程序结束,造成内存泄漏。
  5. 野指针:指向一块“已释放”或“未初始化”内存的指针,使用野指针会导致未定义行为,通常是程序崩溃。

经典笔试题及解析

下面我们通过几个层层递进的经典题目来理解这些考点。 一:最经典的“陷阱”题

代码:

#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;
}

问题:

  1. strcpy(str, "hello world"); 这行代码会成功吗?为什么?
  2. free(str); 会引发问题吗?整个程序的行为是怎样的?

解析:

c语言 getmemory 笔试
(图片来源网络,侵删)
  1. strcpy 会失败,导致程序崩溃。

    • 原因: C 语言是值传递,在 main 函数中调用 getmemory(str) 时,传递的是指针 str 的值,也就是 NULL 的副本。
    • getmemory 函数内部,p 被初始化为这个副本,即 p 也等于 NULL
    • p = (char *)malloc(...); 这行代码,它修改的是 getmemory 函数内部的局部变量 p,它让 p 指向了新分配的内存地址。
    • 这个修改完全不会影响到 main 函数中的 strmain 函数中的 str 依然是 NULL
    • strcpy(str, "hello world"); 相当于 strcpy(NULL, "hello world");,这会导致段错误,程序崩溃。
  2. free(str); 不会执行,或者执行后没有意义。

    • 由于程序已经在 strcpy 处崩溃了,free(str); 这行代码根本不会被执行。
    • 即使程序没有崩溃(如果 str 初始不是 NULL),free(str) 释放的也是 main 函数中的 str 所指向的内存(可能是 NULL,也可能是一块未知的内存),而不是 getmemorymalloc 出来的那块内存,这会导致严重的内存泄漏

这段代码存在双重问题:既使用了空指针导致崩溃,又存在内存泄漏。


如何修正?(使用指针的指针)

c语言 getmemory 笔试
(图片来源网络,侵删)

问题: 如何修改 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 函数: 我们传递的是 &strstr 的地址)。str 是一个 char*&str 的类型是 char**
  • getmemory_fixed 函数: 它的参数现在是 char **pp 本身是一个指针,它指向 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 的操作(如 printfstrcpy)都可能导致程序崩溃或输出乱码。

永远不要返回指向栈内存(局部变量)的指针!


总结与最佳实践

场景 错误做法 正确做法 核心原因
修改外部指针 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 mallocfree 成对出现 避免内存泄漏。free 后最好将指针置为 NULL

在面试或笔试中遇到 getmemory 相关的问题,请务必从“指针是如何传递的?”“这块内存的生命周期是什么?”这两个角度去分析,通常就能抓住问题的关键。

-- 展开阅读全文 --
头像
Metasploit C语言反弹如何实现?
« 上一篇 今天
dede sql里怎么引入文件?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]