C语言架构设计之避免频繁使用malloc

避免频繁malloc,影响程序运行安全及效率减少对malloc的频繁调用,优化C语言架构设计。

C语言架构设计之避免频繁使用malloc

使用malloc()在C语言中是一种灵活而强大的内存管理方式,但同时也带来了一系列的优缺点。了解这些优缺点,并针对性地选择适合项目需求的解决方案,是提高程序性能和稳定性的关键。

优点

1. 灵活性和动态性

malloc()允许程序在运行时根据需要动态分配内存,这种灵活性是静态分配无法比拟的。程序可以根据实际需求分配所需大小的内存,而不是固定大小。

灵活性

  1. 动态大小分配:malloc()允许根据需要分配可变大小的内存块。例如,如果程序需要存储不同数量的整数数组,可以根据用户输入的数组大小使用malloc()动态分配内存。
    // 示例:动态分配整数数组内存  
    int size;  
    printf("请输入数组大小:");  
    scanf("%d", &size);
    int *array = (int *)malloc(size * sizeof(int));
    // 使用数组并在结束时释放内存  
    // …
    free(array); // 释放内存
  1. 动态类型分配:malloc()不限制内存分配的类型。可以为各种类型的数据结构分配内存,比如整数数组、结构体数组等。
    // 示例:动态分配结构体数组内存  
    typedef struct {  
        int id;  
        char name[20];  
    } Person;
    int count;  
    printf("请输入人数:");  
    scanf("%d", &count);
    Person *people = (Person *)malloc(count * sizeof(Person));
    // 使用结构体数组并在结束时释放内存  
    // …
    free(people); // 释放内存

动态性

  1. 运行时分配:malloc()在程序运行时执行内存分配,这使得程序可以根据运行时数据或条件进行灵活的内存管理。
  2. 动态调整内存大小:malloc()的特性之一是通过realloc()函数允许重新分配已分配内存的大小。这使得程序能够动态地调整内存块的大小。
    // 示例:动态调整内存大小  
    int *ptr = (int *)malloc(5 * sizeof(int));
    // 当需要更多内存时重新分配  
    ptr = (int *)realloc(ptr, 10 * sizeof(int));
    // 使用重新分配的内存  
    // …
    free(ptr); // 释放内存

2. 内存使用的精确控制

动态内存分配允许精确地控制内存的使用。程序可以根据不同情况分配所需大小的内存,减少内存浪费,提高内存利用率。

  1. 动态分配所需大小的内存:malloc()允许程序员根据实际需要分配所需大小的内存块。这种精确性可以避免预先分配过多的内存,减少内存浪费。
  2. 减少内存浪费: 通过准确地分配所需大小的内存,程序可以有效地利用内存,避免分配过多或过少的内存。这对于动态数据结构(如动态数组)非常有用,因为可以根据需要动态分配所需大小的内存。
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int num_elements;
        printf("请输入要存储的整数数量:");
        scanf("%d", &num_elements);
    
        // 动态分配所需大小的内存
        int *dynamic_array = (int *)malloc(num_elements * sizeof(int));
    
        if (dynamic_array == NULL) {
            printf("内存分配失败!");
            return 1;
        }
    
        // 使用动态分配的内存
        for (int i = 0; i < num_elements; ++i) {
            dynamic_array[i] = i * 2;
            printf("%d ", dynamic_array[i]);
        }
    
        // 结束后释放内存
        free(dynamic_array);
        return 0;
    }

示例演示了如何使用malloc()动态分配数组所需的精确大小的内存,该大小由用户输入确定。程序根据用户输入的数量动态分配内存,有效地使用所需的内存空间,并在结束时释放已分配的内存块。这种精确的内存控制使得程序能够根据实际需求来分配内存,避免了因分配过多内存导致的内存浪费。

3. 通用性和适用性

malloc()是一种通用的内存分配方式,适用于各种不同大小和类型的内存分配需求。它是C语言中最常用的内存分配函数之一,为程序员提供了强大的工具。

  1. 灵活分配不同大小的内存块:malloc()可以根据需要分配各种不同大小的内存块,这使得它适用于不同大小的数据结构。例如,它可以用于分配单个变量、数组或复杂的数据结构,如链表、树等。
  2. 多种类型的内存分配需求:malloc()不限制于特定类型的内存分配。它可以分配各种数据类型的内存,例如整数、字符、结构体等。
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        // 分配整数变量的内存
        int *int_ptr = (int *)malloc(sizeof(int));
        *int_ptr = 42;
        printf("整数变量的值为:%d\n", *int_ptr);
        free(int_ptr);
    
        // 分配字符数组的内存
        char *char_array = (char *)malloc(10 * sizeof(char));
        strcpy(char_array, "Hello");
        printf("字符数组内容为:%s\n", char_array);
        free(char_array);
    
        // 分配结构体的内存
        typedef struct {
            int id;
            char name[20];
        } Person;
    
        Person *person_ptr = (Person *)malloc(sizeof(Person));
        person_ptr->id = 1;
        strcpy(person_ptr->name, "Alice");
        printf("人员信息:%d, %s\n", person_ptr->id, person_ptr->name);
        free(person_ptr);
    
        return 0;
    }

示例展示了malloc()的通用性和适用性。它分别演示了如何使用malloc()分配整数变量、字符数组和结构体的内存。这些分配展示了malloc()对不同数据类型和大小的适用性,它能够灵活地满足各种内存分配需求,并且在使用完内存后通过free()释放相应的内存空间。

缺点

1. 性能开销

频繁的malloc()free()调用可能导致内存碎片化,增加了系统的内存管理开销。内存碎片化会降低内存分配的效率,增加程序运行时的开销。

  1. 内存碎片化: 频繁的malloc()free()调用可能导致内存碎片化,即可用内存空间分散在已分配和未分配的小块内存之间。这可能导致出现大量不连续的小块空闲内存,使得难以找到足够大的连续内存块。
  2. 频繁调用: 大量频繁的malloc()free()调用会增加内存分配和释放的开销。每次调用都需要系统进行内存管理和分配,因此频繁调用会增加CPU和内存的开销。
  3. 额外开销:malloc()本身也会带来一些额外开销,例如维护内部数据结构、内存分配算法等,这些开销可能会影响程序的性能。
    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int i;
        for (i = 0; i < 10000; ++i) {
            int *ptr = (int *)malloc(sizeof(int));
            free(ptr);
        }
    
        return 0;
    }

示例展示了频繁调用malloc()free()的情况。在一个循环中,程序反复分配和释放内存,这种行为可能导致内存碎片化和频繁的系统调用,增加了额外的内存管理开销。虽然在这个简单的示例中可能不会明显看到性能问题,但在大型程序中,这种频繁的内存分配和释放可能会导致性能下降。

2. 内存泄漏和错误

不正确的内存管理可能导致内存泄漏或访问已释放的内存,这可能导致程序崩溃、不稳定性或安全漏洞。管理动态内存需要仔细处理指针,确保内存的正确释放。

  1. 内存泄漏: 内存泄漏是指程序在动态分配内存后未释放相应内存的情况。如果程序反复分配内存但未释放,则系统的可用内存会逐渐减少,最终可能导致内存耗尽。这可能发生在程序退出时或长时间运行后。
  2. 野指针和访问错误: 如果在释放内存后仍然使用指向已释放内存的指针,或者忘记初始化指针就进行内存访问,可能导致野指针和访问错误。这可能会导致程序崩溃或未定义的行为。
    #include <stdio.h>
    #include <stdlib.h>
    
    void memory_leak_example() {
        int *ptr = (int *)malloc(sizeof(int));
        // 忘记释放内存
    }
    
    void dangling_pointer_example() {
        int *ptr = (int *)malloc(sizeof(int));
        free(ptr);
        *ptr = 10; // 试图访问已释放的内存
    }
    
    int main() {
        memory_leak_example(); // 内存泄漏示例
        dangling_pointer_example(); // 野指针示例
    
        return 0;
    }
    

示例展示了两种内存泄漏和错误的情况。在memory_leak_example()中,程序分配了内存但忘记了释放,导致内存泄漏。在dangling_pointer_example()中,程序释放了内存后,仍然尝试使用已经释放的内存,导致野指针问题。

这些情况都是在动态内存管理中需要特别小心的问题。要避免内存泄漏,务必在不再需要内存时调用free()来释放内存。同时,避免在释放后继续使用指针以防止野指针和访问错误。记得始终谨慎地管理动态分配的内存,以避免这些问题。

3. 潜在的性能瓶颈

在大型程序中,频繁的malloc()free()调用会增加调用开销,影响程序的整体性能。特别是对于实时性要求高或对性能要求苛刻的应用程序来说,这可能成为一个瓶颈。

  1. 碎片化和效率问题: 频繁地分配和释放小块内存可能导致内存碎片化。由于分配和释放的不连续,可能出现大量小的空闲内存块,使得寻找足够大的连续内存块变得困难。这可能会影响内存分配的效率。
  2. 内存分配器的开销:malloc()等内存分配函数本身具有一定的开销。频繁调用这些函数会增加系统调用的开销,包括内存分配器维护、锁和其他管理开销。
    #include <stdio.h>
    #include <stdlib.h>
    
    void allocate_and_free(int n) {
        int i;
        for (i = 0; i < n; ++i) {
            int *ptr = (int *)malloc(sizeof(int));
            free(ptr);
        }
    }
    
    int main() {
        allocate_and_free(10000); // 分配和释放大量小内存块的示例
    
        return 0;
    }
    

示例中,allocate_and_free()函数反复分配和释放大量小内存块。虽然在单次分配和释放时性能影响可能不明显,但在循环中反复调用malloc()free()可能导致内存碎片化和内存分配器开销的累积,从而影响程序的性能。

解决方案

1. 内存池管理

内存池管理是一种常见的解决方案,它可以减少频繁的内存分配和释放。该方法包括预先分配一块固定大小的内存池,在程序初始化时一次性分配所需的内存,然后在运行时重复使用这些内存块。这种方法可以减少内存碎片化,提高内存分配和释放效率。

实现内存池管理需要考虑内存池的大小和管理方式。你可以自己编写一个简单的内存池管理器,也可以使用像Apache Portable Runtime (APR)Boost.Pool这样的第三方库。

  1. 预先分配内存: 内存池在程序启动时预先分配一定数量的内存块,通常是相同大小的内存块。
  2. 重复使用内存块: 当程序需要内存时,它可以从内存池中获取一个已分配但当前未被使用的内存块,并在使用后将其返回给内存池,而不是释放。
  3. 减少内存分配和释放开销: 通过重复使用已分配的内存块,内存池可以减少malloc()free()的调用次数,从而降低内存分配器的开销。
    #include <stdio.h>
    #include <stdlib.h>
    
    #define POOL_SIZE 100 // 内存池大小
    #define BLOCK_SIZE 10 // 内存块大小
    
    typedef struct {
        int is_allocated; // 标记内存块是否已分配
        char data[BLOCK_SIZE];
    } MemoryBlock;
    
    MemoryBlock memory_pool[POOL_SIZE]; // 内存池
    
    void *custom_malloc() {
        for (int i = 0; i < POOL_SIZE; ++i) {
            if (!memory_pool[i].is_allocated) {
                memory_pool[i].is_allocated = 1;
                return memory_pool[i].data;
            }
        }
        return NULL; // 内存池已满
    }
    
    void custom_free(void *ptr) {
        for (int i = 0; i < POOL_SIZE; ++i) {
            if (memory_pool[i].data == ptr) {
                memory_pool[i].is_allocated = 0;
                break;
            }
        }
    }
    
    int main() {
        // 初始化内存池
        for (int i = 0; i < POOL_SIZE; ++i) {
            memory_pool[i].is_allocated = 0;
        }
    
        // 使用自定义内存分配器
        char *ptr1 = (char *)custom_malloc();
        char *ptr2 = (char *)custom_malloc();
    
        custom_free(ptr1); // 释放内存块
        custom_free(ptr2); // 释放内存块
    
        return 0;
    }
    

示例展示了一个简单的内存池管理方案。MemoryBlock结构表示内存块,memory_pool是一个包含多个内存块的数组。custom_malloc()函数模拟自定义的内存分配器,从内存池中获取可用的内存块。custom_free()函数模拟释放内存,将内存块标记为未分配。

实际的内存池管理可能更复杂,可以根据实际需求进行优化和扩展。此外,也有一些第三方库(如jemalloctcmalloc等)提供了更高级、更复杂的内存池管理功能,可用于优化内存分配和释放的性能。

当涉及到内存池管理的第三方库时,有一些常用的库可以提供更高级的内存管理功能。以下是其中一些常见的库以及它们的官方连接:

  1. jemalloc
  • 官方连接:jemalloc GitHub Repository
  • 简介: jemalloc 是一个面向多线程应用的内存分配器,旨在提高内存分配和释放的性能,减少内存碎片化。它被广泛应用于许多大型项目中,如 FreeBSD、Firefox 等。
  1. tcmalloc (Google Performance Tools)
  • 官方连接:Google Performance Tools
  • 简介: tcmalloc 是谷歌开发的一个内存分配器,旨在提高多线程环境下的内存分配和释放性能。它提供了高效的内存管理功能,并被广泛应用于谷歌的许多项目中。
  1. ptmalloc (glibc's malloc)
  • 官方连接:GNU C Library
  • 简介: ptmalloc 是 glibc(GNU C 库)中的默认内存分配器,经过多次改进和优化,提供了良好的性能和功能。它用于大多数基于 GNU/Linux 的系统。

这些库都提供了更高级的内存管理功能,优化了内存分配和释放的性能,并针对多线程环境进行了优化。使用这些库可以有效地管理内存池,减少内存碎片化和系统调用的开销。

优点:减少内存碎片化,提高内存分配和释放效率

  1. 减少内存碎片化: 内存池可以减少内存碎片化,因为它预先分配一组连续的内存块,并重复使用这些块,避免了频繁的内存分配和释放。
  2. 降低系统调用开销: 通过重复使用预先分配的内存块,内存池可以减少系统调用的开销,提高内存分配和释放的效率。
  3. 提高性能: 对于频繁地分配和释放小内存块的情况,内存池可以显著提高性能,因为它避免了内存碎片化和频繁的系统调用。

缺点:需要提前确定内存池的大小,可能需要额外的内存管理

  1. 内存浪费: 如果内存池预先分配了过多的内存块,但实际使用较少,可能会导致内存浪费。
  2. 不适用于所有情况: 内存池适用于对内存使用频繁且可预测的情况。在内存需求不稳定或无法预测的情况下,可能不太适用。
    #include <stdio.h>
    #include <stdlib.h>
    
    #define POOL_SIZE 100 // 内存池大小
    #define BLOCK_SIZE 10 // 内存块大小
    
    typedef struct {
        int is_allocated;
        char data[BLOCK_SIZE];
    } MemoryBlock;
    
    MemoryBlock memory_pool[POOL_SIZE];
    
    void *custom_malloc() {
        for (int i = 0; i < POOL_SIZE; ++i) {
            if (!memory_pool[i].is_allocated) {
                memory_pool[i].is_allocated = 1;
                return memory_pool[i].data;
            }
        }
        return NULL;
    }
    
    void custom_free(void *ptr) {
        for (int i = 0; i < POOL_SIZE; ++i) {
            if (memory_pool[i].data == ptr) {
                memory_pool[i].is_allocated = 0;
                break;
            }
        }
    }
    
    int main() {
        // 初始化内存池
        for (int i = 0; i < POOL_SIZE; ++i) {
            memory_pool[i].is_allocated = 0;
        }
    
        // 使用自定义内存分配器
        char *ptr1 = (char *)custom_malloc();
        char *ptr2 = (char *)custom_malloc();
    
        custom_free(ptr1); // 释放内存块
        custom_free(ptr2); // 释放内存块
    
        return 0;
    }
    

2. 栈上内存或全局/静态变量

尽可能使用栈上的内存,或者使用全局/静态变量来存储长时间需要保留的数据。栈上内存的分配速度更快,而全局/静态变量无需动态分配内存,因此可以避免malloc()free()的开销。

自动变量: 函数内声明的变量通常存储在栈上。它们的生命周期与函数的执行周期相关联,当函数退出时,变量自动被销毁。

    void func() {
        int x = 10; // 栈上的自动变量
        // ...
    }

函数调用: 函数调用时,函数的参数、局部变量和返回地址等信息存储在栈上。每个函数调用都会在栈上分配一块内存,称为栈帧,在函数返回时会被释放。

全局变量: 在函数外部声明的变量是全局变量,它们存储在全局内存中。它们的生命周期贯穿整个程序执行周期。

    int global_var = 5; // 全局变量,存储在全局内存中

静态变量: 使用static关键字声明的变量,即使在函数内部声明,也存储在全局内存中。静态变量具有静态存储期,其生命周期延长到整个程序执行过程中。

    void func() {
        static int static_var = 20; // 静态变量,存储在全局内存中
        // ...
    }

示例:

    #include <stdio.h>
    
    // 全局变量,存储在全局内存中
    int global_var = 5;
    
    void func() {
        // 自动变量,存储在栈上
        int x = 10;
    
        // 静态变量,存储在全局内存中
        static int static_var = 20;
    
        printf("栈上的自动变量 x:%d\n", x);
        printf("全局变量 global_var:%d\n", global_var);
        printf("静态变量 static_var:%d\n", static_var);
    }
    
    int main() {
        func();
        return 0;
    }

示例展示了栈上内存、全局变量和静态变量的使用。函数内部的自动变量(x)存储在栈上,函数外部的全局变量(global_var)和函数内部的静态变量(static_var)存储在全局内存中。它们在内存中的存储位置和生命周期有所不同,根据需要进行合适的选择。

优点:栈上内存分配速度快,全局/静态变量无需动态分配内存

  1. 自动管理: 栈上的内存由编译器自动管理,变量的生命周期与其作用域(通常是函数)相关联,当作用域结束时会自动释放,不需要手动管理内存释放。
  2. 快速访问: 栈上内存的访问速度较快,因为栈通常是线性结构,变量的访问速度较高。
  3. 全局可访问性: 全局变量对整个程序可见,可在程序中的任何地方访问。
  4. 生命周期长: 全局/静态变量的生命周期贯穿整个程序执行周期,不受函数作用域的限制,能够保持状态和数值。

缺点:可用内存大小有限,不能满足所有需求

  1. 大小限制: 栈的大小通常受限于操作系统或编译器设置的限制,较小的栈可能限制了可用内存的大小,因此不能存储大量的数据。
  2. 局部生存期: 变量在函数退出后会被自动销毁,这可能导致变量在函数调用之间无法保持状态。
  3. 不易控制: 全局变量可能被多个函数或模块修改,使得程序的状态变得难以预测和控制,可能导致不易维护的代码。
  4. 内存泄漏风险: 静态变量的生命周期长,如果不恰当地使用可能导致内存泄漏,因为它们只有在程序结束时才被释放。

3. 内存重用

内存重用是指在程序执行期间有效地重复利用已分配的内存,而不是频繁地分配和释放内存。这可以通过各种技术来实现,如对象池、缓存、自定义内存管理等。让我详细解释内存重用的概念,并提供一个简单的示例。

  1. 对象池: 维护一个预分配的对象集合,对象可以被重复使用而不是每次需要时都进行新的分配和释放。
  2. 缓存: 缓存机制可以重复利用已经加载的数据或资源,减少重复加载和释放的开销。
  3. 自定义内存管理: 实现自己的内存分配和释放策略,可以重复利用已分配的内存,避免频繁调用系统的内存分配函数。
    #include <stdio.h>
    #include <stdlib.h>
    
    #define MAX_OBJECTS 100 // 对象池的大小
    
    typedef struct {
        int id;
        // 其他属性...
    } Object;
    
    Object object_pool[MAX_OBJECTS]; // 对象池
    
    int next_available_index = 0; // 下一个可用的索引位置
    
    Object *allocate_object() {
        if (next_available_index < MAX_OBJECTS) {
            Object *obj = &object_pool[next_available_index];
            obj->id = next_available_index;
            next_available_index++;
            return obj;
        }
        return NULL; // 对象池已满
    }
    
    void free_object(Object *obj) {
        // 重置对象的状态以便重用
        obj->id = -1; // 例如,将 ID 重置为无效值
        // 不移动索引,仅标记为可重用状态
    }
    
    int main() {
        // 使用对象池重复分配和释放对象
        Object *obj1 = allocate_object();
        Object *obj2 = allocate_object();
    
        // 使用 obj1 和 obj2
    
        free_object(obj1);
        free_object(obj2);
    
        return 0;
    }
    

示例中,object_pool是一个对象池,用于存储Object结构的对象。allocate_object()函数从对象池中获取可用的对象,而free_object()函数将对象标记为可重用状态。这种方式可以有效地重复利用对象池中的对象,而不是频繁地进行内存分配和释放。实际的内存重用策略可以根据具体需求进行调整和扩展,以优化内存管理。

优点:减少了频繁的内存分配和释放

  1. 性能提升: 通过减少内存分配和释放次数,可以提高程序性能,减少系统调用和内存管理开销。
  2. 减少碎片化: 内存重用可以降低内存碎片化,避免大量小块内存的不连续分配。
  3. 资源有效利用: 有效的内存重用能够充分利用已分配的内存,避免频繁地从系统申请新的内存。

缺点:需要更复杂的内存管理,可能增加代码复杂性

  1. 复杂性: 实现内存重用方案可能需要复杂的逻辑和管理,特别是在多线程环境或需要处理共享资源时更为复杂。
  2. 潜在的错误: 不正确的对象状态管理可能导致内存泄漏、未初始化的数据或不一致的对象状态,这可能会引入潜在的错误和难以发现的 bug。

4. 使用第三方库或工具

在C语言中,有一些第三方库或工具可用于内存管理,提供了更高级的功能和效率。其中,一些常见的工具包括jemalloc、tcmalloc、dlmalloc等。这些库提供了更优化、更灵活的内存管理机制,有助于提高内存分配和释放的性能,并解决了一些标准库malloc函数的一些限制。让我详细介绍一下jemalloc和tcmalloc,以及它们的简单示例。

Jemalloc

是一个用户空间的内存分配器,专门设计用于多线程应用程序。它以性能和碎片化程度的降低而闻名,并且被广泛用于多个大型项目中。

官方连接:

  • jemalloc GitHub Repository
  • 优点:
  • 减少内存碎片化。
  • 高并发性能。
  • 支持多种平台。
  • 缺点:
  • 配置和调整可能需要一些学习成本。
  • 示例:
    #include <jemalloc/jemalloc.h>
    #include <stdio.h>
    
    int main() {
        void *ptr = je_malloc(10 * sizeof(int)); // 分配内存
        if (ptr == NULL) {
            printf("内存分配失败\n");
        } else {
            printf("内存分配成功\n");
            // 使用内存
            je_free(ptr); // 释放内存
        }
        return 0;
    }

Tcmalloc

是 Google 提供的高效内存分配器,专门为多线程应用程序而设计,旨在提高多线程环境下的性能。

官方连接:

  • Google Performance Tools
  • 优点:
  • 优化的多线程性能。
  • 减少内存碎片化。
  • 可用于大型应用程序。
  • 缺点:
  • 集成到非Google环境中可能有一些挑战。
  • 示例:
    #include <gperftools/tcmalloc.h>
    #include <stdio.h>
    
    int main() {
        void *ptr = tc_malloc(10 * sizeof(int)); // 分配内存
        if (ptr == NULL) {
            printf("内存分配失败\n");
        } else {
            printf("内存分配成功\n");
            // 使用内存
            tc_free(ptr); // 释放内存
        }
        return 0;
    }

Read more

C语言架构设计之程序解耦

C语言架构设计之程序解耦

高耦合带来的问题 高耦合度的 C 语言程序会导致以下影响: 可扩展性降低 高耦合度会显著降低程序的可扩展性,这意味着在修改或增加功能时,需要更多的时间和资源,因为一个模块的变化可能需要影响到多个模块。这里有一个示例来说明高耦合度如何降低可扩展性: 假设有一个简单的库存管理系统,包含两个模块:Inventory(库存管理)和 Sales(销售管理)。它们高度耦合,Sales 模块直接依赖于 Inventory 模块。 // Inventory 模块 #include <stdio.h> int available_quantity = 100; void update_quantity(int sold_quantity) { available_quantity -= sold_quantity; } // Sales 模块 #include <stdio.h&

By jackie ma
Ubuntu 22.04 安装 Z-Shell (ZSH) 跟 Oh-My-Zsh

Ubuntu 22.04 安装 Z-Shell (ZSH) 跟 Oh-My-Zsh

Z shell 是有史以来功能最强大的 shell 之一,也是速度最快的 shell 之一。它提供的功能是你在其他任何地方都找不到的,比如内置拼写检查、代码语法高亮等。你甚至可以对命令提示符进行配置,以显示有关系统状态的有用信息,而无需键入任何内容。例如:拼写更正,文件和命令的制表符补全等。 更新系统 执行如下指令更新系统软件: $ sudo apt update && sudo apt dist-upgrade -y 执行如下指令安装相关软件: $ sudo apt install build-essential curl file git 因为我已经按转过,没有提示Y/N。请选择Y继续安装。 安装zsh 现在系统已经是最新状态,并且安装了需要的第三方软件,执行如下指令安装zsh $ sudo apt install zsh 提示是否继续,请输入Y继续安装。

By jackie ma
ubuntu 22.04安装ibus中文输入法

ubuntu 22.04安装ibus中文输入法

安装中文语言支持 打开“设置”,“Region & Language”,“Manage Installed Languages” 点击“Install/Remove Languages…” 选择“Chinese(simplified)”,勾选之后点击“Apply”等待安装完成 之后,在“Keyboard Input Method system”,选择“IBus”,“Close” 再可以执行如下操作安装相关的软件 $ sudo apt-get install ibus ibus-clutter ibus-gtk ibus-gtk3 ibus-qt4 $ im-config -s ibus $ sudo apt-get install ibus-pinyin $ ibus-setup 最后执行sudo reboot重启系统 添加中文输入法 重启起来后,打开“

By jackie ma
  •   渝ICP备2023013474号-1   •   渝公网安备50019002504014号