【C语言回顾】动态内存管理

news2024/11/19 3:49:55

  • 前言
  • 1. 动态内存管理初步概述
  • 2. malloc
  • 3. calloc
  • 4. realloc
  • 5. free
  • 6. 常见的动态内存错误
  • 7. 柔性数组
  • 8. 程序内存区域划分
  • 结语

在这里插入图片描述
在这里插入图片描述

#include<GUIQU.h>
int main
{
上期回顾: 【C语言回顾】联合和枚举
个人主页:C_GUIQU
专栏:【C语言学习】
return 一键三连;
}

在这里插入图片描述

前言

各位小伙伴大家好!上期小编给大家讲解了C语言中的联合和枚举,接下来我们讲解一下动态内存管理!

1. 动态内存管理初步概述

在C语言中,动态内存管理指的是在程序运行时向操作系统请求和释放内存的过程。C语言提供了几个标准库函数来支持动态内存管理,这些函数定义在头文件stdlib.h中。

以下是动态内存管理的关键函数及其用途:

  1. malloc() - 分配指定大小的内存块,返回一个指向void类型的指针,因此需要类型转换。如果分配失败,返回NULL。
    int *ptr = (int*)malloc(n * sizeof(int)); // 分配n个整数的空间
    if (ptr == NULL) {
        // 处理内存分配失败的情况
    }
    
  2. calloc() - 类似于malloc,但它会清除分配的内存,将其初始化为0。它接受两个参数,分别是元素的数量和每个元素的大小。
    int *ptr = (int*)calloc(n, sizeof(int)); // 分配并初始化n个整数
    if (ptr == NULL) {
        // 处理内存分配失败的情况
    }
    
  3. realloc() - 用于调整之前分配的内存块的大小。它接受两个参数,一个是原始指针,另一个是新的大小。如果新的内存分配失败,realloc会返回NULL,但原始的数据不会被释放。
    int *new_ptr = (int*)realloc(ptr, new_size * sizeof(int)); // 调整内存大小
    if (new_ptr == NULL) {
        // 处理内存分配失败的情况
        // 注意:此时ptr仍然包含有效的数据
    } else {
        ptr = new_ptr; // 如果成功,更新指针
    }
    
  4. free() - 释放之前分配的内存。这是一个非常重要的函数,因为如果不释放不再使用的内存,会导致内存泄漏。
    free(ptr); // 释放内存
    ptr = NULL; // 将指针设置为NULL,避免悬空指针
    

动态内存管理的使用需要注意以下几点:

  • 内存分配失败的处理:当内存分配失败时,程序应该有适当的错误处理机制。
  • 指针类型转换:从malloc、calloc或realloc返回的指针需要转换为正确的类型。
  • 内存泄漏:使用free函数释放不再需要的内存,以避免内存泄漏。
  • 内存越界:避免访问分配的内存之外的地址,这可能导致未定义的行为。
  • 内存碎片:频繁的分配和释放可能导致内存碎片,这可以通过内存池等技术来减少。

2. malloc

malloc 是 C 语言标准库中的一个函数,用于在堆上动态分配指定大小的内存块。它是最常用的动态内存分配函数之一。malloc 函数的原型定义在 <stdlib.h> 头文件中,如下所示:

void *malloc(size_t size);

参数 size 表示要分配的内存字节数。malloc 函数返回一个指向 void 类型的指针,因此在使用时通常需要进行类型转换,以匹配所需的数据类型。如果内存分配失败,malloc 返回 NULL 指针。

下面是 malloc 函数的一个示例:

#include <stdio.h>
#include <stdlib.h>
int main() {
    int *array;
    int size = 10; // 假设我们想要一个包含10个整数的数组
    // 分配内存
    array = (int*)malloc(size * sizeof(int));
    if (array == NULL) {
        fprintf(stderr, "内存分配失败\n");
        exit(EXIT_FAILURE);
    }
    // 使用分配的内存
    for (int i = 0; i < size; i++) {
        array[i] = i;
    }
    // 打印数组元素
    for (int i = 0; i < size; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
    // 释放内存
    free(array);
    array = NULL; // 避免悬空指针
    return 0;
}

在这个示例中,我们使用 malloc 分配了一个足够大的内存块来存储 10 个整数。然后,我们检查 malloc 是否返回了 NULL,如果是,则打印错误消息并退出程序。接下来,我们初始化数组并打印其内容。最后,我们使用 free 函数释放内存,并将指针设置为 NULL,以避免悬空指针的问题。

使用 malloc 时应该注意以下几点:

  • 总是检查 malloc 的返回值是否为 NULL,以处理内存分配失败的情况。
  • malloc 返回的指针进行正确的类型转换。
  • 分配的内存未经初始化,可能包含随机数据。如果需要初始化,可以使用 calloc 函数,或者手动初始化内存。
  • 使用 free 函数释放不再需要的内存,以避免内存泄漏。
  • 分配的内存应该在同一个作用域内释放,或者在需要的情况下,确保在适当的时机释放。

3. calloc

calloc 是 C 语言标准库中的一个函数,用于在堆上动态分配指定数量的元素大小的内存块,并将分配的内存初始化为 0。calloc 函数的原型定义在 <stdlib.h> 头文件中,如下所示:

void *calloc(size_t num, size_t size);

calloc 函数接受两个参数:

  • num:要分配的元素数量。
  • size:每个元素的大小(以字节为单位)。
    calloc 函数返回一个指向 void 类型的指针,因此在使用时通常需要进行类型转换,以匹配所需的数据类型。如果内存分配失败,calloc 返回 NULL 指针。

下面是 calloc 函数的一个示例:

#include <stdio.h>
#include <stdlib.h>
int main() {
    int *array;
    int size = 10; // 假设我们想要一个包含10个整数的数组
    // 分配并初始化内存
    array = (int*)calloc(size, sizeof(int));
    if (array == NULL) {
        fprintf(stderr, "内存分配失败\n");
        exit(EXIT_FAILURE);
    }
    // 使用分配的内存
    for (int i = 0; i < size; i++) {
        printf("%d ", array[i]); // 由于calloc初始化为0,这里将打印0
    }
    printf("\n");
    // 释放内存
    free(array);
    array = NULL; // 避免悬空指针
    return 0;
}

在这个示例中,我们使用 calloc 分配了一个足够大的内存块来存储 10 个整数,并将内存初始化为 0。然后,我们检查 calloc 是否返回了 NULL,如果是,则打印错误消息并退出程序。接下来,我们打印数组的内容,由于 calloc 已经将内存初始化为 0,所以这里将打印出一串 0。最后,我们使用 free 函数释放内存,并将指针设置为 NULL,以避免悬空指针的问题。

使用 calloc 时应该注意以下几点:

  • 总是检查 calloc 的返回值是否为 NULL,以处理内存分配失败的情况。
  • calloc 返回的指针进行正确的类型转换。
  • 使用 free 函数释放不再需要的内存,以避免内存泄漏。
  • 分配的内存已经在 calloc 中初始化为 0,这对于某些需要清零内存的应用场景是非常有用的。

4. realloc

realloc 是 C 语言标准库中的一个函数,用于调整之前通过 malloccallocrealloc 分配的内存块的大小。realloc 函数可以增加或减少内存块的大小,并且可以选择性地复制旧数据到新位置。realloc 函数的原型定义在 <stdlib.h> 头文件中,如下所示:

void *realloc(void *ptr, size_t size);

realloc 函数接受两个参数:

  • ptr:指向之前分配的内存块的指针,或者如果是第一次分配,则为 NULL
  • size:新内存块的大小(以字节为单位)。
    realloc 函数返回一个指向 void 类型的指针,因此在使用时通常需要进行类型转换,以匹配所需的数据类型。如果内存分配失败,realloc 返回 NULL 指针。如果 ptr 参数为 NULL,则 realloc 的行为类似于 malloc,分配一个新的内存块。

下面是 realloc 函数的一个示例:

#include <stdio.h>
#include <stdlib.h>
int main() {
    int *array;
    int old_size = 5;
    int new_size = 10;
    // 分配内存
    array = (int*)malloc(old_size * sizeof(int));
    if (array == NULL) {
        fprintf(stderr, "初始内存分配失败\n");
        exit(EXIT_FAILURE);
    }
    // 使用分配的内存
    for (int i = 0; i < old_size; i++) {
        array[i] = i;
    }
    // 调整内存大小
    array = (int*)realloc(array, new_size * sizeof(int));
    if (array == NULL) {
        fprintf(stderr, "内存调整失败\n");
        exit(EXIT_FAILURE);
    }
    // 使用新分配的内存
    for (int i = old_size; i < new_size; i++) {
        array[i] = i;
    }
    // 打印数组元素
    for (int i = 0; i < new_size; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
    // 释放内存
    free(array);
    array = NULL; // 避免悬空指针
    return 0;
}

在这个示例中,我们首先使用 malloc 分配了一个包含 5 个整数的内存块。然后,我们使用 realloc 将内存块的大小调整为 10 个整数。如果 realloc 成功,它会返回一个指向新内存块的指针,这个新内存块可能位于原内存块的位置,也可能是一个完全不同的位置。因此,重要的是更新原始指针 array 以指向新内存块的地址。如果 realloc 失败,它会返回 NULL,并且原始数据仍然有效。最后,我们使用 free 函数释放内存,并将指针设置为 NULL,以避免悬空指针的问题。

使用 realloc 时应该注意以下几点:

  • 总是检查 realloc 的返回值是否为 NULL,以处理内存分配失败的情况。
  • 如果 realloc 失败,原始数据仍然有效,因此需要适当处理这种情况。
  • 在调整内存大小时,可能会发生数据复制,因此 realloc 可能会复制旧数据到新位置,这可能会导致性能开销。
  • 使用 realloc 时,确保所有指向原始内存块的指针都被更新为新地址。
  • 使用 free 函数释放不再需要的内存,以避免内存泄漏。

5. free

free 是 C 语言标准库中的一个函数,用于释放之前通过 malloccallocrealloc 分配的动态内存。这个函数非常重要,因为它允许程序返回不再使用的内存给操作系统,从而避免内存泄漏,这是动态内存管理中的一个常见问题。
free 函数的原型定义在 <stdlib.h> 头文件中,如下所示:

void free(void *ptr);

free 函数接受一个参数:

  • ptr:指向要释放的内存块的指针。这个指针必须是之前由 malloccallocrealloc 返回的,并且尚未被 free 函数释放。

下面是 free 函数的一个示例:

#include <stdio.h>
#include <stdlib.h>
int main() {
    int *ptr = (int*)malloc(10 * sizeof(int)); // 分配内存
    if (ptr == NULL) {
        fprintf(stderr, "内存分配失败\n");
        return 1;
    }
    // 使用分配的内存
    for (int i = 0; i < 10; i++) {
        ptr[i] = i;
    }
    // 释放内存
    free(ptr);
    ptr = NULL; // 将指针设置为 NULL,避免悬空指针
    // 继续执行程序的其他部分
    // ...
    return 0;
}

在这个示例中,我们首先使用 malloc 分配了一个包含 10 个整数的内存块。然后,我们使用这个内存块,并将每个元素初始化为它的索引。最后,我们使用 free 函数释放这个内存块,并将指针 ptr 设置为 NULL,以避免它成为悬空指针。

使用 free 函数时应该注意以下几点:

  • 只能释放由 malloccallocrealloc 分配的内存。
  • 释放内存后,应该将指向该内存的指针设置为 NULL,以避免悬空指针。
  • 重复释放同一块内存会导致未定义行为,这是危险的。
  • 释放内存后,不应该再访问该内存,因为它可能已经被重新分配给其他用途。

6. 常见的动态内存错误

在 C 语言中,动态内存管理是强大的,但同时也是容易出错的。

以下是一些常见的动态内存错误:

  1. 内存泄漏:忘记释放已分配的内存。这会导致程序随着时间的推移消耗越来越多的内存,最终可能导致程序崩溃或系统资源耗尽。
    int *ptr = malloc(sizeof(int) * 10);
    if (ptr == NULL) {
        // 处理错误
    }
    // ... 使用 ptr ...
    // 忘记释放内存
    // free(ptr); // 应该调用 free
    
  2. 悬空指针:在释放内存后,仍然使用指向该内存的指针。这可能导致未定义的行为,因为释放后的内存可能已经被其他数据覆盖或重新分配。
    int *ptr = malloc(sizeof(int));
    if (ptr == NULL) {
        // 处理错误
    }
    *ptr = 42;
    free(ptr);
    // 悬空指针
    printf("%d\n", *ptr); // 未定义行为
    
  3. 野指针:使用未初始化的指针进行内存访问。这通常发生在指针声明后未赋予有效的内存地址就进行访问。
    int *ptr; // 未初始化
    *ptr = 42; // 野指针,可能导致程序崩溃
    
  4. 越界访问:访问动态分配的内存块之外的地址。这可能导致数据损坏、程序崩溃或安全漏洞。
    int *ptr = malloc(sizeof(int) * 10);
    if (ptr == NULL) {
        // 处理错误
    }
    for (int i = 0; i <= 10; i++) {
        ptr[i] = i; // 越界访问,ptr[10] 是未分配的
    }
    
  5. 使用释放后的内存:在调用 free 后,再次尝试访问或释放同一块内存。这可能导致未定义的行为或程序崩溃。
    int *ptr = malloc(sizeof(int));
    if (ptr == NULL) {
        // 处理错误
    }
    free(ptr);
    // 使用释放后的内存
    *ptr = 42; // 未定义行为
    
  6. 错误的大小传递:向 malloccallocrealloc 传递错误的大小参数,可能导致分配的内存不足或浪费。
    int *ptr = malloc(-1); // 错误的大小,可能导致未定义行为
    
  7. 分配失败处理不当:在 malloccallocrealloc 返回 NULL 时,没有正确处理内存分配失败的情况。
    int *ptr = malloc(0); // 可能分配失败
    if (ptr == NULL) {
        // 应该处理错误
    }
    
  8. 多次释放:多次调用 free 释放同一块内存。这可能导致程序崩溃或未定义行为。
    int *ptr = malloc(sizeof(int));
    if (ptr == NULL) {
        // 处理错误
    }
    free(ptr);
    free(ptr); // 多次释放,未定义行为
    

避免这些错误需要仔细的编程和对动态内存管理函数的正确使用。使用工具如静态分析器、动态分析器(如 Valgrind)和运行时检查(如 AddressSanitizer)可以帮助检测和预防这些错误。

7. 柔性数组

在 C99 标准中,结构体的一种特殊用法被称为“柔性数组”(flexible array member),它允许结构体中最后一个成员是一个未知大小的数组。这种特性在处理变长数据时非常有用,因为它允许结构体的大小适应其包含的数据。
柔性数组的定义要求结构体中的最后一个成员必须是一个数组,且该数组的长度在结构体定义时未知。柔性数组前面可以有一个或多个其他成员。由于数组的大小在编译时未知,因此结构体本身的大小只包括其他成员的大小,而不包括柔性数组的大小。

下面是一个柔性数组的示例:

#include <stdio.h>
#include <stdlib.h>
typedef struct {
    int length;
    double values[]; // 柔性数组
} FlexArray;
int main() {
    FlexArray *array = malloc(sizeof(FlexArray) + sizeof(double) * 10);
    if (array == NULL) {
        return 1;
    }
    array->length = 10;
    for (int i = 0; i < array->length; i++) {
        array->values[i] = i * 1.0;
    }
    for (int i = 0; i < array->length; i++) {
        printf("%f ", array->values[i]);
    }
    printf("\n");
    free(array);
    return 0;
}

在这个示例中,FlexArray 结构体包含一个 int 类型的成员 length 和一个柔性数组 values。我们使用 malloc 分配足够的内存来存储结构体和一个包含 10 个 double 类型元素的数组。然后,我们可以像使用普通数组一样使用 array->values

使用柔性数组时需要注意以下几点:

  • 柔性数组必须作为结构体的最后一个成员。
  • 柔性数组的大小在结构体定义时必须是未知的,通常在运行时根据需要动态分配。
  • 柔性数组前面可以有其他成员,这些成员的大小在编译时是已知的。
  • 柔性数组的存在使得结构体的大小可以动态调整,以适应不同大小的数据。
    柔性数组提供了一种方便的方式来处理结构体中的变长数据,而不需要为可能的最大大小分配内存。这在内存受限的应用程序中特别有用,因为它可以减少不必要的内存分配。

8. 程序内存区域划分

在程序的执行过程中,内存被划分为多个不同的区域,每个区域有不同的用途和属性。这些区域可以分为以下几类:

  1. 栈(Stack)
    • 每个函数调用都会创建一个新的栈帧,用于存储函数的参数、局部变量和返回地址。
    • 栈是线性的、连续的内存区域,具有固定的大小。
    • 栈的大小通常是固定的,由编译器或操作系统决定。
    • 栈操作是自动的,不需要程序员显式管理。
  2. 堆(Heap)
    • 堆用于动态内存分配,程序员可以请求任意大小的内存块。
    • 堆的大小通常不固定,可以根据需要动态扩展。
    • 程序员需要手动管理堆上的内存,使用 malloccallocreallocfree 函数。
    • 堆操作是非线性的,可能涉及更多的内存碎片。
  3. 数据段(Data Segment)
    • 包含程序的全局变量和静态变量。
    • 数据段在程序启动时分配,并持续存在,直到程序结束。
    • 数据段的大小在编译时确定,但在运行时不可变。
  4. 代码段(Text Segment)
    • 包含程序的代码(指令和常量)。
    • 代码段在程序启动时加载到内存,并一直存在直到程序结束。
    • 代码段的大小在编译时确定,但在运行时不可变。
  5. BSS 段(Block Started by Symbol)
    • 包含未初始化的全局变量和静态变量。
    • BSS 段在程序启动时被初始化为 0。
    • BSS 段的大小在编译时确定,但在运行时不可变。
  6. 未使用段(Unused Segment)
    • 包含程序未使用的内存区域。
    • 这些区域可能因为编译器或操作系统的原因而存在,但通常不包含有效的数据。
      每个操作系统和编译器可能会有不同的内存区域划分和实现细节,但上述分类提供了一个通用的框架。在 C 语言中,动态内存管理主要发生在堆上,而静态内存管理则发生在栈、数据段、代码段和 BSS 段上。

结语

以上就是小编对动态内存管理的详细讲解。
如果觉得小编讲的还可以,还请一键三连。互三必回!
持续更新中~!

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1687377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能再现大脑细胞导航的活动模式

人工智能再现大脑细胞导航的活动模式 李升伟 编译 深度学习算法可自发模拟特殊神经元的活动&#xff0c;这种神经元活动可以告诉我们在空间的位置。 大鼠使用被称为网格细胞的大脑细胞帮助它们导航&#xff0c;人工智能程序已经可以再现这种能力。 科学家已经使用人工智能来再…

网络初识 一

一、网络发展史 1.1 独立模式 最开始的网络,计算机之间是相互独立的. 比如,三个计算机分别存着各自的数据,A正在获取第一台计算机的数据,等要获取第二台计算机的数据时要移动到第二台计算机那里,B想要获取第一台计算机的数据,就要等A使用完. 1.2 网络互连 随着时代的发展,…

Codigger编码场景介绍(二):驾驶舱场景(Cockpit)

Codigger&#xff0c;一个专为开发人员设计的工具&#xff0c;致力于为不同的开发场景提供最佳的切换体验。Codigger囊括了多种场景&#xff0c;如传统场景、调试场景、设计器场景、驾驶舱场景以及纯净场景等。在上一篇文章中&#xff0c;我们介绍了传统场景模式&#xff0c;今…

Linux 应用入门(一)

1. 交叉编译 概念&#xff1a;在当前编译平台下&#xff0c;编译出来的程序能运行在体系结构不同的另一种目标平台上&#xff0c;但是编译平台本身却不能运行该程序。 为什么需要交叉编译&#xff1f; 速度&#xff1a;目标平台得运行速度比主机往往慢得多&#xff0c;因为许多…

Linux程序开发(十一):进程与进程间通信设计之趣味猫咪抓老鼠游戏

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

在未来你将何去何从?

在数字化的浪潮中&#xff0c;信息技术行业无疑是推动全球经济和社会发展的重要动力。随着科技的不断迭代与进步&#xff0c;云计算、大数据、人工智能&#xff08;AI&#xff09;、物联网&#xff08;IoT&#xff09;、5G通信和区块链等技术已经深入到我们生活的每一个角落&am…

Python调用科大讯飞在线语音合成API --内附完整项目

一&#xff0c;注册讯飞账号&#xff0c;并实名制。 讯飞开放平台-以语音交互为核心的人工智能开放平台 (xfyun.cn) 二、找到音频合成&#xff0c;按页面提示申请免费试用。 在线语音合成_免费试用-讯飞开放平台 (xfyun.cn) 三、申请免费使用后&#xff0c;找到API信息如下…

数据仓库ETL

小白的数据仓库学习笔记 2024/5/20 18:25 文章目录 ETLdim打开创建项目&#xff08;选这个&#xff0c;这个是做etl的&#xff09;建立元数据的连接同样的&#xff0c;建立与数据仓库的连接新建ssis包序列容器全量etl增量etl建立sql任务双击打开&#xff0c;设置连接、内容 双击…

共享单车(八):数据库

实现后台数据库访问模块的框架&#xff0c;能够实现验证请求并响应&#xff08;支持数据库操作&#xff09;。 数据库设计 class SqlTabel //负责数据库表的创建 { public:SqlTabel(std::shared_ptr<MysqlConnection> sqlconn) :sqlconn_(sqlconn) {}bool CreateUserI…

BeautifulSoup4通过lxml使用Xpath,以及获取(定位)元素和其文本或者属性

环境&#xff1a;win10&#xff0c;python3.8.10 首先需要安装&#xff1a;beautifulsoup4&#xff0c;lxml 使用命令&#xff1a; pip38 install beautifulsoup4 pip38 install lxml 安装完毕后查看一下&#xff1a; 写代码&#xff1a; from bs4 import BeautifulSoup …

5.24机器人基础-入门1

参考书籍&#xff1a;《机器人学导论》John.J.Craig 机器人方面的学习和线性代数、矩阵论部分的学习密切相关&#xff0c;建议先学习线性代数会更好理解此部分内容。虽然学了线代但是能忘的都忘得差不多的我要泪目了。本文主要介绍基础的运动学和动力学区别&#xff0c;正向运…

RocketMQ 架构原理

注册中心 注册中心使用得nameserver, nameserver不会进行频繁的读写&#xff0c;所以整体的性能开销小&#xff0c;稳定性也高。 注册中心没隔10s会扫描一次所有的broker&#xff0c;如果2min没有发送心跳过来&#xff0c;就人为挂了&#xff0c;断开连接。此时会更新topic与队…

IEN在Web3.0中的性能与安全优势

随着Web3.0的快速发展&#xff0c;优化网络基础设施变得至关重要。智能生态网络&#xff08;Intelligent Ecological Network, IEN&#xff09;作为新一代网络架构&#xff0c;在提升性能与增强安全方面展现出巨大潜力。本文将深入探讨IEN在Web3.0中的技术优势&#xff0c;并展…

Linux安装Docker教程(实测可用)

前言 Docker是一个开源的应用容器引擎&#xff0c;它允许开发者将应用程序及其依赖打包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的Linux机器上。以下是对Docker的具体介绍&#xff1a; 技术起源&#xff1a;容器技术起源于程序员对于环境搭建与应用部署效…

data studio连接到虚拟机上的openGauss

参考&#xff1a;使用DataStudio连接本地虚拟机中的opengauss数据库_big data_白日梦想家_胖七七-华为云开发者联盟 本实验虚拟机安装的是CentOS7 数据库版本是&#xff1a;openGauss-5.0.2-CentOS-64bit-all.tar.gz 1.配置pg_hba.conf 首先使用su - omm登录到omm用户&…

FPGA搭积木之按键消抖(改进版)

目录 1.前言 2.回顾之前的设计 3.基于读者思路的设计 4.ModelSim仿真 1.前言 昨天分享的关于FPGA对机械按键消抖的设计&#xff0c;有读者指出了其中的不足&#xff0c;并给出了他的思路。今天就读者的设计思路&#xff0c;来再做一个按键消抖模块。这个程序大概是大学的时…

arping 一键检测网络设备连通性(KALI工具系列二)

目录 1、KALI LINUX简介 2、arping工具简介 3、在KALI中使用arping 3.1 目标主机IP&#xff08;win&#xff09; 3.2 KALI的IP 4、操作示例 4.1 IP测试 4.2 ARP测试 4.3 根据存活情况返回 5、总结 1、KALI LINUX简介 Kali Linux 是一个功能强大、多才多艺的 Linux 发…

建投数据收获客户感谢信

建投数据自2021年提出“以数据为核心的智能科技服务商”&#xff0c;并一直在为“成为国内领先的数字化转型合作伙伴”而努力&#xff0c;在赋能行业客户创造更大价值的同时&#xff0c;也陆续收到来自客户的肯定。 建投数据始终践行“成就客户&#xff0c;创新为要&#xff0…

科技引领乡村振兴新潮流:运用现代信息技术手段,提升农业生产和乡村管理效率,打造智慧化、现代化的美丽乡村

一、引言 随着科技的不断进步&#xff0c;现代信息技术已经渗透到社会的各个领域&#xff0c;成为推动社会发展的重要力量。在乡村振兴战略的背景下&#xff0c;科技的力量同样不容忽视。本文旨在探讨如何运用现代信息技术手段&#xff0c;提升农业生产和乡村管理效率&#xf…