C语言回顾 十 动态内存分配

动态内存分配(Dynamic Memory Allocation)就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法.
它是相对重要的一节课,如果能够将它学好,那么对于今后学习OC的内存管理会非常有帮助.
1.内存区划分
内存区域划分(地址由高到低)
(1)栈区
(2)堆区
(3)静态区(全局区)
(4)常量区
(5)代码区
(1)栈区
函数中定义的局部变量由系统在栈区分配内存
开发人员不用关心系统如何分配内存,完全由系统分配和释放内存
栈区内的数据以栈的形式存储
栈的特点:先进后出

(2)静态全局区
全局变量和用static修饰的变量都由系统在静态全局存放
静态全局区所分配的内存,直到程序结束才能被释放
   
用static修饰的变量
a.存放在静态全局区
b.只能被初始化一次
c.如果没有赋初值,默认为0

(3)常量区
常量('a', 123, 34.5, "iPhone")(字符型,整型,实型)都被存到常量区
常量区由系统管理

常量区的数据,是只读状态,不能修改

(4)代码区
程序中的函数和语句会被编译成CPU指令存在代码区
代码区由系统控制
   
(5)堆区
由开发人员手动申请,手动控制

申请内存函数
void *malloc(size_t)
返回值类型:void * (泛类型),泛型指针可以转换成任意一种指针类型
函数名:malloc
参数类型:size_t, unsigned int(无符号整型)
参数含义:多少个字节

堆区地址存放在栈区


内存泄露:内存区域一直被占用,但得不到释放
   
2.内存释放函数
free()
把对应内存区域的标记改为可用
例:free(p3);
实际上指针p3的内容没有被清除,需要置空
P3 = NULL;

做两个练习
(1)有一字符串,其中包含 数字,提取其中的数字,要求动态分配内存保存
    提示:先计算出有几个数字,然后根据数组的个数来开辟空间.
    定义字符串
    char str[] = "s5d4d5fv5ju81224uk5d5s";
    unsigned long n = strlen(str);
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (*(str + j) >= '0' && *(str + j) <= '9') {
                do {
                    *(str + j) = *(str + j + 1);
                    j++;
                } while (*(str + j) != '\0');
            }
        }
    }
    printf("%s", str);
    printf("\n");
    计算字符串中数字的个数
    int i = 0, count = 0;
    while (str[i] != '\0') {
        if (str[i] >= '0' && str[i] <= '9') {
            count++;
        }
        i++;
    }
    printf("%d\n", count);
    //从堆区申请内存,因为是字符串最后要多加一个'\0',申请(数字个数 + 1)个
    char *p5 = malloc(sizeof(char) * (count + 1));
    //再次遍历字符串,把是数字的字符,存到申请到的内存中
    int j = 0, k = 0;
    while (str[j] != '\0') {
        if (str[j] >= '0' && str[j] <= '9') {
            *(p5 + k) = str[j];
            k++;
        }
        j++;
    }
    //在字符串的后面加上'\0'
    *(p5 + k) = '\0';
    printf("%s\n", p5);
    //释放内存
    free(p5);
    p5 = NULL;
(2)输入三个单词,动态分配内存保存单词,并在最后输出
    //提示:定义一个指针数组保存数据char *words[3] = {0}
    //临时字符串
    char str1[50] = {0};
    //指针数组,用于存放三个单词在堆区申请内存的 首地址
    char *words[3] = {0};
    for (int i = 0; i < 3; i++) {
        printf("请输入一个单词:");
        //输入字符串
        scanf("%s", str1);
        //words[i] = malloc(sizeof(char) * (strlen(str1) + 1));
        //strlen计算字符串不包括'\0',需要多加1
        unsigned long length = strlen(str1);
        //把 堆区申请到的内存首地址 存到指针数组中
        words[i] = malloc(sizeof(char) * length);
        //进行字符串拷贝
        strcpy(words[i], str1);
        printf("存入字符串:%s\n", words[i]);
    }
    //释放内存,需要先找到刚才申请的堆区的首地址,首地址都在指针数组存在,遍历指针数组,释放内存
    for (int i = 0; i < 3; i++) {
        free(words[i]);
        words[i] = NULL;
    }

3.动态内存的其他函数
   
(1)void *calloc(n, size)
和malloc一样,都是申请内存,但是calloc申请内存后,会对内存中的内容做清空的操作;由于多了一步清空操作,效率要比malloc低
n:个数
size:字节数
calloc申请的内存字节数 = n * size
   
(2)void realloc(p, size)
从给定的位置p,开始重新申请size个字节
从地址p向后申请size个字节,如果后面可用的字节够的话,就申请内存,并返回当前的指针p;如果不够的话,会再去内存中找一块连续的空间,如果找到,就返回这一块连续空间的首地址,并且把之前所占用的内存释放
   
(3)void memset(p, c, n)
从指针p的位置开始初始化n个字节的内容,把内容改成c
   
(4)void *memcpy(void *dest, const void *source, n)
从指针source的位置开始,向指针dest位置,拷贝n个字节的内容
    char str1[] = "ABC";
    char str2[] = "123";
    memcpy(str1, str2, 2);
    printf("%s\n", str1);
   
(5)memcmp(p1, p2, n)
 比较p1和p2指向的内存所存放的内容是否相同,比较n个字节,相同返回0,不同返回差值
    int *a1 = malloc(4);
    *a1 = 1;
    int *a2 = malloc(4);
    *a2 = 3;
    int result = memcmp(a1, a2, 1);
    printf("%d\n", result);

做一个练习
定义两个 整型指针,分别用malloc、calloc对其分配空间保存3个元素,malloc分配的空间用memset清零,随机对数组进行 赋值 随机范围1-3,赋值后用memcmp比较两个数组。如果相同打印Good!否则打印Failed...
    int *p1 = malloc(sizeof(int) * 3);
    int *p2 = calloc(3, sizeof(int));
    memset(p, 0, sizeof(int) * 3);
    for (int i = 0; i < 3; i++) {
        *(p1 + i) = arc4random() % 3 + 1;
        *(p2 + i) = arc4random() % 3 + 1;
        printf("%d %d\n", *(p1 + i), *(p2 + i));
    }
    int j = memcmp(p1, p2, sizeof(int) * 3);
    if (j == 0) {
        printf("Good!\n");
    } else (printf("Failed!\n"));
    free(p1);
    p = NULL;
    free(p2);
    p = NULL;




1.函数指针
函数指针是指向函数的指针变量,其本质是一个指针变量
函数指针定义
返回值类型(*p)(参数类型1, 参数类型2, ...) = 初值;
注:1.p是指针名字
2.除指针名字p以外的,是函数指针的类型

    //栈区地址
    int a = 88;
    printf("%p\n", &a);
   
    //堆区地址
    int *p = malloc(40);
    printf("%p\n", p);
   
    //静态全局区
    static int b = 10;
    printf("%p\n", &b);
   
    //常量区
    char *p1 = "ABC";
    printf("%p\n", p1);
   
    //代码区
    printHello();
    printf("\n");

数组的名字是数组的首地址
    int array[5] = {1, 2, 3, 4, 5};
    printf("%p\n", array);
   
函数的名字,也是函数的首地址
函数的首地址在代码区
    printf("%p\n", printHello);
  
存储数组的首地址
    int *p2 = array;

如果要存储函数的首地址,需要用到函数指针

例如: 定义一个指针指向printHello
    void (*p3)() = NULL;
    //1.p3 = printHello;
    //2.p3 = &printHello;(&可加可不加)
    p3();

指向数组首地址的指针可以当做数组使用
而指向函数首地址的指针,则可以用来调用函数

2.回调函数
使用函数指针来调用函数
如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用它所指向的函数时,我们就说这是回调函数
   
重命名函数指针类型
函数指针的重命名比较特殊,新名字需要写在*后面
    typedef int (*ABC)(int, int);
    ABC p6 = NULL;

做个练习,一组学生考试成绩按照从高到低排列
int main {
    printStudent(stu, 5);
    printf("\n");
    //姓名
    //    sort(stu, 5, isName);
    //成绩
    //    sort(stu, 5, isScore);
    //年龄
    //    sort(stu, 5, isAge);
   
    sort(stu, 5, "name");
    sort(stu, 5, "score");
    sort(stu, 5, "age");
   
    printStudent(stu, 5);
    printf("\n");
    return 0;
}
struct student {
    char name[20];
    float score;
    int age;
};
typedef struct student Student;
BOOL isName(Student *p1, Student *p2);
BOOL isName(Student *p1, Student *p2) {
    return strcmp(p1->name, p2->name) > 0;
}

BOOL isScore(Student *p1, Student *p2);
BOOL isScore(Student *p1, Student *p2) {
    return p1->score < p2->score;
}

BOOL isAge(Student *p1, Student *p2);
BOOL isAge(Student *p1, Student *p2) {
    return p1->age > p2->age;
}

typedef BOOL(*SortType)(Student *, Student *);
SortType isType(char *string);
SortType isType(char *string) {
    if (strcmp(string, "name") == 0) {
        return isName;
    } else if (strcmp(string, "age") == 0) {
        return isAge;
    } else if (strcmp(string, "score") == 0) {
        return isScore;
    } else {
        return isName;
    }
}

void sort(Student *p, int n, char *string);
void sort(Student *p, int n, char *string) {
    SortType funp = isType(string);
   
    BOOL flag = YES;
    for (int i = 0; i < n - 1 && flag; i++) {
        flag = NO;
        for (int j = 0; j < n - 1 - i; j++) {
            if (funp(p + j, p + j + 1)) {
                Student temp = *(p + j);
                *(p + j) = *(p + j + 1);
                *(p + j + 1) = temp;
                flag = YES;
            }
        }
    }
}
void printStudent(Student *p, int n);
void printStudent(Student *p, int n) {
    for (int i = 0; i < n; i++) {
        printf("%s %.2f %d ", (p + i)->name, (p + i)->score, (p + i)->age);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值