嵌入式C语言自我修养《GNU C编译器扩展语法》学习笔记
目录
一、C语言标准和编译器
二、指定初始化
三、宏构造“利器”:语句表达式
四、typeof与container_of宏
五、零长度数组
六、属性声明:section
七、属性声明:aligned
一、C语言标准和编译器
C语言标准的发展过程:
● K&R C.
● ANSI C.
● C99.
● C11.
二、指定初始化
指定初始化结构体成员:
和数组类似,在C语言标准中,初始化结构体变量也要按照固定的顺序,但在GNU C中我们可以通过结构域来指定初始化某个成员。
在程序中,我们定义一个结构体类型student,然后分别定义两个结构体变量stu1和stu2。初始化stu1时,我们采用C语言标准的初始化方式,即按照固定顺序直接初始化。初始化stu2时,我们采用GNU C的初始化方式,通过结构域名.name和.age,就可以给结构体变量的某一个指定成员直接赋值。当结构体的成员很多时,使用第二种初始化方式会更加方便。
Linux内核驱动注册:
在Linux内核驱动中,大量使用GNU C的这种指定初始化方式,通过结构体成员来初始化结构体变量。如在字符驱动程序中,我们经常见到下面这样的初始化。
指定初始化的好处:
如果采用C标准按照固定顺序赋值,当file_operations结构体类型发生变化时,如添加了一个成员、删除了一个成员、调整了成员顺序,那么使用该结构体类型定义变量的大量C文件都需要重新调整初始化顺序,牵一发而动全身。
通过指定初始化方式,就可以避免这个问题。无论file_operations结构体类型如何变化,添加成员也好、删除成员也好、调整成员顺序也好,都不会影响其他文件的使用。
三、宏构造“利器”:语句表达式
什么是表达式、操作符、操作数?
表达式就是由一系列操作符和操作数构成的式子。操作符可以是C语言标准规定的各种算术运算符、逻辑运算符、赋值运算符、比较运算符。操作数可以是一个常量,也可以是一个变量。
语句表达式:
GNU C对C语言标准作了扩展,允许在一个表达式里内嵌语句,允许在表达式内部使用局部变量、for循环和goto跳转语句。这种类型的表达式,我们称为语句表达式。语句表达式的格式如下。
和一般表达式一样,语句表达式也有自己的值。语句表达式的值为内嵌语句中最后一个表达式的值。
在宏定义中使用语句表达式:请定义一个宏,求两个数的最大值。
合格:
#define MAX(x,y) x > y ? x : y
中等:
#define MAX(x,y) (x) > (y) ? (x) : (y)
良好:
#define MAX(x,y) ((x) > (y) ? (x) : (y))
更良好:
#define MAX(x,y)({ \int _x = x; \int _y = y; \_x > _y ? _x : _y; \
})
优秀:
#define MAX(type,x,y)({ \type _x = x; \type _y = y; \_x > _y ? _x : _y; \
})
更优秀:
#define MAX(x,y)({ \typeof(x) _x = (x); \typeof(y) _y = (y); \(void) (&_x == &_y); \_x > _y ? _x : _y; \
})
在这个宏定义中,我们使用了typeof关键字来自动获取宏的两个参数类型。比较难理解的是(void)(&x==&y);这句话,看起来很多余,仔细分析一下,你会发现这条语句很有意思。它的作用有两个:一是用来给用户提示一个警告,对于不同类型的指针比较,编译器会发出一个警告,提示两种数据的类型不同。
二是两个数进行比较运算,运算的结果却没有用到,有些编译器可能会给出一个warning,加一个(void)后,就可以消除这个警告。
四、typeof与container_of宏
typeof关键字:
ANSI C定义了sizeof关键字,用来获取一个变量或数据类型在内存中所占的字节数。GNU C扩展了一个关键字typeof,用来获取一个变量或表达式的类型。
使用typeof可以获取一个变量或表达式的类型。typeof的参数有两种形式:表达式或类型。
在上面的代码中,因为变量i的类型为int,所以typeof(i)就等于int,typeof(i) j=20就相当于int j=20,typeof(int*) a;相当于int*a,f()函数的返回值类型是int,所以typeof(f()) k;就相当于int k;
Linux内核中的container_of宏:
它的主要作用就是,根据结构体某一成员的地址,获取这个结构体的首地址。根据宏定义,我们可以看到,这个宏有三个参数:type为结构体类型,member为结构体内的成员,ptr为结构体内成员member的地址。也就是说,如果我们知道了一个结构体的类型和结构体内某一成员的地址,就可以获得这个结构体的首地址。container_of宏返回的就是这个结构体的首地址。
结构体作为一个复合类型数据,它里面可以有多个成员。当我们定义一个结构体变量时,编译器要给这个变量在内存中分配存储空间。根据每个成员的数据类型和字节对齐方式,编译器会按照结构体中各个成员的顺序,在内存中分配一片连续的空间来存储它们。
在这个程序中,我们定义一个结构体,里面有3个int型数据成员。我们定义一个变量stu,分别打印这个变量stu的地址、各个成员变量的地址,程序运行结果如下。
从运行结果可以看到,结构体中的每个成员变量,从结构体首地址开始依次存放,每个成员变量相对于结构体首地址,都有一个固定偏移。如num相对于结构体首地址偏移了4字节。math的存储地址相对于结构体首地址偏移了8字节。
一个结构体数据类型,在同一个编译环境下,各个成员相对于结构体首地址的偏移是固定不变的。我们可以修改一下上面的程序:当结构体的首地址为0时,结构体中各个成员的地址在数值上等于结构体各成员相对于结构体首地址的偏移。
在上面的程序中,我们没有直接定义结构体变量,而是将数字0通过强制类型转换,转换为一个指向结构体类型为student的常量指针,然后分别打印这个常量指针指向的各成员地址。运行结果如下。
从语法角度来看,container_of宏的实现由一个语句表达式构成。语句表达式的值即最后一个表达式的值。
最后一句的意义就是,取结构体某个成员member的地址,减去这个成员在结构体type中的偏移,运算结果就是结构体type的首地址。因为语句表达式的值等于最后一个表达式的值,所以这个结果也是整个语句表达式的值,container_of最后会返回这个地址值给宏的调用者。
计算结构体某个成员在结构体内的偏移,内核中定义了offset宏来实现这个功能.
这个宏有两个参数,一个是结构体类型TYPE,一个是结构体TYPE的成员MEMBER,它使用的技巧和我们上面计算零地址常量指针的偏移是一样的。将0强制转换为一个指向TYPE类型的结构体常量指针,然后通过这个常量指针访问成员,获取成员MEMBER的地址,其大小在数值上等于MEMBER成员在结构体TYPE中的偏移。
结构体的成员数据类型可以是任意数据类型,为了让这个宏兼容各种数据类型,我们定义了一个临时指针变量__mptr,该变量用来存储结构体成员MEMBER的地址,即存储宏中的参数ptr的值。如何获取ptr指针类型呢,可以通过下面的方式。
宏的参数ptr代表的是一个结构体成员变量MEMBER的地址,所以ptr的类型是一个指向MEMBER数据类型的指针,当我们使用临时指针变量__mptr来存储ptr的值时,必须确保__mptr的指针类型和ptr一样,是一个指向MEMBER类型的指针变量。typeof(((type*)0)->member)表达式使用typeof关键字,用来获取结构体成员MEMBER的数据类型,然后使用该类型,通过typeof(((type*)0)->member)*__mptr这条程序语句,就可以定义一个指向该类型的指针变量了。
在语句表达式的最后,因为返回的是结构体的首地址,所以整个地址还必须强制转换一下,转换为TYPE*,即返回一个指向TYPE结构体类型的指针,所以你会在最后一个表达式中看到一个强制类型转换(TYPE*)。
五、零长度数组
顾名思义,零长度数组就是长度为0的数组。ANSI C标准规定:定义一个数组时,数组的长度必须是一个常数,即数组的长度在编译的时候是确定的。在ANSI C中定义一个数组的方法如下。
数组的长度在编译时是未确定的,在程序运行的时候才确定,甚至可以由用户指定大小。
指针与零长度数组:
数组名在作为参数传递时,传递的确实是一个地址,但数组名绝不是指针,两者不是同一个东西。数组名用来表征一块连续内存空间的地址,而指针是一个变量,编译器要给它单独分配一个内存空间,用来存放它指向的变量的地址。我们看下面的程序。
运行结果如下。
对于一个指针变量,编译器要为这个指针变量单独分配一个存储空间,然后在这个存储空间上存放另一个变量的地址,我们就说这个指针指向这个变量。而对于数组名,编译器不会再给它分配一个单独的存储空间,它仅仅是一个符号,和函数名一样,用来表示一个地址。如下代码:
#include <stdio.h>int array1[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int array2[0];
int *p = &array1[5];int main(void)
{return 0;
}
在这个程序中,我们分别定义一个普通数组、一个零长度数组和一个指针变量。其中这个指针变量p的值为array1[5]这个数组元素的地址,也就是说,指针p指向arraay1[5]。我们接着对这个程序使用ARM交叉编译器进行编译,并进行反汇编。
从反汇编生成的汇编代码中,我们找到array1和指针变量p的汇编代码。
从汇编代码中,可以看到,对于长度为10的数组array1[10],编译器给它分配了从0x20524~0x20548共40字节的存储空间,但并没有给数组名array1单独分配存储空间,数组名array1仅仅表示这40个连续存储空间的首地址,即数组元素array1[0]的地址。对于指针变量p,编译器给它分配了0x20538这个存储空间,在这个存储空间上存储的是数组元素array1[5]的地址:0x20538。
而对于array2[0]这个零长度数组,编译器并没有为它分配存储空间,此时的array2仅仅是一个符号,用来表示内存中的某个地址,我们可以通过查看可执行文件a.out的符号表来找到这个地址值。
readelf -s a.out
从符号表可以看到,array2的地址为0x21054,在BSS段的后面。array2符号表示的默认地址是一片未使用的内存空间,仅此而已,编译器绝不会单独再给其分配一个存储空间来存储数组名。
数组名和指针并不是一回事,数组名虽然在作为函数参数时,可以当作一个地址使用,但是两者不能画等号。
六、属性声明:section
GNU C编译器扩展关键字:__attribute__
__attribute__的使用非常简单,当我们定义一个函数、变量或类型时,直接在它们名字旁边添加下面的属性声明即可。
使用__atttribute__这个属性声明,就相当于告诉编译器:按照我们指定的边界对齐方式去给这个变量分配存储空间。
有些属性可能还有自己的参数。如aligned(8)表示这个变量按8字节地址对齐,属性的参数也要使用小括号括起来,如果属性的参数是一个字符串,则小括号里的参数还要用双引号引起来。
我们可以使用__attribute__来声明一个section属性,section属性的主要作用是:在程序编译时,将一个函数或变量放到指定的段,即放到指定的section中。
一个可执行文件主要由代码段、数据段、BSS段构成。代码段主要存放编译生成的可执行指令代码,数据段和BSS段用来存放全局变量、未初始化的全局变量。代码段、数据段和BSS段构成了一个可执行文件的主要部分。
除了这三个段,可执行文件中还包含其他一些段。用编译器的专业术语讲,还包含其他一些section,如只读数据段、符号表等。我们可以使用下面的readelf命令,去查看一个可执行文件中各个section的信息。
例如下面的程序,我们分别定义一个函数、一个全局变量和一个未初始化的全局变量。
#include <stdio.h>
int global_val = 8;
int global_val;void print_star(void)
{printf("****\n");
}
int main(void)
{print_star();return 0;
}
readelf
是一个用于查看和分析可执行文件、共享库和目标文件的工具。它提供了多种选项来显示不同类型的信息。其中,-s
选项和-S
选项用于显示不同的符号表和节表信息。
-s
选项:-s
选项用于显示符号表(Symbol Table)的信息。符号表是一个记录了程序中各种符号(如函数、变量、常量等)的表格,它包含了符号的名称、类型、大小、地址等信息。使用-s
选项可以查看符号表中的符号列表以及相关的属性。示例命令:
readelf -s <file>
-S
选项:-S
选项用于显示节表(Section Table)的信息。节表是一个记录了程序各个节(Section)的表格,它包含了每个节的名称、类型、大小、偏移量等信息。节表描述了程序的不同部分,如代码段、数据段、BSS段、符号表等。示例命令:
readelf -S <file>
总结:
-s
选项用于显示符号表的信息,包括符号的名称、类型、大小、地址等。-S
选项用于显示节表的信息,包括节的名称、类型、大小、偏移量等。
查看可执行文件的符号表信息:
对应的section header表信息如下。
通过符号表和section header表信息,我们可以看到,函数print_star(400526)被放在可执行文件中的.text section(400430),即代码段;初始化的全局变量global_val(601038)被放在了a.out的.data section(601028),即数据段;而未初始化的全局变量uninit_val(601040)则被放在了.bss section(60103c),即BSS段。
编译器在编译程序时,以源文件为单位,将一个个源文件编译生成一个个目标文件。在编译过程中,编译器都会按照这个默认规则,将函数、变量分别放在不同的section中,最后将各个section组成一个目标文件。编译过程结束后,链接器会将各个目标文件组装合并、重定位,生成一个可执行文件。
在GNU C中,我们可以通过__attribute__的section属性,显式指定一个函数或变量,在编译时放到指定的section里面。通过上面的程序我们知道,未初始化的全局变量默认是放在.bss section中的,即默认放在BSS段中。现在我们就可以通过section属性声明,把这个未初始化的全局变量放到数据段.data中。
通过readelf命令查看符号表,我们可以看到,uninit_val(601034)这个未初始化的全局变量,通过__attribute__((section(".data")))属性声明,就和初始化的全局变量一样,被编译器放在了数据段.data(601020)中。
U-boot镜像自复制分析:
有了section这个属性声明,我们就可以试着分析:U-boot在启动过程中,是如何将自身代码加载的RAM中的。U-boot的用途主要是加载Linux内核镜像到内存,给内核传递启动参数,然后引导Linux操作系统启动。U-boot一般存储在NOR Flash或NAND Flash上。无论从NOR Flash还是从NAND Flash启动,U-boot其本身在启动过程中,都会从Flash存储介质上加载自身代码到内存,然后进行重定位,跳到内存RAM中去执行。
char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
char __image_copy_end[0] __attribute__((section(".__image_copy_end")));
这两行代码定义在U-boot-2016.09中的arch/arm/lib/section.c文件中。在其他版本的U-boot中可能路径不同,这两行代码的作用是分别定义一个零长度数组,并指示编译器要分别放在.__image_copy_start和.__image_copy_end这两个section中。
链接器在链接各个目标文件时,会按照链接脚本里各个section的排列顺序,将各个section组装成一个可执行文件。U-boot的链接脚本Uboot.lds在U-boot源码的根目录下面。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{. = 0x00000000;. = ALIGN(4);.text :{*(.__image_copy_start)*(.vectors)arch/arm/cpu/armv7/start.o (.text*)*(.text*)}. = ALIGN(4);.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }. = ALIGN(4);.data : {*(.data*)}. = ALIGN(4);. = .;. = ALIGN(4);.u_boot_list : {KEEP(*(SORT(.u_boot_list*)));}. = ALIGN(4);.image_copy_end :{*(.__image_copy_end)}.rel_dyn_start :{*(.__rel_dyn_start)}.rel.dyn : {*(.rel*)}.rel_dyn_end :{*(.__rel_dyn_end)}.end :{*(.__end)}_image_binary_end = .;. = ALIGN(4096);.mmutable : {*(.mmutable)}.bss_start __rel_dyn_start (OVERLAY) : {KEEP(*(.__bss_start));__bss_base = .;}.bss __bss_base (OVERLAY) : {*(.bss*). = ALIGN(4);__bss_limit = .;}.bss_end __bss_limit (OVERLAY) : {KEEP(*(.__bss_end));}.dynsym _image_binary_end : { *(.dynsym) }.dynbss : { *(.dynbss) }.dynstr : { *(.dynstr*) }.dynamic : { *(.dynamic*) }.plt : { *(.plt*) }.interp : { *(.interp*) }.gnu.hash : { *(.gnu.hash) }.gnu : { *(.gnu*) }.ARM.exidx : { *(.ARM.exidx*) }.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}
通过链接脚本我们可以看到,__image_copy_start和__image_copy_end这两个section,在链接的时候分别放在了代码段.text的前面、数据段.data的后面,作为U-boot复制自身代码的起始地址和结束地址。而在这两个section中,我们除了放两个零长度数组,并没有放其他变量。
在arch/arm/lib/relocate.S中,ENTRY(relocate_code)汇编代码主要完成代码复制的功能。
ENTRY(relocate_code)ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */subs r4, r0, r1 /* r4 <- relocation offset */beq relocate_done /* skip relocation */ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */copy_loop:ldmia r1!, {r10-r11} /* copy from source address [r1] */stmia r0!, {r10-r11} /* copy to target address [r0] */cmp r1, r2 /* until source end address [r2] */blo copy_loop
在这段汇编代码中,寄存器R1、R2分别表示要复制镜像的起始地址和结束地址,R0表示要复制到RAM中的地址,R4存放的是源地址和目的地址之间的偏移,在后面重定位过程中会用到这个偏移值。在汇编代码中:
ldr r1, =__image_copy_start
通过ARM的LDR伪指令,直接获取要复制镜像的首地址,并保存在R1寄存器中。数组名本身其实就代表一个地址,通过这种方式,Uboot在嵌入式启动的初始阶段,就完成了自身代码的复制工作:从Flash复制自身镜像到内存中,然后进行重定位,最后跳到内存中执行。
七、属性声明:aligned
地址对齐:aligned
GNU C通过__attribute__来声明aligned和packed属性,指定一个变量或类型的对齐方式。这两个属性用来告诉编译器:在给变量分配存储空间时,要按指定的地址对齐方式给变量分配地址。如果你想定义一个变量,在内存中以8字节地址对齐,就可以这样定义。
通过aligned属性,我们可以显式地指定变量a在内存中的地址对齐方式。aligned有一个参数,表示要按几字节对齐,使用时要注意,地址对齐的字节数必须是2的幂次方,否则编译就会出错。
编译器一定会按照aligned指定的方式对齐吗?
通过aligned属性,我们可以显式指定一个变量的对齐方式,编译器就一定会按照我们指定的大小对齐吗?非也!我们通过这个属性声明,其实只是建议编译器按照这种大小地址对齐,但不能超过编译器允许的最大值。一个编译器,对每个基本数据类型都有默认的最大边界对齐字节数。如果超过了,则编译器只能按照它规定的最大对齐字节数来给变量分配地址。
在这个程序中,我们指定char型的变量c2以16字节对齐,编译运行结果如下。
我们可以看到,编译器给c2分配的地址是按16字节地址对齐的,如果我们继续修改c2变量按32字节对齐,你会发现程序的运行结果不再有变化,编译器仍然分配一个16字节对齐的地址,这是因为32字节的对齐方式已经超过编译器允许的最大值了。
属性声明:packed
aligned属性一般用来增大变量的地址对齐,元素之间因为地址对齐会造成一定的内存空洞。而packed属性则与之相反,一般用来减少地址对齐,指定变量或类型使用最可能小的地址对齐方式。
在上面的程序中,我们将结构体的成员b和c使用packed属性声明,就是告诉编译器,尽量使用最可能小的地址对齐给它们分配地址,尽可能地减少内存空洞。程序的运行结果如下。
通过结果我们看到,结构体内各个成员地址的分配,使用最小1字节的对齐方式,没有任何内存空间的浪费,导致整个结构体的大小只有7字节。
这个特性在底层驱动开发中还是非常有用的。例如,你想定义一个结构体,封装一个IP控制器的各种寄存器,在ARM芯片中,每一个控制器的寄存器地址空间一般都是连续存在的。如果考虑数据对齐,则结构体内就可能有空洞,就和实际连续的寄存器地址不一致。使用packed可以避免这个问题,结构体的每个成员都紧挨着,依次分配存储地址,这样就避免了各个成员因地址对齐而造成的内存空洞。
我们也可以对整个结构体添加packed属性,这和分别对每个成员添加packed属性效果是一样的。修改结构体后,重新编译程序,运行结果和上面程序的运行结果相同:结构体的大小为7,结构体内各成员地址相同。
相关文章:
嵌入式C语言自我修养《GNU C编译器扩展语法》学习笔记
目录 一、C语言标准和编译器 二、指定初始化 三、宏构造“利器”:语句表达式 四、typeof与container_of宏 五、零长度数组 六、属性声明:section 七、属性声明:aligned 一、C语言标准和编译器 C语言标准的发展过程: ●…...
密码学二: md5 网站服务器与用户通信过程 ca原理 签名原理 Flame 病毒原理
md5被破解? MD5(Message Digest Algorithm 5)是一个较早的哈希函数,但由于其弱点和漏洞,它已经被认为不再适合用于安全性要求较高的应用。MD5的一些安全性问题包括: 碰撞攻击: MD5已经被证明容易受到碰撞攻…...
Zend Framework 3.1.3 gadget chain
前言 在推特上的PT SWARM账号发布了一条消息。 一个名为Zend Framework的php框架出现了新的gadget chain,可导致RCE。笔者尝试复现,但失败了。所幸,我基于此链,发现在这个框架的最新版本中的另一条链。 复现过程 这里使用vscod…...
互联网Java工程师面试题·Java 并发编程篇·第四弹
目录 39、volatile 有什么用?能否用一句话说明下 volatile 的应用场景? 40、为什么代码会重排序? 41、在 java 中 wait 和 sleep 方法的不同? 42、用 Java 实现阻塞队列 43、一个线程运行时发生异常会怎样? 44、…...
3、Linux下安装
以下操作仅限于rh系列:支持rpm/yum安装方式,不支持deb/apt安装方式。 以下操作仅限于rh系列:支持rpm/yum安装方式,不支持 deb/apt安装方式。 1、在线下载安装包: wget https://downloads.mysql.com/archives/get/p/23/file/ m…...
Zookeeper【Curator客户端Java版】从0到1——万字学习笔记
目录 初识Zookeeper Zookeeper作用 维护配置信息 分布式锁服务 集群管理 生产分布式唯一ID Zookeeper的设计目标 Zookeeper 工作机制 数据模型 ZooKeeper 命令操作 服务端常用命令 客户端常用命令 ZooKeeper JavaAPI操作 Curator 介绍 Curator API 常用操作 导入依赖 建立连接 …...
生物标志物发现中的无偏数据分析策略
目录 0. 导论基本概念 1. 生物标志物发现的注意事项2. 数据预处理2.1 高质量原始数据和缺失值处理2.2 数据过滤2.3 数据归一化 3. 数据质量评估3.1 混杂因素3.2 类别分离3.3 功效分析3.4 批次效应 4. 生物标志物发现4.1 策略4.2 数据分析工具4.3 模型优化策略 0. 导论 组学技术…...
华为校招机试题- 机器人活动区域-2023年
题目描述: 现有一个机器人,可放置于 M N的网格中任意位置,每个网格包含一个非负整数编号。当相邻网格的数字编号差值的绝对值小于等于 1 时,机器人可在网格间移动 问题:求机器人可活动的最大范围对应的网格点数目。 说明: 1)网格左上角坐标为 (0, 0),右下角坐标为 (m-…...
半屏小程序
准备工作 tip 管理后台配置 设置-》第三方设置-》半屏小程序管理-》我调用的 添加小程序 有些手机会唤起失败,直接唤起了全屏的小程序,所以我们为了兼容,需要在app.config.ts加上 {"embeddedAppIdList": ["wxxxxxxxx"]/…...
2023年最新Python大数据之Python基础【七】管理系统
文章目录 7、学生管理系统8、函数递归9、lambda函数后记 7、学生管理系统 # 需求拆分:1.展示学生管理系统的功能有哪些,引导用户键入序号选择功能 2.获取用户键入的功能 3.分析具体要执行哪一项功能 4.执行功能 def print_all_option():"""用户功能界面展示&qu…...
【网安】网络安全防止个人信息泄露
网络安全防止个人信息泄露 1、尝试检查自己的网络隐私数据是否泄漏过,可以使用下面的网站2、使用安全非盈利组织的浏览器3、安装浏览器插件,防止网络跟踪4、保持安全的访问方式 1、尝试检查自己的网络隐私数据是否泄漏过,可以使用下面的网站 …...
ChatGPT,AIGC 数据库应用 Mysql 常见优化30例
使用ChatGPT,AIGC总结出Mysql的常见优化30例。 1. 建立合适的索引:在Mysql中,索引是重要的优化手段,可以提高查询效率。确保表的索引充分利用,可以减少查询所需的时间。如:create index idx_name on table_name(column_name); 2. 避免使用select * :尽可能指定要返回的…...
并查集路径压缩
并查集里的 find 函数里可以进行路径压缩,是为了更快速的查找一个点的根节点。对于一个集合树来说,它的根节点下面可以依附着许多的节点,因此,我们可以尝试在 find 的过程中,从底向上,如果此时访问的节点不…...
spring和springMVC的说明
Spring和Spring MVC都是Java应用程序开发中常用的框架,它们提供了一种结构化的方法来构建企业级Java应用程序。下面我将对它们进行详细的说明: Spring: 概述: Spring是一个综合的Java应用程序开发框架,旨在简化企业级…...
软件工程与计算总结(十)软件体系结构设计与构建
目录 编辑 一.体系结构设计过程 1.分析关键需求和项目约束 2.选择体系结构风格 3.体系结构逻辑设计 4.体系结构实现 5.完善体系结构设计 6.定义构件接口 二.体系结构原型构建 1.包的创建 2.重要文件的创建 3.定义构件之间的接口 4.关键需求的实现 三.体系结构的…...
【实操】基于ChatGPT构建知识库
前言 最近有些实践,因为后面要去研究fine-tune了,想着记录一下chatgpt向量数据库构建知识库的一些实操经验,不记我很快就忘了,哈哈。 首先,提一下为啥会出现向量数据库这个技术方案? 大家经过实践发现&…...
ribbonx编程笔记-读写注册表与使用自定义对话框
Windows 注册表是一个数据库,用于存储与计算机不同方面相关的设置,例如用户设置、应用程序设备、硬件设置,等等。 VBA 提供了与注册表直接交互的方式,这不仅允许我们获取其它程序和硬件的信息,而且也能够使我们选择应用程序中的重要信息并将其存储在注册表中。本文中,…...
网工记背配置命令(3)----POE配置示例
POE 供电就是通过以太网供电,这种方式仅凭借那根连接通信终端的网线就可完成为它们供电。POE提供的是-53V~0v 的直流电,供电距离最长可达 100m。PoE 款型的交换机的软件大包天然支持 POE,无需 license,通过执行 poe-enable 命令使…...
网络安全(黑客技术)—0基础学习手册
目录 一、自学网络安全学习的误区和陷阱 二、学习网络安全的一些前期准备 三、网络安全学习路线 四、学习资料的推荐 想自学网络安全(黑客技术)首先你得了解什么是网络安全!什么是黑客! 网络安全可以基于攻击和防御视角来分类…...
[部署网站]01安装宝塔面板搭建WordPress
宝塔面板安装WordPress(超详细)_Wordpress主题网 参考教程 宝塔面板 - 简单好用的Linux/Windows服务器运维管理面板 官网 1.首先你需要一个服务器或者主机 (Windows系统或者Linux系统都可以) 推荐Linux系统更稳定,…...
Can We Edit Multimodal Large Language Models?
本文是LLM系列文章,针对《Can We Edit Multimodal Large Language Models?》的翻译。 我们可以编辑多模态大型语言模型吗? 摘要1 引言2 相关工作3 编辑多模态LLM4 实验5 结论 摘要 本文主要研究多模态大语言模型(Multimodal Large Language Models, mllm)的编辑…...
使用jsqlparser创建MySQL建表语句
语法 create table [IF NOT EXISTS] 表名 ( 字段名 类型 [约束条件], 字段名 类型 [约束条件], 字段名 类型 [约束条件], 字段名 类型 [约束条件] ); 字段定义在括号内约束条件可以有多个多个字段定义之间用都会隔开 常见约束 NOT NULL 非空DEFAULT 0 默认值AUTO_INCREMENT…...
字符串思维题练习 DAY6 (CF 245H , CF 559B , CF 1731C , CF1109B)
字符串思维题练习 DAY6 (CF 245H , CF 559B , CF 1731C , CF1109B) CF 245 H. Queries for Number of Palindromes(字符串 dp) Problem - H - Codeforces 大意:给出一个字符串S (|S| ≤ 5000) , 给出 Q 次询问 , 每…...
Linux:Mac VMware Fusion13以及CentOS7安装包
Linux:Mac VMware Fusion13以及CentOS7安装包 1. Mac VMware Fusion132. CentOS7安装包3. 安装 1. Mac VMware Fusion13 下载官网地址:https:www.vmware.com/products/fusion/fusion-evaluation.html 2. CentOS7安装包 注意是m芯片需要使用arm架构的i…...
【微服务部署】十、使用Docker Compose搭建高可用Redis集群
现如今,业务系统对于缓存Redis的依赖似乎是必不可少的,我们可以在各种各样的系统中看到Redis的身影。考虑到系统运行的稳定性,Redis的应用和MySQL数据库一样需要做到高可用部署。 一、Redis 的多种高可用方案 常见的Redis的高可用方案有以下…...
【数据结构】树状数组C++详解
文章目录 引入树状数组定义什么是单点修改和区间查询工作原理区间查询代码实现单点修改实现代码242. 一个简单的整数问题AC代码如下:练习:AC代码如下:引入 242. 一个简单的整数问题 给定长度为 N的数列 A A A<...
机器人制作开源方案 | 扫地机器人
1. 功能描述 扫地机器人是现代家庭清洁的得力助手,能够自主规划清扫路径,避开障碍物,有效覆盖整个清洁区域。扫地机器人的出现极大地减轻了家庭清洁的负担,节省了时间和精力,它可以定期清理地面,确保家居环…...
10.2手动推导linux中file, cdev, inode之间的关系
是时候可以手动推导一下linux里面基类父类和子类的关系了 代码放最后把 简单说明版 详细流程 第一步注册驱动 cdev结构体能看做是一个基类,那么链表里面都是字符设备驱动的cdev连载一起,啥串口,lcd的,通过cdev->list_head连接 那cdev结构体里有主次设备号 第一步 使用r…...
JavaScript基础知识13——运算符:一元运算符,二元运算符
哈喽,大家好,我是雷工。 JavaScript的运算符可以根据所需表达式的个数,分为一元运算符、二元运算符、三元运算符。 一、一元运算符 1、一元运算符:只需要一个表达式就可以运算的运算符。 示例:正负号 一元运算符有两…...
异步使用langchain
文章目录 一.先利用langchain官方文档的AI功能问问二.langchain async api三.串行,异步速度比较 一.先利用langchain官方文档的AI功能问问 然后看他给的 Verified Sources 这个页面里面虽然有些函数是异步函数,但是并非专门讲解异步的 二.langchain asy…...
wordpress dokuwiki/免费行情网站
效果图 微信小程序交流群:111733917 | 微信小程序从0基础到就业的课程:https://edu.csdn.net/topic/huangjuhua wxml代码 <view>按下去不做任何样式 </view><view hover-classmyred>按下去做样式 </view><view>flex-di…...
建设企业网站公司/软件测试培训费用大概多少
解决办法: #编辑文件 vi test.sh#检查是否含有\r换行符 :set ff?#如果显示fileforma=dos,则需要进行格式转换 :set fileformatunix#保存退出 :wq...
做网站大公司还是小公司/网站怎么优化
2018年洛阳十大技校排名 排名前十的学校有哪些2018-06-23 19:25:32文/王蕊中考结束后一定有很多初中生想就读技校,下面,初三网小编就为大家整理一下2018年洛阳最好的十大技校排名,以便大家选择合适的技校。2018洛阳十大技校排名技校排名技校名…...
泸州做网站公司/手机建网站软件
隐写3 打开是一张图片 winhex打开没发现异常 winhex改一下长高后发现碰巧出了flag zip伪加密 简单题 winhex打开后将图片的09改为00即可解伪加密 赛博朋克 打开是一个txt打开发现有PNG前缀 于是改后缀为png 发现一张图片 winhex查看后发现有Steganography字样 就猜测为LS…...
iis网站伪静态/谷歌google
这是免费系列教程《7天学会商业智能(BI)-Tableau》的第5天,前面我们介绍了如何用Tableau对数据建模?,今天介绍如何用Tableau可视化数据。你将学会:如何可视化数据?如何创建筛选器?如何绘制地图?…...
图片站wordpress模板下载/免费网页在线客服系统代码
20172303 2018-2019-1 《程序设计与数据结构》第1周学习总结 教材学习内容总结 第1章 概述 1.软件工程 定义:一门关于高质量软件开发的技术和理论的学科。目标:软件工程的目标与其他工程学科类似 解决正确性问题————客户所需实现的需求按时且在预算之…...