通过例子深入了解c++/c的构建系统

这篇具有很好参考价值的文章主要介绍了通过例子深入了解c++/c的构建系统。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Understanding C/C++ Build system by building a simple project

C/C++ is the mother of many popular programming languages out there today, all the fancy programming languages we use today like Python, JavaScript are built using C/C++. For example, the standard python interpreter CPython is built using C and the most popular JavaScript implementation V8 is built using C/C++, C/C++ also powers most of the underlying libraries used by Node.js, In other words, C/C++ powers most of the open source software ever written by humans. One of the main reasons we prefer high level languages like Python is because of the robust package management tools they provide, we don't have to worry about managing dependencies anymore pip automatically manages it for us. Same case holds true for JavaScript as well. These languages also have robust build systems which allow us to build and ship the software more easily.

C/C++ also has few popular build systems like cmake and bazel which manages dependencies automatically, but in this post, we will be compiling a C/C++ project without making use of these tools in order to understand how things work internally.

We will be building a simple system logger that logs total free RAM memory of the system every 5 seconds.

First things first! Let's create a project structure:

Project structure has to be easily understandable and should isolate different functionalities as much as possible to avoid confusion. No one will ever stop us from using our own project structure, but most of the open source projects built with C/C++ use this structure :

project_root
  - include
  - src
      - module-1
      - module-2
      - module-n
      - main.c/main.cc (depends on the project)
  - Makefile
  - README
  - LICENSE
  - misc files

Let's have a look at what each and every file/directory means:

  1. include - This is the place where all our header files live.
  2. src - The directory that contains all our source code. We can have multiple sub-directories/modules inside src. Also we can have a main function file inside src.
  3. Makefile : Makefiles are used by make command, we will be using make to build our project.

For our project we will have the following structure:

memlogger
   - bin  - will explain the need for this
   - include
       - free_memory_api.h
       - file_writer.h
   - src
       - free_memory_api
          - free_memory_api.c
       - file_writer
          - file_writer.c
       - main.c
   - Makefile

Let's Code

We can start writing code once we are done with the project structure setup. I will not be explaining the code in depth to avoid writing very long post, but we will stress more on the concepts.

What are header files?

Header files are blueprints of our actual C code. For every C module we write, it is a good practice to export the header-file. These header files are used by the compiler to understand what all functions are exported by a module. Once compilation is done, header files are not used anywhere. The actual use of header files comes into picture when our project/module is used as a module in some other project, other programmers can simply include our header file to use the function declarations we exported.

Let us create free_memory_api.h as per the structure :

#ifndef __FREE_MEMORY_API
#define __FREE_MEMORY_API
//this is our API function which returns free memory in bytes
unsigned long long get_free_system_memory();
#endif

Let us create file_writer.h which declares file writer API

#ifndef __FILE_WRITER_API
#define __FILE_WRITER_API

#include <stdio.h>
//opens the log file for writing
FILE * open_log_file(char * path);
// we will use this function to write contents to the log file
void write_log_to_file(FILE * file, unsigned long long free_memory);
//closes the log file
void close_log_file(FILE * file);
#endif

Let's define these APIs:

We declared what all APIs we need, but we did not write the underlying code for those APIs. We will be writing the code for file logging and getting free memory from the system. Before writing the code, we have to import the blueprint we declared before.
file_writer.c

#include "file_writer.h"

// Open the log-file in append mode and return it
FILE * open_log_file(char * file_path) {
    FILE * fp = fopen(file_path, "a");
    return fp;
}

// Close the file 
void close_log_file(FILE * fp) {
    if(fp) {
        fclose(fp);
    }
}

//write log entry into the file
void write_log_to_file(FILE * fp, unsigned long long free_memory) {
    if(fp) {
        fprintf(fp, "free_memory=%llu\n", free_memory);
    }
}

Now let us define the free memory api i.e free_memory_api.c:

#include <sys/sysinfo.h>
#include "free_memory_api.h"

unsigned long long get_free_system_memory() {
    struct sysinfo info;
    if (sysinfo(&info) < 0) {
         return 0;
    }
    return info.freeram;;
}

And finally, main.c

#include "file_writer.h"
#include "free_memory_api.h"

#include <unistd.h>

int main(int argc, char **argv) {
    if (argc < 2) {
        printf("Provide log file name\n");
        return 0;
    } 

    unsigned long long free_memory = 0;
    while(1) {
        free_memory = get_free_system_memory();
        FILE * log = open_log_file(argv[1]);
        write_log_to_file(log, free_memory);
        close_log_file(log);
        sleep(5);
    }
}

Let's start building the project

Now that we have written the code, it is time to compile the project. Now we have multiple modules in our project. These modules can be linked together to build a standalone executable, or we can build individual modules alone as shared libraries and link them together at runtime.

Building a static-monolithic executable

In this section, we build a single binary that can be shipped, there are many ways we can build a C/C++ project. In this post we will only build a standalone executable which is the most easiest way of building a C project.
We make use of make a Linux command-line utility that can automate any task, it is a series of shell commands which can be grouped and tagged under a name to perform a specific task. We can write multiple such tasks conveniently using a Makefile. Let's see our Makefile now.

COMPILER=gcc

file_writer:
    @$(COMPILER) -c src/file_writer/*.c -Iinclude/ -o bin/file_writer.o
    @echo "Built file_writer.o"

free_memory_api:
    @$(COMPILER) -c src/free_memory_api/*.c -Iinclude/ -o bin/free_memory_api.o 
    @echo "Built free_memory_api.o"

project:
    $(COMPILER) -c src/main.c -Iinclude/ -o bin/main.o
    @$(COMPILER) bin/free_memory_api.o bin/file_writer.o bin/main.o -o memlogger
    @echo "Finished building memlogger"

Even though we can build the entire project in a single command, I have divided this into three phases.

  1. file_writer : This make rule will generate file_writer.o object file under ./bin.
  2. free_memory_api : This rule generates free_memory_api.o under ./bin.
  3. project : This builds the entire project,it generates main.o and links main.o with other two object files to create a standalone executable called memlogger.

Let's execute these commands with make:
Step-1. file_writer.o

   make file_writer

Step-2. free_memory_api.o

   make free_memory_api

Step-3. Final binary:

   make project

Executing the binary:

We can execute the binary by running it like a normal linux executable:

memlogger logs.txt

After 15 seconds, we see 3 entries in the log file:

free_memory=8322523136
free_memory=8330776576
free_memory=8335728640

Which is approximately 8GB free out of 16GB RAM and it is correct. hurray! we created a small system logger.

What happened under the hood??

It is important to understand the build process we used here. To understand that, we need to know the concept of object files.

What we did in the Makefile??

We defined three rules, each rule builds a module, the third rule goes ahead by one step and links all the three modules.
We used gcc compiler, (g++ for C++ projects). Options used:

  1. -c : This tells the compiler to only compile and don't perform linking, since we are explicitly linking the object files in Step-3.
  2. -I : Since we defined our own headers, we have to provide it to the compiler during compile time, by default compiler searches for these headers in standard locations, we can also tell the compiler to include our custom location for resolving the headers using -I.
  3. -o : The output file name.

What are object files?

An object file is a linux ELF - Executable and linkable format binary produced by the compiler. ELF is designed by POSIX standards and all Linux distributions can understand what an object file is. What this object file contains in layman terms is a mapping table and symbol definitions.

  1. mapping-table or Symbol table : The mapping table contains a set of symbols defined by the object file and an offset in text segment where actual code for the symbols are defined.
  2. Symbol definitions : This section contains machine code of all our functions. So, to get the machine code of a function/symbol we do these two steps: First, we lookup the symbol table of the object file to get its offset in the text segment. Second, we go to the text segment and obtain its code.

These are just layman terms, ELF has standard definitions for mapping-table and Segment definitions which are confusing for a beginner.

Let's see the object file contents of file_writer.o, we use a tool called readelf which is default in all the linux systems.

readelf -h bin/file_writer.o

Output:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          1168 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         13
  Section header string table index: 12

These are ELF headers. Now let's see the Symbol table (or mapping table in our terms)

readelf --syms bin/file_writer.o

Output:

Symbol table '.symtab' contains 16 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS file_writer.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000    41 FUNC    GLOBAL DEFAULT    1 open_log_file
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND fopen
    12: 0000000000000029    34 FUNC    GLOBAL DEFAULT    1 close_log_file
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND fclose
    14: 000000000000004b    54 FUNC    GLOBAL DEFAULT    1 write_log_to_file
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND fprintf

As we can see in the table, we have entries for open_log_fileclose_log_filewrite_log_to_file which are the API functions we defined. Hurray! Our object file is correct. Also, if you observe carefully, we see the presence of fclosefopen and fprintf and they are prefixed with UND which means these symbol addresses are not known yet, but C/C++ runtime resolves them during linking which is in step-3, it either links to these functions statically or dynamically during the runtime, we will see the concept of shared libraries in the next part.

Similarly, we can run the same command for free_memory_api.o to see it's symbol table. We get the output as :

Symbol table '.symtab' contains 14 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS free_memory_api.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
     6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
     9: 0000000000000000    89 FUNC    GLOBAL DEFAULT    1 get_free_system_memory
    10: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND sysinfo
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __stack_chk_fail

We can see get_free_system_memory as a FUNC and sysinfo which is undefined.

What we did in the final step?

In the first two steps, we compiled the modules and generated the object files, but they can't be executed because they don't have the main function definition which is the entry-point of any C/C++ program. We have two commands in Makefile under project rule (step-3), the first command only compiles the main.c file into main.o, let's try to run it, it should run because it has main function.

./main.o

Output:

bash: ./bin/main.o: cannot execute binary file: Exec format error

We cannot run it because it is not an executable file, it is an object file, the final step is still remaining which links all the three object files and generate the final executable binary.
Before that we will try to link only main.o and discard the remaining two modules, let's see what happens:

gcc bin/main.o -o memlogger

Output:

bin/main.o: In function `main':
main.c:(.text+0x36): undefined reference to `get_free_system_memory'
main.c:(.text+0x4d): undefined reference to `open_log_file'
main.c:(.text+0x64): undefined reference to `write_log_to_file'
main.c:(.text+0x70): undefined reference to `close_log_file'
collect2: error: ld returned 1 exit status

This is exactly what was supposed to happen, the executable file needs the following functions but don't know where they are. So we need to link it with remaining two object files.

gcc bin/free_memory_api.o bin/file_writer.o bin/main.o -o memlogger

Now the compiler looks into the Symbol tables of file_writer.o and free_memory_api.o to resolve the functions which were undefined in our previous command. Since the Symbol tables of these two object files defines those symbols/functions the linking is successful and the final executable is generated.

Let's see the mapping table or Symbol table of our memlogger binary:

readelf --syms memlogger

Output:

    26: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    27: 0000000000000780     0 FUNC    LOCAL  DEFAULT   14 deregister_tm_clones
    28: 00000000000007c0     0 FUNC    LOCAL  DEFAULT   14 register_tm_clones
    29: 0000000000000810     0 FUNC    LOCAL  DEFAULT   14 __do_global_dtors_aux
    30: 0000000000201010     1 OBJECT  LOCAL  DEFAULT   24 completed.7698
    31: 0000000000200d88     0 OBJECT  LOCAL  DEFAULT   20 __do_global_dtors_aux_fin
    32: 0000000000000850     0 FUNC    LOCAL  DEFAULT   14 frame_dummy
    33: 0000000000200d80     0 OBJECT  LOCAL  DEFAULT   19 __frame_dummy_init_array_
    34: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS free_memory_api.c
    35: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS file_writer.c
    36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    37: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    38: 0000000000000c5c     0 OBJECT  LOCAL  DEFAULT   18 __FRAME_END__
    39: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
    40: 0000000000200d88     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_end
    41: 0000000000200d90     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    42: 0000000000200d80     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_start
    43: 0000000000000a78     0 NOTYPE  LOCAL  DEFAULT   17 __GNU_EH_FRAME_HDR
    44: 0000000000200f80     0 OBJECT  LOCAL  DEFAULT   22 _GLOBAL_OFFSET_TABLE_
    45: 0000000000000a30     2 FUNC    GLOBAL DEFAULT   14 __libc_csu_fini
    46: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    47: 0000000000201000     0 NOTYPE  WEAK   DEFAULT   23 data_start
    48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@@GLIBC_2.2.5
    49: 0000000000201010     0 NOTYPE  GLOBAL DEFAULT   23 _edata
    50: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fclose@@GLIBC_2.2.5
    51: 0000000000000a34     0 FUNC    GLOBAL DEFAULT   15 _fini
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@@GLIBC_2
    53: 00000000000008dc    34 FUNC    GLOBAL DEFAULT   14 close_log_file
    54: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5
    55: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    56: 0000000000201000     0 NOTYPE  GLOBAL DEFAULT   23 __data_start
    57: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fprintf@@GLIBC_2.2.5
    58: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    59: 0000000000201008     0 OBJECT  GLOBAL HIDDEN    23 __dso_handle
    60: 0000000000000a40     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used
    61: 000000000000085a    89 FUNC    GLOBAL DEFAULT   14 get_free_system_memory
    62: 00000000000009c0   101 FUNC    GLOBAL DEFAULT   14 __libc_csu_init
    63: 0000000000201018     0 NOTYPE  GLOBAL DEFAULT   24 _end
    64: 0000000000000750    43 FUNC    GLOBAL DEFAULT   14 _start
    65: 0000000000201010     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    66: 0000000000000934   130 FUNC    GLOBAL DEFAULT   14 main
    67: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND fopen@@GLIBC_2.2.5
    68: 00000000000008fe    54 FUNC    GLOBAL DEFAULT   14 write_log_to_file
    69: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sysinfo@@GLIBC_2.2.5
    70: 0000000000201010     0 OBJECT  GLOBAL HIDDEN    23 __TMC_END__
    71: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    72: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@@GLIBC_2.2.5
    73: 00000000000008b3    41 FUNC    GLOBAL DEFAULT   14 open_log_file
    74: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.2
    75: 0000000000000698     0 FUNC    GLOBAL DEFAULT   11 _init

Since the Symbol table is very big, I have pasted entries starting from 26. As you can see our final executable has all our definitions from all the three modules and also there are no UND symbols, these symbols are now replaced with something like fopen@@GLIBC_2.2.5. This means, the code for these functions are not copied into our binary, instead they have to be resolved during the runtime, it is the responsibility of Linux loader ld.so to link these symbols dynamically during runtime.

So this is it! We are done with the first part of the Post. If you are reading this line. I would like to really thank you for reading the entire Post, keep the compliments even if you skipped everything and came here directly.

Thank you :) Have a good time.文章来源地址https://www.toymoban.com/news/detail-444357.html

到了这里,关于通过例子深入了解c++/c的构建系统的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • “深入了解Spring Boot:构建高效、可扩展的Java应用程序“

    标题:深入了解Spring Boot:构建高效、可扩展的Java应用程序 摘要:Spring Boot是一款快速构建Java应用程序的开发框架,它基于Spring框架,提供了一系列的自动化配置和约定,使得开发者能够更快速、更高效地构建出高质量的应用程序。本文将深入探讨Spring Boot的核心特性和使用

    2024年02月14日
    浏览(57)
  • “深入了解API和Python的完美结合:构建强大、灵活的应用程序“

    引言: 在当前科技高速发展的时代,应用程序是各行各业的重要组成部分。而API(Application Programming Interface)作为不同系统之间实现通信和数据交换的桥梁,具有至关重要的作用。而Python作为一种强大而灵活的编程语言,与API的结合更是创造了无限的可能性。本文将深入探讨

    2024年02月16日
    浏览(43)
  • 【深入了解PyTorch】PyTorch生态系统概述

    PyTorch是一个开源的深度学习框架,被广泛应用于研究和工业界的实际应用中。PyTorch生态系统提供了许多有用的工具和库,用于简化和加速深度学习任务的开发过程。本文将介绍PyTorch生态系统中的一些重要组成部分,包括TorchVision、TorchText、TorchAudio等,并讨论如何将它们与

    2024年02月16日
    浏览(34)
  • 【Linux操作系统】深入了解系统编程gdb调试工具

    在软件开发过程中,调试是一个非常重要的步骤。无论是在开发新的软件还是维护现有的代码,调试都是解决问题的关键。对于Linux开发者来说,GDB是一个非常有用的调试工具。在本文中,我们将探讨Linux中使用GDB进行调试的方法和技巧。 GDB是GNU调试器的缩写,是一个功能强

    2024年02月14日
    浏览(49)
  • 【Linux】深入了解冯诺依曼体系结构与操作系统

    目录 导读 🌞专栏导读 🌞冯·诺依曼 🌞冯诺依曼体系结构 🌛木桶效应 🌞操作系统(Operator System) 🌛概念 🌛设计OS的目的 🌛系统调用和库函数概念  六一儿童节快到了,提前祝大家六一儿童节快乐哦!!! 六一儿童节的即将来到,祝愿我家宝🧡,愿你童心未泯,无忧无虑

    2024年02月06日
    浏览(65)
  • 构建定制化的嵌入式 Linux 系统:了解 Buildroot

    引言 Linux Buildroot及其在嵌入式系统开发中的重要性 为什么使用Buildroot可以帮助简化Linux系统构建过程 Buildroot概览 Buildroot的基本概念和原理 Buildroot与其他类似工具的区别,如Yocto Project 准备工作 下载和安装Buildroot 介绍Buildroot的目录结构 Linux Buildroot是一个开源项目,旨在帮助

    2024年02月13日
    浏览(69)
  • 深入了解Linux下的dmesg指令:查看系统内核日志的利器

    简介: 在Linux系统中,了解系统的内核信息和故障诊断是至关重要的。而dmesg指令就是一个强大的工具,它可以帮助我们查看系统内核的日志信息,了解系统的运行状态和故障原因。本篇博客将以通俗易懂的方式,详细解析dmesg指令的功能和用法,并提供示例代码和参考链接,

    2024年02月08日
    浏览(54)
  • 借助一个例子简要了解机器学习

    环境: azureml_py boot_size harness_size 0 39 58 1 38 58 2 37 52 3 39 58 4 38 57 5 35 52 6 37 55 7 36 53 8 35 49 9 40 54 10 40 59 11 36 56 12 38 53 13 39 58 14 42 57 15 42 58 16 36 56 17 36 51 18 35 50 19 41 59 20 42 59 21 38 59 22 37 55 23 35 50 24 40 55 25 36 52 26 35 53 27 39 54 28 41 61 29 37 56 30 35 55 31 41 60 32 39 57 33 41 56 34 42 61 35 42 58 36 3

    2024年01月25日
    浏览(40)
  • 深入理解 Vue Router:构建可靠的前端路由系统

    前端路由是指在单页应用(SPA,Single Page Application)中,通过客户端的路由机制实现页面切换和导航的方式。它将不同的 URL 映射到相应的组件或页面,并通过页面切换而无需重新加载整个页面。 前端路由的特点包括: 基于 URL:前端路由使用 URL 来标识不同的页面或视图状态

    2024年02月11日
    浏览(46)
  • 深入浅出Rust内存安全:构建更安全、高效的系统应用

    在过去几年中,Rust编程语言以其独特的安全保障特性和高效的性能,成为了众多开发者和大型科技公司的新宠。尤其是其内存安全特性,成为了广泛讨论和赞扬的焦点。本文旨在深入探讨内存安全的概念、Rust在内存安全方面的独到之处,以及这些特性对系统开发的深远影响

    2024年02月19日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包