C 语言 动态存储

发布于 2024-04-04 04:19:36 字数 3989 浏览 14 评论 0

到目前为止,写的所有程序都是 静态存储,也就是每当想保存一个东西,就需要在代码中声明一个变量,然后这些变量保存在栈中, 以上面的例子,如果要创建更多 island 则需要 write N islands。

1. 用堆进行动态存储

堆是程序中长期保存的地方,不会自动清除,首先会用 malloc() 申请空间, malloc() 函数会向操作系统要求分配空间,然后返回指向该空间的指针。但是这种申请也不是随意的,因为堆空间有限,用完之后要释放掉,调用 free() 释放存储器。虽然程序运行结束后所有的堆空间会自动释放,但是显式调用 free 更加合理。

malloc:memory allocation(存储器分配), malloc() 接收一个参数,表示申请的字节大小,往往我们不知道字节大小,所以配合 sizeof()

malloc 返回通用指针,即 void* 类型的指针:

island *p = malloc(sizeof(island));

free() 需要调用 malloc() 创建的地址,释放方式如下:

free(p);

那么接着上面的例子,动态创建岛屿:

// 创建
island* create(char *name) {
    island *i = malloc(sizeof(island));
    i->name = name;
    i->opens = "09:00";
    i->closes = "21:00";
    i->next = NULL;
    return i;   // 返回新结构的地址
}

在 main 函数中动态创建 island:

int main(int argc, char *argv[]) {	    
    island *A = create("A", "10:00", "22:00");
    island *B = create("B", "09:00", "22:00");
    A->next = B;
	printf("current island:%s, the next island is:%s", A->name, A->next->name);
    return 0;
}

2.strdup() 函数把字符串复制到堆上

如下实例:

// 创建递归结构必须为此命名,typedef struct island 不可省略
typedef struct island{
    char *name;
    char *opens;
    char *closes;
    struct island *next;    // 保存一个指向下一个的指针
} island;

// 循环展示
void display(island *start) {
    island *i = start;
    for (; i != NULL; i=i->next) {
        // i != NULL,表示需要一直循环下去直到当前 island 没有 next 值
        // i = i->next. 表示循环结束后跳转到下一个 island
        printf("Name:%s\n open:%s-%s\n", i->name, i->opens, i->closes);
    }
}

// 创建
island* create(char *name) {
    island *i = malloc(sizeof(island));
    i->name = name;
    i->opens = "10:00";
    i->closes = "22:00";
    i->next = NULL;
    return i;   // 返回新结构的地址
}

int main(int argc, char *argv[]) {    
    char name[80];
    fgets(name, 80, stdin);
    island *A = create(name);
    
    fgets(name, 80, stdin);
    island *B = create(name);
    A->next = B;
    display(A);
    return 0;
}

当我们输入后却发现返回值一样:

AAA
BBB
Name:BBB

 open:10:00-22:00
Name:BBB

 open:10:00-22:00

这是因为 fgets 公用字符数组 name, 所以 A,B 变量共用同一 name 地址,为了解决此类问题,除了更换名称外,还可以用 strdup() 函数,strdup() 函数可以把字符串复制到堆上:

char *copy = strdup(s);

strdup 的原理:

  • 计算字符串长度,然后调用 malloc() 在堆上分配相应的空间
  • 把所有字符串复制到堆上的新空间

注意:在使用 strdup 后,一定要记得 free 释放空间。

我们把上面代码改成这样就 ok 了:

i->name = strdup(name);

接下来,来一个创建与释放的例子:

// 释放
void release(island *start) {
    island *i = start;
    island *next = NULL;
    for (; i != NULL; i=next) {
        next = i->next;             // 把 next 设为指向下一个的指针
        free(i->name);              // 先释放用 strdup() 创建的 name 字符串
        free(i);                    // 只有先释放 name 才能释放 island 结构,否则就找不到 name 了
    }
}

int main(int argc, char *argv[]) {	    
    island *start = NULL;       // 开始结构
    island *i = NULL;           // 当前结构
    island *next = NULL;        // 下一个指向的结构
    char name[80];
    for (; fgets(name, 80, stdin) != NULL; i=next) {    // i=next:在每次循环的最后,将 i 设为下一个要创建的结构
        next = create(name);        // 创建结构
        if (start == NULL) {        // 第一次循环时,start 值为 NULL,因此将 start 设为第一座岛屿
            start = next;
        }
        if (i != NULL) {            //当前结构不为 NULL,就将当前结构的下一条指向 next
            i->next = next;
        }
    }
    // 从开始结构开始循环展示
    display(start);
    // 用完了释放它
    release(start)
    return 0;
}

3.valgrid 检查存储器泄露

valgrid 通过拦截 malloc() 与 free() 的调用工作,打印留在堆上的数据信息,使用方法:

gcc -g main.c -o main
valgrid --leak-check=full ./main

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
23 人气
更多

推荐作者

金兰素衣

文章 0 评论 0

ゃ人海孤独症

文章 0 评论 0

一枫情书

文章 0 评论 0

清晰传感

文章 0 评论 0

mb_XvqQsWhl

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文