C进阶-动态内存

Posted 程序彤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C进阶-动态内存相关的知识,希望对你有一定的参考价值。

malloc,有借有还再借不难

int main() 
    int *p = malloc(10 * sizeof(int)); // 借空间
    if (p == NULL) 
        printf("%s\\n", strerror(errno));
     else 
        printf("成功\\n");
        for (int i = 0; i < 10; ++i) 
            *(p + i) = i;
        
        for (int i = 0; i < 10; ++i) 
            printf("%d\\n", *(p + i));
        
    
    printf("%s\\n", p);
    // 还空间
    free(p);
    p = NULL;
    return 0;


malloc开辟后不初始化,直接返回第一个地址空间。malloc、calloc,relloc都必须和free成对使用,并最终p置空。relloc在调整空间时分两种情况:当前调整空间后有无空闲空间。
int* p = realloc(null,40)等价于int* p = malloc(40);

动态开辟空间常见错误

1. 对NULL指针解引用

若malloc开辟空间失败,那么就是对空指针解引用

2. 对动态开辟的内存空间越界访问了

开辟40个字节,而访问第i=11个元素。

3. 对非动态开辟的内存用free释放

不要莫名其妙的free

4. 使用free释放动态开辟内存的一部分(常见)

若当前的p和之前p所指地址发生变化后,*p++,则必出错。若星(p+i)则无错

5. 对同一空间free多次。(为将p习惯置于空指针NULL)

6. 最佳实践。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void GetMemory1(char **p)
    *p = (char*)malloc(100);


char* GetMemory2(char* p)
    return (char*) malloc(100);


char* GetMemory3(void)
    char p[] = "hello lwt"; //出错
    // static int a =10; return &a;,返回类型为int*,则不出错。
    return p;

void Test1(void)
    char* str = NULL;
//    GetMemory1(&str); // 法一:成功
    str = GetMemory2(&str); // 法二:成功
    strcpy(str,"hello");
    printf(str);


// Test2从新开始,返回栈空间的地址问题
void Test2(void)
    char* str = NULL;
    str = GetMemory3();
    printf(str);

int main() 
    Test1(); // 成功
    Test2(); // 报错,非法访问内存空间,此时的p地址虽然在,但不知指向了谁。
    return 0;


柔型数组(0长度数组)

  1. 柔型数组成员前必须至少有一个其他成员变量
  2. sizeof不计算柔型数组大小,默认+0
  3. 让数组变得弹性可扩展,依赖于malloc和realloc。
#include <stdio.h>
#include <stdlib.h>

struct S 
    int n;
    int arr[0]; // 柔性数组
 s;

int main() 
    printf("%d\\n", sizeof(s)); // 4
    struct S *ps = malloc(sizeof(struct S) + 5 * sizeof(int)); // 24
    printf("%d\\n", sizeof(ps)); // 指针8不是24
    printf("******************\\n");
    ps->n = 666;
    for (int i = 0; i < 5; ++i) 
        ps->arr[i] = i;
    
    struct S *ptr = realloc(ps, 44);
    if (ptr != NULL) 
        ps = ptr;
    
    for (int i = 5; i < 10; ++i) 
        ps->arr[i] = i;
    
    for (int i = 0; i < 10; ++i) 
        printf("%d\\n", ps->arr[i]);
    
    free(ps);
    ps = NULL;


    return 0;


第二种写法不用柔性数组也能将一个int* arr进行替代柔型数组:ps->arr = malloc(5*sizeof(struct S));
释放空间从内而外释放。

哪一种写法更好,使用柔性数组还是不使用呢?

柔型数组优势(虽不用也行)

  1. 尽可能少的导致free太多释放导致的错误,而使用第二种方式会free释放两次。
  2. malloc开辟的独立空间可能导致内存碎片,致使内存利用率变低,而使用柔性数组则可避免。

对比传统指针两次malloc不使用柔型数组,效果相似 法二代码:

#include <stdio.h>
#include <stdlib.h>

struct S 
    int n;
    int* arr; // 柔性数组
 s;

int main() 
    struct S* p = (struct S*) malloc(sizeof(struct S));
    p->n = 100;
    p->arr = malloc(10* sizeof(int));
    for (int i = 0; i < 10; ++i) 
        p->arr[i] = i;
    
    for (int i = 0; i < 10; ++i) 
        printf("%d\\n", p->arr[i]);
    
    // 两次malloc,必须两次free
    free(p->arr);
    p->arr = NULL;

    free(p);
    p = NULL;
    return 0;


动态开辟内存空间扩容通讯录升级版代码

contact.h文件

//#define MAX 1000

#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30

#define DEFAULT_SZ 3

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct PeoInfo
    char name[MAX_NAME];
    int age;
    char sex[MAX_SEX];
    char tele[MAX_TELE];
    char addr[MAX_ADDR];
;

struct Contact
    struct PeoInfo *data;
//    struct PeoInfo data[MAX];
    int size; // 当前已有条数

    int capacity; // 通讯录当前容量
;

enum Option
    EXIT,ADD,DEL,SELECT,UPDATE,SHOW,SORT
;
void InitContact(struct Contact* ps);
void AddContact(struct Contact* ps);
void ShowContact(const struct Contact *ps);
void DelContact(struct Contact *ps); // 删除指定条目
void SelectContact(const struct Contact* ps);
//int FindByName(struct Contact* ps,char name[MAX_NAME]); //不需要在这声明,加上static不向外暴露
void UpdateContact(struct Contact* ps);
void DestroyContact(struct Contact* ps);

contact.c文件

#include "contact.h"

void CheckCapacity(struct Contact* ps)
   if(ps->size == ps->capacity)
       struct PeoInfo* ptr = realloc(ps->data,(ps->capacity+2)*sizeof(struct PeoInfo));
       if(ptr!=NULL)
           ps->data = ptr;
           ps->capacity+=2;
           printf("扩容成功\\n");
        else
           printf("扩容失败\\n");
       
   else
       printf("无需扩容,size++直接添加新联系人:>\\n");
   

// 抽取查找函数,必须事先声明
static int FindByName(struct Contact *ps, char name[MAX_NAME]) 
    int i = 0;
    for (i = 0; i < ps->size; ++i) 
        if (0 == strcmp(ps->data[i].name, name)) 
            return i; // 找到了,抓取当前i值
        
    
    return -1;


void InitContact(struct Contact *ps) 
//    memset(ps->data, 0, sizeof(ps->data));
//    ps->size = 0;
    ps->data = (struct PeoInfo*)malloc(3*sizeof(struct PeoInfo));
    if(ps->data ==NULL)
        return;
    
    ps->size = 0;
    ps->capacity = DEFAULT_SZ;


void AddContact(struct Contact *ps) 
//    if (ps->size == MAX) 
//        printf("通讯录已满,无法增加\\n");
//        return;
//     else 
//        printf("请输入名字:>");
//        scanf("%s", ps->data[ps->size].name);
//        printf("请输入年龄:>");
//        scanf("%d", &(ps->data[ps->size].age));
//        printf("请输入性别:>");
//        scanf("%s", ps->data[ps->size].sex);
//        printf("请输入电话:>");
//        scanf("%s", ps->data[ps->size].tele);
//        printf("请输入地址:>");
//        scanf("%s", ps->data[ps->size].addr);
//        ps->size++;
//        printf("!!!添加成功!!!\\n");
//    
    CheckCapacity(ps); // 检测当前通讯录容量,如果满扩容,如果未满,size++增加数据就行
    printf("请输入名字:>");
        scanf("%s", ps->data[ps->size].name);
        printf("请输入年龄:>");
        scanf("%d", &(ps->data[ps->size].age));
        printf("请输入性别:>");
        scanf("%s", ps->data[ps->size].sex);
        printf("请输入电话:>");
        scanf("%s", ps->data[ps->size].tele);
        printf("请输入地址:>");
        scanf("%s", ps->data[ps->size].addr);
        ps->size++;
        printf("!!!添加成功!!!\\n");



void ShowContact(const struct Contact *ps) 
    if (ps->size == 0) 
        printf("通讯录为空\\n");
//        return;
     else 
        printf("%20s\\t%4s\\t%5s\\t%12s\\t%20s\\n", "名字", "年龄", "性别", "电话", "地址");
        for (int i = 0; i < ps->size; ++i) 
            printf("%20s\\t%4d\\t%5s\\t%12s\\t%20s\\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].tele,
                   ps->data[i].addr);
        
    


void DelContact(struct Contact *ps) 
    printf("请输入要删除的人名-联系人:>");
    char name[MAX_NAME];
    int i = 0;
    scanf("%s", name);
//    for (i = 0; i < ps->size; ++i) 
//        if (0 == strcmp(ps->data[i].name, name)) 
//            break; // 找到了,抓取当前i值
//        
//    
    int pos = FindByName(ps, name); // 找到返回下标,找不到-1

//    if (i == ps->size) 
    if (pos == -1) 
        printf("要删除的人不存在\\n");
     else 
        int j = 0;
        for (j = pos; j < ps->size - 1; j++) 
            ps->data[j] = ps->data[j + 1];
        
        ps->size--;
        printf("删除成功\\n");
    


void SelectContact(const struct Contact *ps) 
    printf("请输入要查的人名:>");
    char name[MAX_NAME];
    scanf("%s", name);
    int pos = FindByName(ps, name);
    if (pos == -1) 
        printf("要查找的人不存在\\n");
     else 
        printf("%20s\\t%4s\\t%5s\\t%12s\\t%20s\\n", "名字", "年龄", "性别", "电话", "地址");
        printf("%20s\\t%4d\\t%5s\\t%12s\\t%20s\\n", ps->data[pos].name, ps->data[pos].age, ps->data[pos].sex,
               ps->data[pos].tele,
               ps->data[pos].addr);
    


void UpdateContact(struct Contact* ps)
    printf("请输入要修改的人名:>");
    char name[MAX_NAME];
    scanf("%s", name);
    int pos = FindByName(ps,name);
    if(pos == -1)
        printf("要修改的人不存在\\n");
    else
        printf("请输入名字:>");
        scanf("%s", ps->data[pos].name);
        printf("请输入年龄:>");
        scanf("%d", &(ps->data[pos].age));
        printf("请输入性别:>");
        scanf("%s", ps->data[pos].sex);
        printf("请输入电话:>");
        scanf("%s", ps->data[pos].tele);
        printf("请输入地址:>");
        scanf("%s", ps->data[pos].addr);

        printf("!!!修改成功!!!\\n");
    


void DestroyContact(struct Contact* ps)
    free(ps->data);
    ps->data = NULL;

main.c文件

#include <stdio.h>
#include "contact.c" // main.c引入contact.c&#

以上是关于C进阶-动态内存的主要内容,如果未能解决你的问题,请参考以下文章

C语言进阶—— 动态内存开辟+柔性数组

刨析《C语言》进阶付费知识完结

C语言进阶四.动态内存管理

动态内存管理C进阶

C语言进阶之旅(14)动态内存

《C语言深度剖析》第五章 内存管理 p1(完结)( C语言从入门到入土(进阶篇)