【C语言】结构体——我就是秩序的创建者!(结构体数组、结构体指针、嵌套、匿名、字面量、伸缩型数组、链式结构)

news2024/11/28 21:46:57

  • 一、结构体基础
    • 1.1 声明和定义
    • 1.1 初始化和赋值
    • 1.3 访问结构体成员
  • 二、结构体数组
    • 2.1 定义和初始化
    • 2.2 访问
  • 三、结构体的嵌套
  • 五、指向结构体的指针
  • 六、向函数传递结构体
    • 6.1 只传递结构体成员
    • 6.2 传递结构体指针
    • 6.3 传递结构体
  • 七、结构体的其他特性——不容小觑
    • 7.1 结构体的大小问题
    • 7.2 成员变量是字符数据和字符指针
      • 7.21 注意事项
      • 7.22 指针型成员变量的正确使用方式
    • 7.3 结构体的复合字面量——临时结构体
    • 7.4 匿名结构体
    • 7.5 伸缩型数组成员
    • 7.6 应用——结构体与文件、数据库
  • 八、结构体的应用——链式结构

一、结构体基础

1.1 声明和定义

结构体(也有称:结构)是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member),使用结构体,有助于提高数据的表示能力。

如:

struct name{
    char first[10];
    char last[10];
    int age;
};

声明了一个结构体name,现在并没有创建实际的数据对象,编译器没有分配存储空间,只是声明了该对象由什么组成,即模板

上述结构中,有3个结构体成员,结构体成员可以使任何数据类型:基本数据类型,结构体,结构体指针等。

可以用struct name来定义来定义变量,变量的结构和模板一样,相当于使用int来定义一个int型变量。

struct name Jay;

这样就定义了一个结构体变量。编译器为他分配24字节的空间。

当然也可以声明多个结构体变量,以及指向struct name类型结构的指针:

struct name Jay,David,*Jolin;

通常我们使用typedef对结构体类型重命名:

typedef struct {
	char first[10];
	char last[10];
	int age;
}Name;

现在:Name就相当于前面的struct name,这样是的代码更简洁,可读性更强。

1.1 初始化和赋值

(1)初始化

定义结构体变量的同时可以进行初始化,使用花括号括起来的初始化列表进初始化,各项之间逗号隔开,各项按照其初始化规则进行初始化,比如成员变量是一个数组,该变量应该按照数组初始化方式初始化。

Name Jay = {"Jay","Chou",18};

结构体变量也支持初始化器,即指定某几项进行初始化:

Name Jay = {.age = 18};   //只初始化age成员变量

(2)赋值

只有初始化的时候可以使用花括号进行整体复制,后面需要单独对成员进行复制。或者使用结构体字面量(后面将)。

结构体变量之间支持直接赋值,这和数组不同。

    //struct name Jay={"Jay","Chou",18};
    // Name Jay = {"Jay","Chou",18};
    //Name Jay = {.age=18};
    Name Jay;
    //Jay = {"Jay","Chou",18};  //这是错误写法
    //Jay = (Name){"Jay","Chou",18};   //正确
    Name David = {"David","Tao",18};
    Jay =David;
    Jay.age = 888;

1.3 访问结构体成员

使用结构体成员运算符:. 来访问结构中的成员,访问成员变量的元素则由成员类型决定,如数组使用下标访问各个元素。

如访问结构体变量的成员age:

Jay.age

本小节测试代码:

#include <stdio.h>

typedef struct{
    char first[10];
    char last[10];
    int age;
}Name;

int main(){
    //struct name Jay={"Jay","Chou",18};
    // Name Jay = {"Jay","Chou",18};
    //Name Jay = {.age=18};
    Name Jay;
    //Jay = {"Jay","Chou",18};  //这是错误写法
    //Jay = (Name){"Jay","Chou",18};
    Name David = {"David","Tao",18};
    Jay =David;
    Jay.age = 888;

    printf("%s %s,%d\n",Jay.last,Jay.first,Jay.age);
    return 0;
}

输出:
在这里插入图片描述


本小节的内容仅适用于结构体变量成员中不含指针的情况。

二、结构体数组

结构体数组:数组中每个元素都是结构体。当素组每个元素都有多个属性时,就可以使用结构体数组,比如用它来记录一个班每个学生的信息。

2.1 定义和初始化

定义并初始化一个结构体数组,包含2个元素,每个元素都是结构体:

    Name singer[2]={
        {"Jay","Chou",18},
        {"David","Tao",888},
    };

2.2 访问

访问数组元素使用下标,访问结构体变量使用.

比如第一个数组元素的age成员

singer[0].age

本小节代码:

#include <stdio.h>

typedef struct{
    char first[10];
    char last[10];
    int age;
}Name;

int main(){
    Name singer[2]={
        {"Jay","Chou",18},
        {"David","Tao",888},
    };

    for(int i=0;i<2;i++)printf("%s %s,%d\n",singer[i].first,singer[i].last,singer[i].age);

    return 0;
}

输出:
在这里插入图片描述

三、结构体的嵌套

即结构体的成员变量也可以是结构体。

例:

#include <stdio.h>

typedef struct{
    char early_aalbum [2][20];
}Album;

typedef struct{
    char first[10];
    char last[10];
    int age;
    Album album;
}Name;

int main(){
    Name singer[2]={
        {"Jay","Chou",18,{"《Jay》","《Fantasy》"}},
        {"David","Tao",888,{"《David Tao》","《I'm OK》"}},
    };

    for(int i=0;i<2;i++)printf("%s %s,  %d,  Album:  %s, %s\n", singer[i].first,singer[i].last,singer[i].age,
                               singer[i].album.early_aalbum[0],singer[i].album.early_aalbum[1]);

    return 0;
}

输出:
在这里插入图片描述

  • 结构体的大小等于各成员大小之和;
  • 上面的Name结构体大小时64字节,则singer这个结构体数组大小时128字节;
  • 结构体大小也有可能大于成员大小之和,因为有的系统将成员放在偶数地址上。

五、指向结构体的指针

(1)声明:

Album *p;

指针p现在可以指向任意一个Album类型的结构体。如:

p = &singer[0].album

p指向了数组singer的第一个元素的成员album,该成员是个Album类型的结构体。

注意:结构体变量名不是结构体的地址。

(2)用指针访问成员

使用箭头:->

下面3种写法等价:

    printf("His first albul is: %s\n",p->early_aalbum[0]);
    printf("His first albul is: %s\n",singer[0].album.early_aalbum[0]);
    printf("His first albul is: %s\n",(*p).early_aalbum[0]);

六、向函数传递结构体

结构体:

typedef struct{
    int num1;
    int num2;
}Num;

...

Num num = {888,999};
Num *p =&num;

6.1 只传递结构体成员

// 只传递结构体成员
int func_1(int a,int b){
    return a>b? a:b;
}

...

func(num.num1,num.num2);

6.2 传递结构体指针

// 传递结构体指针
int func_2(Num *tmp){
    return tmp->num1 > tmp->num2? tmp->num1 : tmp->num2;
}

...
func(p);

6.3 传递结构体

// 传递结构体 
int func_3(Num tmp){
    return tmp.num1 > tmp.num2? tmp.num1 : tmp.num2;
}

...
func(num);

完整实例:

#include <stdio.h>

typedef struct{
    int num1;
    int num2;
}Num;

// 只传递结构体成员
int func_1(int a,int b){
    return a>b? a:b;
}

// 传递结构体指针
int func_2(const Num *tmp){
    return tmp->num1 > tmp->num2? tmp->num1 : tmp->num2;
}

// 传递结构体 
int func_3(const Num tmp){
    return tmp.num1 > tmp.num2? tmp.num1 : tmp.num2;
}

int main(){
    Num num = {888,999};
    Num *p = &num;
    printf("%d\n",func_1(num.num1,num.num2));
    printf("%d\n",func_2(p));
    printf("%d\n",func_3(num));
    return 0;
}

输出:

root@CQUPTLEI:~/Linux_test/LinuxC_learn/c_struct# gcc -Wall -o test transport.c
root@CQUPTLEI:~/Linux_test/LinuxC_learn/c_struct# ./test
999
999
999

注:

  1. 使用const防止结构体被篡改;
  2. 传递结构体的优缺点:
    • 简单直接;
    • 内存开销大,效率低:这种方式会在栈上创建一个结构体的副本,以保证原始结构体不变。
  3. 使用结构体指针的优缺点:
    • 节省内存和时间;
    • 可修改原始结构体(如果你想避免,可以使用const);
    • 可能会增加代码复杂度;
    • 要注意空指针的问题。

七、结构体的其他特性——不容小觑

7.1 结构体的大小问题

结构体的大小是由其成员变量的类型对齐方式决定的。

  1. 成员变量的大小:结构体的大小受到其成员变量大小的影响。基本数据类型的大小在大多数编译器中是固定的,例如int通常是4个字节。对于数组或指针等类型,大小取决于其元素类型的大小。结构体的成员变量按照其定义的顺序依次存储在内存中。

  2. 对齐方式:为了提高内存访问的效率,结构体的成员变量通常会按照一定的对齐方式进行排列。对齐方式可以通过编译器的默认规则或指令进行控制。常见的对齐规则是按照成员变量的大小将其放置在内存地址能被整除的位置上。例如,如果一个结构体成员变量的大小为4字节,那么它通常会被放置在内存地址是4的倍数的位置上。

  3. 填充字节:为了满足对齐要求,有时候编译器会在结构体的成员变量之间插入额外的填充字节,以保证对齐。填充字节不属于结构体成员变量,但会影响结构体的大小。填充字节的大小取决于编译器和对齐方式。

  4. 结构体大小计算:结构体的大小可以使用sizeof运算符来计算。sizeof返回结构体所占用的字节数,包括成员变量和填充字节。注意,sizeof的计算结果可能会受到编译器和编译选项的影响,因此在跨平台或多编译器环境中,可能需要注意结构体大小的一致性。

需要注意的是,结构体的大小可能因编译器、对齐规则和平台而异。为了确保结构体的大小和布局符合预期,可以使用编译器提供的对齐指令或预处理指令进行控制。例如,#pragma pack指令可以在某些编译器中用来指定结构体的对齐方式。

例:

typedef struct{
    int num1;
    int num2;
}Num;
...
Num num = {888,999};
...
printf("%zd\n",sizeof(num));

在Ubuntu上使用gcc编译,结果是8字节。

7.2 成员变量是字符数据和字符指针

7.21 注意事项

当结构体的成员是字符数组或字符指针的时候,要注意:

  1. 字符数据(字符数组):

    • 确保字符数组有足够的空间来存储所需的字符串。如果字符串长度超过数组大小,可能会导致缓冲区溢出和未定义行为。
    • 使用字符串处理函数时,确保输入的字符串以 null 终止(即以\0结尾),以避免在处理字符串时出现错误。
    • 考虑字符数组的长度和预留空间,以防止内存浪费或不足。
  2. 字符指针:

    • 确保字符指针指向有效的内存区域,且不为 null。使用字符指针之前,应该先进行空指针检查,以避免出现悬空指针错误。
    • 注意字符指针的生命周期和内存管理。确保字符指针指向的内存区域在使用期间有效,并在不再需要时正确释放内存,以避免内存泄漏。
  3. 字符串操作和安全性:

    • 当对字符数据或字符指针进行字符串操作时,务必确保输入的字符串是正确的、合法的,并进行适当的输入验证和边界检查,以避免缓冲区溢出和安全漏洞。
    • 使用安全的字符串处理函数,如strncpy()strncat()等,以指定最大操作长度,防止缓冲区溢出。
  4. 字符编码和字符集:

    • 理解所使用的字符编码和字符集。不同的编码和字符集可能影响字符数据的表示方式和处理方法。确保正确地处理和处理不同字符编码和字符集的情况。
  5. 内存对齐和填充字节:

    • 注意结构体中字符数据和字符指针的对齐方式和填充字节,特别是在涉及跨平台或与外部环境交互的情况下。可以使用编译器指令或预处理指令进行控制。

例:

  1. 成员变量是一个指针,在赋值的时候让它指向一个字符串。这时,计算结构的大小时应该使用指针变量本身的大小,而不是它指向的字符串的大小,因为字符串并不存储在结构体内部,结构体成员只存储了这个指针变量。(在64位系统中,一个指针变量的大小是8字节)
  2. 成员变量是指针,没有初始化或其他处理就使用,这会有潜在风险,或许你的程序看起来没什么问题。

下面的代码定义了一个Test类型的结构体变量var,它有一个指针类型成员,初始化的时候将它指向一个字符串常量。

#include <stdio.h>

typedef struct{
    int a;
    int b;
    char arr[10];
    char *p;
}Test;

int main(){
    char str[20]="Hello,C language.";
    Test var = {11,22,{[5]='B'},str};
    puts("The content of var:");
    printf("a: %d\nb: %d\narr: ",var.a,var.b);
    for(int i=0;i<10;i++)printf("%c ",var.arr[i]);
    printf("\n*p: %s\n",var.p);

    printf("Size of var is: %zd\n",sizeof(var));
    return 0;
}

程序的输出:
在这里插入图片描述

可见,结构体变量的大小时32字节,分析:

  1. 大小按理应该是:4+4+10+8=26字节;
  2. 至少不是:4+4+10+18=36字节(那个字符串大小时18字节);
  3. 这说明大小还与对齐方式有关(7.1节中讲到)。

验证:

这里打印4个成员的地址:

printf("成员变量的地址:\na:   %p\nb:   %p\narr:  %p\narr+10:   %p\np:   %p\n",&var.a,&var.b,var.arr,var.arr+10,&var.p);

输出:
在这里插入图片描述

int a地址加上4个字节是int b的地址,int b加4个字节是char arr[10]的地址;
char arr[10]再加10个字节,应该是:0x7fff27ce8b12

这就印证了7.1节中说的对齐问题,p这个指针变量大小时8字节,那么他会放在8字节对齐的地址上,即 0x7fff27ce8b18

7.22 指针型成员变量的正确使用方式

其实就是使用指针的注意问题。

当结构体成员是指针类型时, 不是必须要进行初始化。然而,根据具体的使用场景和需求,初始化指针成员可能是一个良好的编程实践。

如果指针成员在结构体中没有被初始化,它的初始值将是未定义的,也就是说它可能指向任意的内存地址。这可能导致在访问或解引用指针成员时出现未定义行为、段错误或崩溃。

因此,为了避免这些问题,建议在创建结构体实例时初始化指针成员,为其分配合适的内存空间,或者将其指向有效的内存位置。这可以通过以下方式之一实现:

  1. 直接赋值一个有效的指针:

    struct MyStruct {
        int* ptr;
    };
    
    // 初始化指针成员
    struct MyStruct myStruct;
    int value = 42;
    myStruct.ptr = &value;
    
  2. 使用动态内存分配函数(如malloc)为指针成员分配内存(记得要free):

    struct MyStruct {
        char* str;
    };
    
    // 初始化指针成员
    struct MyStruct myStruct;
    myStruct.str = malloc(sizeof(char) * 10);
    if (myStruct.str != NULL) {
        // 分配成功,可以使用指针成员
        strcpy(myStruct.str, "Hello");
    }
    
  3. 将指针成员设置为NULL,表示它当前不指向任何有效的内存地址:

    struct MyStruct {
        float* data;
    };
    
    // 初始化指针成员
    struct MyStruct myStruct;
    myStruct.data = NULL;
    

在使用指针成员之前,确保已经进行了适当的初始化或赋值操作,以避免潜在的问题。对于动态分配的内存,还要记得在不需要时及时释放内存,以避免内存泄漏。

7.3 结构体的复合字面量——临时结构体

我前面的代码已经出现过:

Name Jay;
//Jay = {"Jay","Chou",18};  //这是错误写法
Jay = (Name){"Jay","Chou",18};   //正确

符复合字面量是一种语法构造,用于创建临时的、匿名的复合类型的对象,前面文章讲过数组的复合字面量:

intsum =int []){1,1,4,6};

这里结构体的复合字面量是一个道理。

7.4 匿名结构体

前面有一段代码:

typedef struct{
    char early_aalbum [2][20];
}Album;

typedef struct{
    char first[10];
    char last[10];
    int age;
    Album album;
}Name;

它可以简写为:

typedef struct{
    char first[10];
    char last[10];
    int age;
    struct {char early_aalbum [2][20];};
}Name;

这个嵌套的匿名结构体的成员可以直接作为外层结构以成员使用:Name.early_aalbum[0]

7.5 伸缩型数组成员

伸缩型数组成员(Flexible Array Member)是C语言中的一种特殊结构体成员,允许在结构体的末尾定义一个数组成员,该数组的大小可以根据需要动态调整。伸缩型数组成员在C语言中的使用主要涉及动态内存分配和管理。

详细介绍:

  1. 定义伸缩型数组成员:

    struct MyStruct {
        int length;
        int data[];
    };
    

    在上面的示例中,data成员是一个伸缩型数组,它没有指定数组的大小,只定义了数组的类型。伸缩型数组成员必须是结构体的最后一个成员

  2. 动态分配伸缩型数组成员:
    伸缩型数组成员不能直接使用常规的结构体定义方式来分配内存,因为数组的大小是变化的。要使用伸缩型数组成员,需要结合动态内存分配函数(如malloc)来为结构体分配内存,同时考虑数组的大小。

    struct MyStruct* createMyStruct(int length) {
        struct MyStruct* myStruct = malloc(sizeof(struct MyStruct) + length * sizeof(int));
        if (myStruct != NULL) {
            myStruct->length = length;
        }
        return myStruct;
    }
    

    在上面的示例中,使用malloc动态分配了足够的内存空间来容纳结构体本身的大小以及数组成员的大小。

  3. 使用伸缩型数组成员:
    伸缩型数组成员可以像普通数组一样使用。可以通过索引来访问数组元素,但要注意确保不超出数组的有效范围。

    void processMyStruct(struct MyStruct* myStruct) {
        for (int i = 0; i < myStruct->length; i++) {
            printf("%d ", myStruct->data[i]);
        }
    }
    

    在上面的示例中,通过遍历数组成员,打印出数组中的元素。

  4. 释放伸缩型数组成员的内存:
    当不再需要使用结构体及其伸缩型数组成员时,必须记得释放分配的内存以避免内存泄漏

    void destroyMyStruct(struct MyStruct* myStruct) {
        free(myStruct);
    }
    

    在上面的示例中,使用free函数释放了通过malloc动态分配的内存。

伸缩型数组成员的使用需要小心处理,特别是在内存分配、访问越界等方面。确保正确分配足够的内存,并遵守对数组成员的访问限制,以避免导致未定义行为和内存错误。

7.6 应用——结构体与文件、数据库

结构体在文件和数据库方面的应用广泛:

在文件方面的应用:

  1. 文件读写:结构体可以方便地用于读取和写入文件。通过将结构体的数据以二进制形式写入文件,可以保留结构体的完整信息,并可以随后从文件中读取数据并重新构建结构体。

  2. 文件格式:结构体可以用于定义文件的格式。例如,在图像处理中,可以使用结构体定义图像文件的头部信息、像素数据以及其他元数据。这样可以方便地将整个结构体写入文件或从文件中读取,并以结构化的方式处理图像数据。

  3. 序列化:结构体可以用于对象的序列化和反序列化。通过将结构体的数据转换为特定格式(如JSON、XML等)的字符串,并将其写入文件,可以将对象的状态保存到文件中。然后,可以从文件中读取该字符串,并将其转换回结构体,以还原对象的状态。

在数据库方面的应用:

  1. 数据库表映射:结构体可以用于将数据库表映射到编程语言中的对象。每个结构体字段可以对应数据库表中的列,从而实现对象和数据库之间的数据交互。通过将结构体实例化并将其数据插入数据库表中,或从数据库中查询数据并填充结构体字段,可以在应用程序和数据库之间方便地传输和处理数据。

  2. 数据库查询结果:在执行数据库查询操作后,查询结果通常以结构体的形式返回。每个查询结果的行可以映射到一个结构体实例,结构体的字段表示结果集中的列。这样,可以通过访问结构体的字段来获取和操作查询结果的数据。

  3. 数据库事务:结构体可以用于表示数据库事务中的数据操作。通过将相关数据存储在结构体中,并使用数据库事务管理器执行一系列数据库操作,可以确保数据操作的一致性和原子性。

例:

#include <stdio.h>
#include <mysql.h>

struct Employee {
    int id;
    char name[50];
    double salary;
};

int main() {
    // 假设已连接到MySQL数据库并执行查询操作,结果存储在MYSQL_RES对象中
    MYSQL_RES *result;

    // 定义结构体数组来存储查询结果
    struct Employee employees[100];
    int numRows = 0;

    // 从查询结果中获取数据并填充结构体数组
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(result)) != NULL) {
        struct Employee employee;
        employee.id = atoi(row[0]);
        strcpy(employee.name, row[1]);
        employee.salary = atof(row[2]);

        employees[numRows++] = employee;
    }

    // 打印查询结果
    for (int i = 0; i < numRows; i++) {
        printf("ID: %d, Name: %s, Salary: %.2f\n", employees[i].id, employees[i].name, employees[i].salary);
    }

    // 释放查询结果和关闭数据库连接等操作...

    return 0;
}

对于文件和数据库的操作,应注意数据的一致性、完整性和安全性。正确地处理文件和数据库操作可以提高应用程序的性能和可靠性。此外,在使用结构体进行文件和数据库操作时,还应注意处理错误、异常情况和数据格式兼容性等问题。

八、结构体的应用——链式结构

结构体的重要用途就是创建新的数据形式,如:队列、二叉树、堆、哈希表、图等。

后面开单独章节讲解。



~

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

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

相关文章

硬盘数据丢失怎么办?一招轻松恢复硬盘数据!

硬盘应该是最为常用的数据存储设备了&#xff0c;它为电脑等设备提供巨大的存储空间。我们在平时的工作和学习中也经常会使用硬盘来存储数据&#xff0c;很多用户会将多年搜集到的资料存到电脑硬盘里。 硬盘上的文件&#xff0c;不论是工作文档还是照片、视频&#xff0c;对用…

(四)ArcGIS空间数据的转换与处理——数据结构转换

ArcGIS空间数据的转换与处理——数据转换 空间数据的来源很多&#xff0c;如地图、工程图、规划图、航空与遥感影像等&#xff0c;因此空间数据也有多种格式。根据应用需要&#xff0c;需对数据进行格式转换&#xff0c;不同数据结构间的转换主要包括矢量数据到栅格数据的转换…

Guava的骚操作,大大提升了我摸鱼的时间

以面向对象思想处理字符串对基本类型进行支持总结 概述 1、Guava是google公司开发的一款Java类库扩展工具包&#xff0c;包括新的集合类型&#xff08;如 multimap 和 multiset&#xff09;、不可变集合、图形库&#xff0c;以及用于缓存、并发、I/O等实用程序。使用这些API一…

内存溢出导致的Full GC异常

背景 线上服务GC耗时过长&#xff0c;普遍10s&#xff0c;此外GC后&#xff0c;内存回收不多 问题一 通过查询gc日志可以发现&#xff0c;CMS进行垃圾回收的时候报concurrent mode failure错误&#xff0c;该错误是因为CMS进行垃圾回收的时候&#xff0c;新生代进行GC产生的对象…

JupyterLab(Jupyter Notebook)安装与使用

文章目录 前言安装JupyterLab切换中文语言JupyterLab desktop 使用演示其它补充后记 前言 目前在看《程序员数学&#xff1a;用Python学透线性代数和微积分》这个书&#xff0c;它里面的代码是在Jupyter中编写的&#xff0c;所以也安装下用用。 JupyterLab是一个可以同时编写…

[PyTorch][chapter 37][经典卷积神经网络-2 ]

1&#xff1a; VGG 2: GoogleNet 一 VGG 1.1 简介 VGGNet 是牛津大学计算机视觉组&#xff08;Visual Geometry Group&#xff09;和谷歌 DeepMind 一起研究出来的深度卷积神经网络&#xff0c;因而冠名为 VGG。VGG是一种被广泛使用的卷积神经网络结构&#xff0c;其在在20…

生成式模型的质量评估标准

Sample Quality Matrix 如何评价生成式模型的效果&#xff1f;ISFIDsFIDPrecision & RecallPrecisonRecall计算precision和recall 如何评价生成式模型的效果&#xff1f; Quality: 真实性&#xff08;逼真&#xff0c;狗咬有四条腿&#xff09; Diversity: 多样性&#x…

全网为数不多清晰可行的在VUE中使用sortable.js实现动态拖拽排序的教程!

目录 0 写在前面的 1 依赖安装 2 手写简单标签演示 3 要点 4 效果 0 写在前面的 首先批评以下文章 (10条消息) sortable.js 实现拖拽_sortablejs_花铛的博客-CSDN博客 (10条消息) sortablejs拖拽排序功能&#xff08;vue&#xff09;_C_fashionCat的博客-CSDN博客 他们…

Spring6入门 + Log4j2

1、环境要求 JDK&#xff1a;Java17&#xff08;Spring6要求JDK最低版本是Java17&#xff09; Maven&#xff1a;3.6 Spring&#xff1a;6.0.2 2、构建模块 &#xff08;1&#xff09;构建父模块spring6 在idea中&#xff0c;依次单击 File -> New -> Project ->…

什么是内部网络分段渗透测试?

网络攻击的规模、范围和复杂性与日俱增。随着黑客及其攻击方法变得越来越复杂&#xff0c;您的企业必须做出相应的响应&#xff0c;否则您的安全边界就会不堪重负。 如今&#xff0c;内部网络分段是将攻击成功风险降至最低、改善数据流和隔离关键支付数据的主要方法之一。 但是…

【U-Boot 之七】fastboot原理分析及uboot fastboot功能实践

本文首先介绍了fastboot的基本原理&#xff0c;然后分析了uboot中fastboot的实现&#xff0c;最后&#xff0c;从实践的角度测试了fastboot协议及各种fastboot命令的使用方式等 。本文的仅按照我本人的实际测试过程进行了简单的描述。若有不当之处&#xff0c;欢迎各位大神不吝…

专家警告AI可能会导致人类灭绝?

人工智能可能导致人类灭绝&#xff0c;包括 OpenAI 和 Google Deepmind 负责人在内的专家警告说 数十人支持在人工智能安全中心 的网页上发表的声明。 它写道&#xff1a;“减轻人工智能灭绝的风险应该与其他社会规模的风险&#xff08;如流行病和核战争&#xff09;一起成为全…

Linux系统安装RabbitMQ

rabbitmq安装 说明&#xff1a;本次使用centos7.9 安装虚拟机. 1. 安装依赖环境 在线安装依赖环境&#xff1a; yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c kernel-devel m4 ncurses-devel tk tc xz2. 安装Erlang 根据课…

[原创]集权设施保护之LDAP协议

LDAP是一种目录访问协议&#xff0c;它规定了以树状结构的方式来存储和访问数据。然而协议是抽象的&#xff0c;要产生具体的功效&#xff0c;必须在应用中实现&#xff0c;比如AD域服务就实现了LDAP协议。 LDAP最明显的优势就是读取速度快&#xff0c;拥有极高的搜索效率。 可…

Drools规则引擎

Drools规则引擎 Drools规则引擎1、Drools简介2、Drools入门案例2.1、业务场景2.2、maven坐标2.3、编写xml配置文件&#xff08;多方法&#xff09;2.4、创建drl规则文件2.5、单元测试 3、Drools基础语法3.1、规则文件的构成3.2、规则体语法结构3.2.1、条件部分3.2.1.1、约束连接…

day4,day5 -java集合框架

List、Set、Map等常用集合类的特点和用法。 常用集合类&#xff08;List、Set、Map 等&#xff09;是 Java 中提供的数据结构&#xff0c;用于存储和操作一组数据。以下是它们的特点和用法&#xff1a; List&#xff08;列表&#xff09;: 特点&#xff1a;有序集合&#xff0…

多元办公场景下,企业如何保障工作效率与数据安全流通?

为适应数字化转型需求&#xff0c;提升办公效率&#xff0c;很多企业对工作模式进行革新&#xff0c;并将更多协同办公工具引入工作流程。然而&#xff0c;这也扩大了企业内网对外的安全暴露面&#xff0c;企业亟需进一步加强底层基础设施的网络安全建设&#xff0c;严防勒索病…

分布式监控平台——Zabbix6.0

市场上常用的监控软件&#xff1a; 传统运维&#xff1a;zabbix、 Nagios云原生环境&#xff1a; Prometheus &#xff08;go语言开发的&#xff09; 一、zabbix概述 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数…

内网渗透(八十六)之Exchange ProxyLogon攻击链利用

Exchange ProxyLogon攻击链利用 漏洞背景 2021年3月2日,微软发布了Exchange服务器的紧急安全更新,修复了如下7个相关的漏洞。 Exchange服务端请求伪造漏洞(CVE-2021-26855):未经身份验证的攻击者能够构造HTTP请求扫描内网并通过Exchange服务器进行身份验证。Exchange反序列…

基于SpringBoot的财务管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SpringBoot 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;…