C语言基础-九、动态内存分配

目录

1、动态内存分配函数

[1.1 内存分配函数总览](#1.1 内存分配函数总览)

[1.2 malloc - 动态分配内存](#1.2 malloc - 动态分配内存)

malloc特点

[1.3 calloc - 分配并初始化](#1.3 calloc - 分配并初始化)

[malloc vs calloc](#malloc vs calloc)

[1.4 realloc - 重新分配内存](#1.4 realloc - 重新分配内存)

realloc注意事项

[1.5 free - 释放内存](#1.5 free - 释放内存)

free使用规则

2、动态内存实践

[2.1 动态数组实现](#2.1 动态数组实现)

[2.2 动态二维数组](#2.2 动态二维数组)

[2.3 动态字符串](#2.3 动态字符串)

3、C语言内存结构

[3.1 内存分区详解](#3.1 内存分区详解)

[3.2 各区域特点对比](#3.2 各区域特点对比)

[3.3 变量存储位置示例](#3.3 变量存储位置示例)

[3.4 内存泄漏演示](#3.4 内存泄漏演示)

4、虚拟内存机制

[4.1 虚拟内存原理](#4.1 虚拟内存原理)

[4.2 延迟分配(Lazy Allocation)](#4.2 延迟分配(Lazy Allocation))

[4.3 内存过载测试](#4.3 内存过载测试)

5、常见错误与避坑指南

[5.1 错误类型总结](#5.1 错误类型总结)

[5.2 安全编程模板](#5.2 安全编程模板)

[5.3 内存调试技巧](#5.3 内存调试技巧)

6、综合练习

[6.1 学生成绩管理系统(动态版)](#6.1 学生成绩管理系统(动态版))

7、最佳实践总结

动态内存 :程序运行时根据需要手动申请和释放的内存空间

核心价值:灵活管理内存、突破栈空间限制、实现动态数据结构


1、动态内存分配函数

1.1 内存分配函数总览

函数 头文件 功能 返回值 特点
malloc() stdlib.h 分配指定字节 void* 不初始化
calloc() stdlib.h 分配并初始化为0 void* 自动清零
realloc() stdlib.h 重新分配大小 void* 保留原数据
free() stdlib.h 释放内存 void 必须配对使用

1.2 malloc - 动态分配内存

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 基本用法
    // 申请10个int的连续空间(40字节)
    int *p = (int*)malloc(10 * sizeof(int));
    
    // 重要:检查是否分配成功
    if (p == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }
    
    // 赋值(两种方式等价)
    for (int i = 0; i < 10; i++) {
        *(p + i) = (i + 1) * 10;  // 指针运算
        // p[i] = (i + 1) * 10;   // 数组下标(推荐)
    }
    
    // 遍历
    printf("malloc分配的数据:\n");
    for (int i = 0; i < 10; i++) {
        printf("%d ", p[i]);
    }
    printf("\n");
    
    // 释放内存(必须!)
    free(p);
    p = NULL;  // 避免悬空指针
    
    return 0;
}
malloc特点
特性 说明
单位 字节(byte)
返回值 void*(无类型指针)
初始化 ❌ 不初始化(内容是随机值)
失败返回 NULL
类型转换 C语言自动转换,C++需显式转换
cpp 复制代码
// C语言(可省略强转)
int *p1 = malloc(100);
int *p2 = (int*)malloc(100);  // 推荐写法,更清晰

// C++(必须强转)
// int *p = (int*)malloc(100);

1.3 calloc - 分配并初始化

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    // calloc(元素个数, 单个元素大小)
    // 自动初始化为0
    int *p = (int*)calloc(10, sizeof(int));
    
    if (p == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }
    
    // 验证初始化为0
    printf("calloc初始值:\n");
    for (int i = 0; i < 10; i++) {
        printf("%d ", p[i]);  // 全部输出0
    }
    printf("\n");
    
    free(p);
    p = NULL;
    
    return 0;
}
malloc vs calloc
特性 malloc calloc
参数 总字节数 (元素个数, 元素大小)
初始化 随机值 自动清零
效率 稍高 稍低(多一步初始化)
适用场景 会立即赋值 需要初始化为0

1.4 realloc - 重新分配内存

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 1. 初始分配5个int
    int *p = (int*)malloc(5 * sizeof(int));
    if (p == NULL) {
        return 1;
    }
    
    // 2. 初始化数据
    for (int i = 0; i < 5; i++) {
        p[i] = (i + 1) * 10;
    }
    
    // 3. 扩容到10个int
    // 重要:使用临时指针接收返回值
    int *temp = (int*)realloc(p, 10 * sizeof(int));
    
    if (temp == NULL) {
        // 扩容失败,原空间仍可用
        printf("扩容失败!\n");
        free(p);
        return 1;
    }
    
    // 扩容成功,更新指针
    p = temp;
    
    // 4. 验证原数据保留
    printf("扩容后原数据:\n");
    for (int i = 0; i < 5; i++) {
        printf("%d ", p[i]);  // 10 20 30 40 50
    }
    printf("\n");
    
    // 5. 使用新空间
    for (int i = 5; i < 10; i++) {
        p[i] = (i + 1) * 10;
    }
    
    printf("扩容后全部数据:\n");
    for (int i = 0; i < 10; i++) {
        printf("%d ", p[i]);
    }
    printf("\n");
    
    // 6. 缩容(也支持)
    int *temp2 = (int*)realloc(p, 3 * sizeof(int));
    if (temp2 != NULL) {
        p = temp2;
        printf("缩容后数据:\n");
        for (int i = 0; i < 3; i++) {
            printf("%d ", p[i]);  // 10 20 30
        }
        printf("\n");
    }
    
    free(p);
    p = NULL;
    
    return 0;
}
realloc注意事项
情况 说明
地址可能变 扩容后地址可能改变
原数据保留 保留到新空间(按较小容量)
失败返回NULL 原空间仍有效,需手动free
无需free原指针 成功时底层已处理

1.5 free - 释放内存

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p = (int*)malloc(100 * sizeof(int));
    
    if (p != NULL) {
        // 使用内存...
        
        // 正确释放
        free(p);
        p = NULL;  // 避免悬空指针
    }
    
    // 错误做法
    // free(p);
    // free(p);  // 重复释放,未定义行为!
    
    // 错误做法
    // free(p);
    // *p = 10;  // 使用已释放内存,危险!
    
    return 0;
}
free使用规则
cpp 复制代码
✅ 应该做的:
1. 谁malloc,谁free(配对使用)
2. free后立即置NULL
3. 只free动态分配的内存
4. 检查指针不为NULL再free

❌ 不应该做的:
1. 重复free同一指针
2. free后继续使用
3. free栈内存或常量区内存
4. free部分数组(必须整体释放)

2、动态内存实践

2.1 动态数组实现

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int *data;      // 数据指针
    int size;       // 当前元素个数
    int capacity;   // 总容量
} DynamicArray;

// 初始化
void initArray(DynamicArray *arr, int initialCapacity) {
    arr->data = (int*)malloc(initialCapacity * sizeof(int));
    if (arr->data == NULL) {
        printf("初始化失败!\n");
        arr->capacity = 0;
        arr->size = 0;
        return;
    }
    arr->size = 0;
    arr->capacity = initialCapacity;
}

// 添加元素(自动扩容)
void pushBack(DynamicArray *arr, int value) {
    // 容量不足时扩容(翻倍)
    if (arr->size >= arr->capacity) {
        int newCapacity = arr->capacity * 2;
        int *temp = (int*)realloc(arr->data, newCapacity * sizeof(int));
        
        if (temp == NULL) {
            printf("扩容失败!\n");
            return;
        }
        
        arr->data = temp;
        arr->capacity = newCapacity;
        printf("扩容到: %d\n", arr->capacity);
    }
    
    arr->data[arr->size++] = value;
}

// 获取元素
int get(DynamicArray *arr, int index) {
    if (index < 0 || index >= arr->size) {
        printf("索引越界!\n");
        return -1;
    }
    return arr->data[index];
}

// 释放
void freeArray(DynamicArray *arr) {
    if (arr->data != NULL) {
        free(arr->data);
        arr->data = NULL;
    }
    arr->size = 0;
    arr->capacity = 0;
}

// 打印
void printArray(DynamicArray *arr) {
    printf("[");
    for (int i = 0; i < arr->size; i++) {
        printf("%d", arr->data[i]);
        if (i < arr->size - 1) printf(", ");
    }
    printf("]\n");
}

int main() {
    DynamicArray arr;
    initArray(&arr, 5);
    
    // 添加15个元素(会触发多次扩容)
    for (int i = 0; i < 15; i++) {
        pushBack(&arr, i * 10);
    }
    
    printf("最终数组: ");
    printArray(&arr);
    printf("大小: %d, 容量: %d\n", arr.size, arr.capacity);
    
    // 获取元素
    printf("第5个元素: %d\n", get(&arr, 4));
    
    // 释放
    freeArray(&arr);
    
    return 0;
}

2.2 动态二维数组

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int rows = 3, cols = 4;
    
    // 方式1:指针数组(每行可不同长度)
    int **arr1 = (int**)malloc(rows * sizeof(int*));
    if (arr1 == NULL) {
        return 1;
    }
    
    // 为每行分配内存
    for (int i = 0; i < rows; i++) {
        arr1[i] = (int*)malloc(cols * sizeof(int));
        if (arr1[i] == NULL) {
            // 释放已分配的行
            for (int j = 0; j < i; j++) {
                free(arr1[j]);
            }
            free(arr1);
            return 1;
        }
    }
    
    // 赋值
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            arr1[i][j] = i * cols + j;
        }
    }
    
    // 打印
    printf("动态二维数组:\n");
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%4d", arr1[i][j]);
        }
        printf("\n");
    }
    
    // 释放(先释放每行,再释放行指针)
    for (int i = 0; i < rows; i++) {
        free(arr1[i]);
    }
    free(arr1);
    
    // 方式2:连续内存(性能更好)
    int *arr2 = (int*)malloc(rows * cols * sizeof(int));
    
    // 访问:arr2[i * cols + j]
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            arr2[i * cols + j] = i * cols + j;
        }
    }
    
    free(arr2);
    
    return 0;
}

2.3 动态字符串

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 动态字符串结构
typedef struct {
    char *data;
    int length;
    int capacity;
} DynamicString;

// 初始化
void initString(DynamicString *str, const char *initial) {
    if (initial == NULL) {
        str->capacity = 16;
        str->data = (char*)malloc(str->capacity);
        str->data[0] = '\0';
        str->length = 0;
    } else {
        str->length = strlen(initial);
        str->capacity = str->length + 1;
        str->data = (char*)malloc(str->capacity);
        strcpy(str->data, initial);
    }
}

// 追加字符串
void appendString(DynamicString *str, const char *append) {
    if (append == NULL) return;
    
    int appendLen = strlen(append);
    int newLength = str->length + appendLen;
    
    // 容量不足时扩容
    if (newLength + 1 > str->capacity) {
        int newCapacity = (newLength + 1) * 2;
        char *temp = (char*)realloc(str->data, newCapacity);
        
        if (temp == NULL) {
            printf("扩容失败!\n");
            return;
        }
        
        str->data = temp;
        str->capacity = newCapacity;
    }
    
    // 追加
    strcpy(str->data + str->length, append);
    str->length = newLength;
}

// 释放
void freeString(DynamicString *str) {
    if (str->data != NULL) {
        free(str->data);
        str->data = NULL;
    }
    str->length = 0;
    str->capacity = 0;
}

int main() {
    DynamicString str;
    initString(&str, "Hello");
    
    printf("初始: %s\n", str.data);
    
    appendString(&str, ", ");
    appendString(&str, "World");
    appendString(&str, "!");
    
    printf("追加后: %s\n", str.data);
    printf("长度: %d, 容量: %d\n", str.length, str.capacity);
    
    freeString(&str);
    
    return 0;
}

3、C语言内存结构

3.1 内存分区详解

cpp 复制代码
┌─────────────────────────────────────────┐
│           高地址                        │
├─────────────────────────────────────────┤
│  栈区 (Stack)                           │
│  - 局部变量                             │
│  - 函数参数                             │
│  - 函数调用信息                         │
│  - 自动分配和释放                       │
│  - 大小有限(通常1-8MB)                  │
├─────────────────────────────────────────┤
│  ↓ 栈向下增长                           │
│  ↑ 堆向上增长                           │
├─────────────────────────────────────────┤
│  堆区 (Heap)                            │
│  - malloc/calloc/realloc分配            │
│  - 手动管理(需要free)                   │
│  - 大小较大(受物理内存限制)             │
│  - 可能产生碎片                         │
├─────────────────────────────────────────┤
│  未初始化静态区 (BSS)                   │
│  - 未初始化的全局变量                   │
│  - 未初始化的static变量                 │
│  - 自动初始化为0                        │
├─────────────────────────────────────────┤
│  已初始化静态区 (Data)                  │
│  - 已初始化的全局变量                   │
│  - 已初始化的static变量                 │
│  - 常量数据                             │
├─────────────────────────────────────────┤
│  常量区 (Read-Only)                     │
│  - 字符串常量                           │
│  - const修饰的常量                      │
│  - 只读,不可修改                       │
├─────────────────────────────────────────┤
│  代码区 (Text/Code)                     │
│  - 程序指令                             │
│  - 函数代码                             │
│  - 只读,可执行                         │
└─────────────────────────────────────────┘
│           低地址                        │

3.2 各区域特点对比

区域 存储内容 生命周期 管理方式 大小限制
栈区 局部变量、参数 函数结束 自动 小(1-8MB)
堆区 动态分配内存 手动free 手动 大(受内存限制)
静态区 全局/static变量 程序全程 自动 中等
常量区 字符串常量 程序全程 只读 中等
代码区 程序指令 程序全程 只读 中等

3.3 变量存储位置示例

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 全局变量(已初始化静态区)
int globalVar = 100;

// 全局变量(未初始化静态区/BSS)
int globalVar2;

// 常量区
const char *strConst = "Hello World";

void func(int param) {  // param在栈区
    // 栈区
    int localVar = 200;
    static int staticVar = 300;  // 已初始化静态区
    
    // 堆区
    int *heapVar = (int*)malloc(sizeof(int));
    *heapVar = 400;
    
    printf("全局变量地址: %p\n", (void*)&globalVar);
    printf("全局变量2地址: %p\n", (void*)&globalVar2);
    printf("局部变量地址: %p\n", (void*)&localVar);
    printf("静态变量地址: %p\n", (void*)&staticVar);
    printf("堆变量地址: %p\n", (void*)heapVar);
    printf("常量字符串地址: %p\n", (void*)strConst);
    printf("函数参数地址: %p\n", (void*)&param);
    printf("函数代码地址: %p\n", (void*)func);
    
    free(heapVar);
}

int main() {
    func(500);
    return 0;
}

3.4 内存泄漏演示

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 内存泄漏示例
void memoryLeak() {
    int *p = (int*)malloc(100 * sizeof(int));
    // 忘记free,程序结束后内存无法回收
}

// 正确做法
void noLeak() {
    int *p = (int*)malloc(100 * sizeof(int));
    if (p != NULL) {
        // 使用...
        free(p);
        p = NULL;
    }
}

// 悬空指针
void danglingPointer() {
    int *p = (int*)malloc(100 * sizeof(int));
    free(p);
    *p = 10;  // 危险!使用已释放内存
}

// 避免悬空指针
void safePointer() {
    int *p = (int*)malloc(100 * sizeof(int));
    free(p);
    p = NULL;  // 置NULL
    // if (p != NULL) *p = 10;  // 安全检查
}

int main() {
    // 模拟多次调用导致内存泄漏
    for (int i = 0; i < 1000; i++) {
        memoryLeak();  // 每次泄漏400字节
    }
    
    printf("程序结束,泄漏的内存由操作系统回收\n");
    return 0;
}

4、虚拟内存机制

4.1 虚拟内存原理

cpp 复制代码
物理内存有限 → 虚拟内存扩展

程序看到的地址 = 虚拟地址
实际存储的地址 = 物理地址

操作系统通过页表映射虚拟地址到物理地址

4.2 延迟分配(Lazy Allocation)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 申请1GB空间(虚拟地址)
    char *p = (char*)malloc(1024 * 1024 * 1024);
    
    if (p == NULL) {
        printf("申请失败\n");
        return 1;
    }
    
    printf("申请成功(虚拟地址)\n");
    
    // 此时并未真正分配物理内存
    // 只有实际写入数据时才会分配物理页
    
    // 触发物理内存分配
    p[0] = 'A';  // 第一页
    p[1024 * 1024] = 'B';  // 第二页
    
    printf("写入数据后,物理内存才真正分配\n");
    
    free(p);
    return 0;
}

4.3 内存过载测试

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int count = 0;
    size_t total = 0;
    size_t blockSize = 10 * 1024 * 1024;  // 10MB
    
    printf("开始申请内存...\n");
    
    while (1) {
        char *p = (char*)malloc(blockSize);
        
        if (p == NULL) {
            printf("\n申请失败!\n");
            printf("成功申请次数: %d\n", count);
            printf("总申请大小: %.2f GB\n", total / (1024.0 * 1024.0 * 1024.0));
            break;
        }
        
        // 写入数据触发物理分配
        p[0] = 'A';
        
        count++;
        total += blockSize;
        
        if (count % 10 == 0) {
            printf("已申请: %d 块 (%.2f GB)\n", 
                   count, total / (1024.0 * 1024.0 * 1024.0));
        }
        
        // 实际项目应该保存指针以便后续free
        // 这里为了演示泄漏,故意不free
    }
    
    return 0;
}

5、常见错误与避坑指南

5.1 错误类型总结

错误类型 错误示例 后果 正确做法
忘记free malloc后不释放 内存泄漏 配对使用free
重复free free(p); free(p); 程序崩溃 free后置NULL
悬空指针 free(p); *p=10; 未定义行为 free后置NULL
越界访问 p[100](只分配50) 数据损坏 记录大小并检查
未检查NULL 直接使用malloc返回值 可能崩溃 if(p==NULL)检查
错误大小 malloc(10)(应为10*sizeof(int)) 空间不足 使用sizeof
free栈内存 free(&localVar) 程序崩溃 只free动态内存
realloc丢失 p = realloc(p, size) 原指针丢失 用临时指针接收

5.2 安全编程模板

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 安全的内存分配函数
void* safeMalloc(size_t size) {
    void *p = malloc(size);
    if (p == NULL) {
        fprintf(stderr, "内存分配失败!需要 %zu 字节\n", size);
        exit(1);
    }
    return p;
}

// 安全的realloc
void* safeRealloc(void *ptr, size_t newSize) {
    void *temp = realloc(ptr, newSize);
    if (temp == NULL && newSize > 0) {
        fprintf(stderr, "内存重新分配失败!\n");
        free(ptr);  // 释放原内存
        exit(1);
    }
    return temp;
}

// 安全的free
void safeFree(void **ptr) {
    if (ptr != NULL && *ptr != NULL) {
        free(*ptr);
        *ptr = NULL;
    }
}

int main() {
    // 使用安全函数
    int *p = (int*)safeMalloc(100 * sizeof(int));
    
    // 使用...
    
    // 安全释放
    safeFree((void**)&p);
    
    return 0;
}

5.3 内存调试技巧

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 调试宏
#ifdef DEBUG
    #define MALLOC(size) \
        printf("malloc %zu bytes at %s:%d\n", size, __FILE__, __LINE__), \
        malloc(size)
    #define FREE(ptr) \
        printf("free %p at %s:%d\n", ptr, __FILE__, __LINE__), \
        free(ptr)
#else
    #define MALLOC(size) malloc(size)
    #define FREE(ptr) free(ptr)
#endif

int main() {
    int *p = (int*)MALLOC(100 * sizeof(int));
    
    // 使用...
    
    FREE(p);
    p = NULL;
    
    return 0;
}

6、综合练习

6.1 学生成绩管理系统(动态版)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int id;
    char *name;
    float *scores;
    int scoreCount;
} Student;

typedef struct {
    Student *students;
    int count;
    int capacity;
} StudentManager;

// 初始化
void initManager(StudentManager *mgr, int initialCapacity) {
    mgr->capacity = initialCapacity;
    mgr->count = 0;
    mgr->students = (Student*)malloc(initialCapacity * sizeof(Student));
}

// 添加学生
void addStudent(StudentManager *mgr, int id, const char *name, float scores[], int scoreCount) {
    // 扩容
    if (mgr->count >= mgr->capacity) {
        mgr->capacity *= 2;
        mgr->students = (Student*)realloc(mgr->students, mgr->capacity * sizeof(Student));
    }
    
    Student *s = &mgr->students[mgr->count];
    s->id = id;
    s->name = (char*)malloc(strlen(name) + 1);
    strcpy(s->name, name);
    s->scoreCount = scoreCount;
    s->scores = (float*)malloc(scoreCount * sizeof(float));
    memcpy(s->scores, scores, scoreCount * sizeof(float));
    
    mgr->count++;
}

// 释放单个学生
void freeStudent(Student *s) {
    if (s->name != NULL) {
        free(s->name);
        s->name = NULL;
    }
    if (s->scores != NULL) {
        free(s->scores);
        s->scores = NULL;
    }
}

// 释放管理器
void freeManager(StudentManager *mgr) {
    for (int i = 0; i < mgr->count; i++) {
        freeStudent(&mgr->students[i]);
    }
    if (mgr->students != NULL) {
        free(mgr->students);
        mgr->students = NULL;
    }
    mgr->count = 0;
    mgr->capacity = 0;
}

// 打印所有学生
void printAll(StudentManager *mgr) {
    printf("\n=== 学生列表 ===\n");
    for (int i = 0; i < mgr->count; i++) {
        Student *s = &mgr->students[i];
        printf("学号: %d, 姓名: %s, 成绩: ", s->id, s->name);
        for (int j = 0; j < s->scoreCount; j++) {
            printf("%.1f ", s->scores[j]);
        }
        printf("\n");
    }
}

int main() {
    StudentManager mgr;
    initManager(&mgr, 5);
    
    // 添加学生
    float scores1[] = {85.0, 90.0, 78.0};
    float scores2[] = {92.0, 88.0, 95.0};
    float scores3[] = {76.0, 82.0, 80.0};
    
    addStudent(&mgr, 1001, "张三", scores1, 3);
    addStudent(&mgr, 1002, "李四", scores2, 3);
    addStudent(&mgr, 1003, "王五", scores3, 3);
    
    printAll(&mgr);
    
    // 释放
    freeManager(&mgr);
    
    printf("\n内存已释放\n");
    return 0;
}

7、最佳实践总结

cpp 复制代码
内存管理原则:
1. 谁分配,谁释放(责任明确)
2. 分配后立即检查NULL
3. 释放后立即置NULL
4. 使用sizeof计算大小
5. 记录分配的大小
6. 避免内存泄漏
7. 避免悬空指针
8. 避免重复释放

代码规范:
1. 统一分配/释放风格
2. 使用辅助函数封装
3. 添加错误处理
4. 使用调试宏追踪
5. 定期用工具检测(valgrind)

性能优化:
1. 批量分配减少碎片
2. 合理预估容量
3. 避免频繁realloc
4. 及时释放不用的内存
相关推荐
Highcharts.js1 小时前
如何根据派生数据创建钟形曲线图表?highcharts正态分布曲线使用指南:从创建到设置一文搞定
开发语言·javascript·开发文档·正态分布·highcharts·图表类型·钟形图
追随者永远是胜利者1 小时前
(LeetCode-Hot100)301. 删除无效的括号
java·算法·leetcode·职场和发展·go
楼田莉子2 小时前
Linux网络学习:网络的基础概念
linux·运维·服务器·网络·c++·学习
追随者永远是胜利者2 小时前
(LeetCode-Hot100)239. 滑动窗口最大值
java·算法·leetcode·职场和发展·go
im_AMBER2 小时前
Leetcode 126 两数之和 II - 输入有序数组 | 盛最多水的容器
数据结构·学习·算法·leetcode
科技林总2 小时前
【系统分析师】9.6 安全管理措施
学习
ShiJiuD6668889992 小时前
Java 异常 File
java·开发语言
lxl13072 小时前
C++算法(5)位运算
java·c++·算法
tankeven2 小时前
HJ96 表示数字
c++·算法