内存对齐:C/C++编程中的重要性和技巧
C/C++中的内存对齐
- 前言
- 基本概念
- 什么是内存对齐?
- 内存对齐的定义
- 内存对齐的作用
- 数据类型的大小
- ARM 64 位架构和 x86_64 架构下的数据类型大小
- ARM 32 位架构下的数据类型大小
- 内存对齐的边界
- 填充字节的作用
- 内存对齐的原理
- 结构体中的内存对齐
- 结构体的定义和使用
- 结构体中成员变量的内存对齐
- 结构体中填充字节的作用
- 结构体的大小和内存对齐方式
- 压栈中的内存对齐
- 压栈的定义和实现
- 压栈中变量的内存对齐
- 压栈中变量的内存对齐与结构体不同的点
- 压栈中填充字节的作用
- 压栈中变量的地址和内存对齐方式
- 其他内存对齐方式
- 类成员变量的内存对齐
- 类成员在进行内存对齐的注意点
- 指针的内存对齐
- 动态内存分配的内存对齐
- 内存对齐的优缺点
- 内存对齐的优点
- 内存对齐的缺点
- 如何进行内存对齐
- 编译器提供的特殊指令和选项
- 使用库和工具进行内存对齐
- 内存对齐的注意事项
- 实例分析
- 结构体内存对齐的实例分析
- 压栈内存对齐的实例分析
- 其他内存对齐方式的实例分析
- 内存对齐的实现原理
- 硬件层面
- 软件层面
- 总结取消和控制内存对齐的方式
- 结语
前言
内存对齐是计算机系统中的一个重要概念,它可以提高内存访问的效率和安全性。在C/C++编程中,了解内存对齐的原理和实现方式非常重要,可以帮助开发者编写高效和安全的程序。本篇文章将介绍C/C++中内存对齐的概念、原理、实现方式和注意事项,并通过实例分析来帮助读者更好地理解内存对齐的作用和实现方法。如果你想深入了解计算机系统中的内存管理和优化,本篇文章将是一个不错的入门指南。
基本概念
内存对齐是指将数据存储在内存中时,要求数据的地址必须是某个值的倍数。例如,32位系统中,整型数据的地址必须是4的倍数,双精度浮点数的地址必须是8的倍数。内存对齐是计算机系统中的一个重要概念,它可以提高内存访问的效率和安全性。
内存对齐是指将数据存储在内存中时,要求数据的地址必须是某个值的倍数。这个值称为对齐边界(Alignment Boundary)。对齐边界通常是数据类型的大小,例如,整型数据的对齐边界是4字节,双精度浮点数的对齐边界是8字节。
内存对齐可以提高内存访问的效率和安全性。当数据按照对齐边界存储时,CPU可以更快地访问内存,从而提高程序的运行效率。
在现代计算机中,CPU从内存中读取数据是按照一定的块大小进行的。如果数据没有按照块大小对齐存储在内存中,CPU就需要进行额外的操作来从内存中读取正确的数据,这会降低程序的运行效率。此外,如果变量没有按照正确的方式对齐存储,还可能导致程序出现未定义行为或内存泄漏等问题,从而影响程序的安全性。同时,内存对齐可以保证数据的访问是安全的,避免了因为数据存储位置不对齐而导致的内存读写错误和数据损坏等问题。
对于char类型的变量,它们占用的空间很小,只有1个字节,因此没有必要进行内存对齐。即使在没有对齐的情况下,CPU仍然可以通过单字节访问的方式直接从内存中读取char类型的数据,不会造成额外的开销。
对于int类型的变量,它们占用的空间较大,通常是4个字节或8个字节,而CPU从内存中读取数据的块大小通常也是4个字节或8个字节。如果int类型的变量没有按照正确的方式对齐存储,CPU就需要进行额外的操作来从内存中读取正确的数据,这会降低程序的运行效率。因此,为了提高程序的效率和安全性,int类型的变量通常要进行内存对齐。不同数据类型在内存中占用的字节数不同,例如,整型数据通常占用4字节,双精度浮点数通常占用8字节。在进行内存对齐时,需要考虑数据类型的大小。
ARM 64 位架构和 x86_64 架构下的数据类型大小
数据类型 大小 (字节) char 1 signed char 1 unsigned char 1 short 2 unsigned short 2 int 4 unsigned int 4 long 8 unsigned long 8 long long 8 unsigned long long 8 float 4 double 8 在 ARM 64 位架构和 x86_64 架构下,大部分数据类型的大小是一样的,但也有一些细微的差别。需要注意的是,不同的编译器和操作系统可能会有所不同,这里只是列举了一些常见的数据类型大小。另外,x86_64 架构下的 long double 数据类型大小可能会因编译器实现的不同而有所区别,可能是 16、12 或 16 字节。
ARM 32 位架构下的数据类型大小
数据类型 大小 (字节) char 1 signed char 1 unsigned char 1 short 2 unsigned short 2 int 4 unsigned int 4 long 4 unsigned long 4 long long 8 unsigned long long 8 float 4 double 8 long double 8 内存对齐的边界是指数据存储的起始地址必须是某个值的倍数。
在32位系统中,整型数据的地址必须是4的倍数,双精度浮点数的地址必须是8的倍数。
在进行内存对齐时,需要考虑对齐边界。为了满足内存对齐的要求,编译器会在数据成员之间插入一些不需要的字节,这些字节称为填充字节。
填充字节的作用是保证数据成员存储在对齐的边界上,从而保证内存访问的效率和安全性。填充字节的数量取决于数据成员的大小和对齐边界的大小。内存对齐的原理是在变量之间插入填充字节,使得变量按照对齐方式排列。这是因为CPU访问内存时,通常是按照特定的方式进行访问的。如果变量的内存地址不是按照对齐方式进行排列的,那么CPU在访问这个变量时需要进行额外的计算,从而影响程序的运行效率。
在进行内存对齐时,编译器会根据数据类型的大小和对齐边界来计算出需要插入多少个填充字节,以保证变量按照对齐方式排列。
例如,当定义一个结构体时,编译器会对结构体中的每个成员变量进行内存对齐,使得成员变量按照对齐方式排列。在进行内存对齐时,编译器通常会遵循以下规则:
变量的对齐方式通常是变量类型的大小和平台的字长中的较小值。
结构体和联合体中的成员变量按照声明的顺序进行内存对齐。
结构体和联合体中的成员变量的对齐方式是成员变量类型的大小和平台字长中的较小值。
单个变量的对齐方式可以使用特殊的指令和选项进行控制。
总之,内存对齐的原理是通过插入填充字节来保证变量按照对齐方式排列,以提高程序的运行效率。
结构体中的内存对齐
结构体中的内存对齐是指在结构体中成员变量所占用的内存空间的排列方式。这是由编译器自动进行的处理。
结构体的定义和使用是通过定义一个包含多个成员变量的数据类型来创建一个结构体。通过创建结构体变量并访问结构体成员变量的方式来使用结构体。
结构体的定义和使用需要使用 struct 关键字。例如,定义一个包含两个成员变量的结构体可以使用以下语法:struct Person {char name[20];int age; };
可以使用以下方式声明和使用一个 Person 结构体的变量:
struct Person p; p.age = 20; strcpy(p.name, "John");
在结构体中,成员变量的内存对齐是指编译器在分配内存空间时,会按照一定的规则将成员变量排列在内存中。这样可以提高内存访问的效率,减少内存碎片的产生。
结构体中成员变量的内存对齐方式通常是根据变量类型和平台字长来决定的。例如,在 x86 平台上,int 类型的变量需要按照 4 字节对齐,而 char 类型的变量只需要按照 1 字节对齐。如果结构体中包含一个 int 类型的变量和一个 char 类型的变量,那么编译器会在 char 变量后面自动填充 3 个字节的空间,使得后面的 int 变量可以按照 4 字节对齐方式排列。结构体中的填充字节是为了保证结构体中的成员变量按照对齐方式排列,从而提高程序的运行效率。在进行内存对齐时,编译器会自动插入填充字节,使得成员变量按照对齐方式排列。填充字节通常是无效的字节,只是为了填充内存空间。
结构体的大小取决于结构体中成员变量的大小和内存对齐方式。在进行内存对齐时,编译器会自动插入填充字节,使得成员变量按照对齐方式排列。结构体的大小通常是成员变量大小的总和加上填充字节的总和。例如,如果一个结构体中包含一个 char 类型的变量和一个 int 类型的变量,在 x86 平台上,该结构体的大小为 8 字节(1 字节的 char 变量后面填充了 3 字节的空间,然后是 4 字节的 int 变量)。
压栈中的内存对齐
压栈是指将数据存储到栈中的过程,栈是一种后进先出(LIFO)的数据结构。
在函数调用过程中,函数的参数和局部变量通常会被压入栈中,然后在函数返回时再从栈中弹出这些数据。压栈通常通过使用栈指针(stack pointer)实现。
栈指针指向栈顶的地址,每次数据入栈时栈指针向下移动一定的偏移量,数据出栈时栈指针向上移动相应的偏移量。在进行压栈时,变量的内存对齐方式决定了变量在栈中存储的位置。与结构体类似,变量的内存对齐方式通常是根据变量类型和平台字长来决定的。
例如,在 x86 平台上,int 类型的变量需要按照 4 字节对齐,而 char 类型的变量只需要按照 1 字节对齐。如果在函数中定义了一个 int 类型的变量和一个 char 类型的变量,那么编译器会在 char 变量后面自动填充 3 个字节的空间,使得后面的 int 变量可以按照 4 字节对齐方式排列。在结构体中,编译器通常按照结构体成员中最大数据类型的大小进行对齐,以保证结构体的每个成员都能够对齐到正确的边界。在进行对齐时,编译器会在成员之间添加填充字节,以使得下一个成员的地址能够对齐到特定字节的边界。
在压栈过程中,编译器也会进行内存对齐,但对齐方式可能略有不同。在压栈过程中,编译器通常会按照数据类型的大小进行对齐,不同类型的变量有不同的对齐方式。例如,在32位系统中,对于int和float类型的变量,通常按照4字节对齐;对于double类型的变量,通常按照8字节对齐。在进行对齐时,编译器也会在变量之间添加填充字节,以使得下一个变量的地址能够对齐到特定字节的边界。
和结构体一样,压栈中的填充字节也是为了保证变量按照对齐方式排列,从而提高程序的运行效率。
在进行内存对齐时,编译器会自动插入填充字节,使得变量按照对齐方式排列。填充字节通常是无效的字节,只是为了填充内存空间。
在压栈时,变量的地址和内存对齐方式通常是根据栈指针和变量的内存对齐方式来决定的。
栈指针指向栈顶的地址,每次数据入栈时栈指针向下移动一定的偏移量,数据出栈时栈指针向上移动相应的偏移量。在进行内存对齐时,编译器会自动插入填充字节,使得变量按照对齐方式排列。
因此,在压栈时,变量的地址通常是栈指针减去填充字节的数量,而内存对齐方式则是变量类型的大小和平台字长中的较小值。
其他内存对齐方式
除了结构体和压栈外,还有其他一些情况需要注意内存对齐。
类成员变量的内存对齐是指在类中定义的各个成员变量按照一定的规则在内存中进行排列的过程。类成员变量的内存对齐规则和结构体成员变量的内存对齐规则是类似的,不同的编译器可能有不同的实现。
在进行内存对齐时,编译器会根据变量类型的大小和平台字长的大小来决定对齐方式。通常情况下,成员变量的对齐方式是成员变量类型的大小和平台字长中的较小值。例如,在 x86 平台上,int 类型的变量需要按照 4 字节对齐,而 char 类型的变量只需要按照 1 字节对齐。
在类成员变量中,如果某个成员变量的对齐方式比其他成员变量的对齐方式要大,那么编译器会在这个成员变量后面自动填充一定数量的字节,以保证后面的成员变量可以按照对齐方式排列。填充字节通常是无效的字节,只是为了填充内存空间。
需要注意的是,不同的编译器可能会有不同的内存对齐规则,因此在进行内存对齐时需要注意。另外,在继承中,子类中的成员变量的内存对齐方式也受到父类成员变量对齐方式的影响。如果父类成员变量的对齐方式比子类成员变量的对齐方式大,那么编译器会在子类成员变量的前面自动填充一定数量的字节,以保证子类成员变量可以按照对齐方式排列。类成员在进行内存对齐的注意点
- 不同的编译器可能有不同的内存对齐规则,需要根据具体的编译器进行调整。
- 内存对齐会增加内存的使用量,因此需要根据实际情况进行权衡。
- 可以使用 #pragma pack 指令来指定某个成员变量的对齐方式,这个指令的实现和具体的编译器有关。
- 在进行内存对齐时,需要注意数据结构中的各个成员变量之间的相对位置,以避免数据读写错误。
- 如果需要对某个成员变量进行特殊的对齐方式,可以使用 attribute((aligned(n))) 属性来指定,其中 n 是对齐方式的字节数。需要注意的是,这个属性的实现和具体的编译器有关。
- 在类中定义的成员变量的顺序可能会影响内存对齐方式,因此需要根据实际情况进行调整。
- 如果某个成员变量的对齐方式比较大,那么在使用这个成员变量时需要注意是否需要进行类型转换,以避免数据读写错误。
- 在使用类成员变量时,需要注意成员变量的地址和内存对齐方式,以避免指针错误和数据读写错误。
指针的内存对齐是指在内存中存储指针变量时,按照一定的规则进行排列的过程。不同的编译器可能有不同的指针对齐规则,但通常情况下,指针变量的对齐方式与平台字长的大小有关。在 x86 平台上,指针变量需要按照 4 字节对齐,而在 x86-64 平台上,指针变量需要按照 8 字节对齐。
在进行指针的内存对齐时,编译器会根据指针类型和平台字长的大小来决定对齐方式。通常情况下,指针变量的对齐方式是指针类型的大小和平台字长中的较小值。例如,在 x86 平台上,int * 类型的指针变量需要按照 4 字节对齐,而 double * 类型的指针变量需要按照 8 字节对齐。
需要注意的是,指针变量的对齐方式可能会影响程序的运行效率。如果某个指针变量的对齐方式比较大,那么在使用这个指针变量时可能会增加额外的内存访问时间,从而降低程序的运行效率。因此,在编写代码时,需要根据实际情况来选择合适的数据类型和内存对齐方式,以保证程序的正确性和效率。
另外,指针变量的地址和内存对齐方式也需要注意。在使用指针变量时,需要保证指针变量的地址按照对齐方式排列,以避免指针错误和数据读写错误。如果需要使用特定的对齐方式,可以使用 attribute((aligned(n))) 属性来指定,其中 n 是对齐方式的字节数。需要注意的是,这个属性的实现和具体的编译器有关。动态内存分配是指在程序运行时,根据需要申请一定大小的内存空间,以供程序使用。动态内存分配通常使用 malloc、calloc 或 realloc 函数来实现。在进行动态内存分配时,需要注意内存对齐的问题。
和静态分配和栈分配一样,动态分配的内存也需要进行内存对齐。在进行内存对齐时,编译器会根据变量类型的大小和平台字长的大小来决定对齐方式。通常情况下,动态分配的内存块的对齐方式是内存块中最大数据类型的大小和平台字长中的较小值。
在进行动态内存分配时,需要注意以下几点:
不同的编译器可能有不同的内存对齐规则,需要根据具体的编译器进行调整。
内存对齐会增加内存的使用量,因此需要根据实际情况进行权衡。
可以使用 posix_memalign 函数来申请特定对齐方式的内存块,具体的实现和平台有关。
在进行内存对齐时,需要注意数据结构中的各个成员变量之间的相对位置,以避免数据读写错误。
如果需要对某个数据类型进行特殊的对齐方式,可以使用 attribute((aligned(n))) 属性来指定,其中 n 是对齐方式的字节数。需要注意的是,这个属性的实现和具体的编译器有关。
在使用动态分配的内存块时,需要注意成员变量的地址和内存对齐方式,以避免指针错误和数据读写错误。
总之,在进行动态内存分配时,需要根据具体的编译器和实际情况进行权衡,以保证程序的正确性和效率。
内存对齐的优缺点
内存对齐是为了提高内存访问的效率,主要有以下优点:
- 提高内存访问速度:内存对齐可以使CPU在访问内存时更快地读取数据,提高程序的运行效率。
- 减少内存碎片:内存对齐可以减少内存碎片的产生,从而提高内存的利用率。
- 保证数据正确性:内存对齐可以确保数据存储在正确的地址上,避免数据被覆盖或损坏。
内存对齐的缺点主要是在内存空间的利用上会产生一定的浪费,会使得数据结构的大小变大,从而增加内存的使用量。
如何进行内存对齐
许多编译器提供了特殊的指令和选项,可以用来控制内存对齐。例如,GCC编译器提供了__attribute__ ((aligned (n)))指令,可以指定变量或结构体的对齐方式。
许多库和工具可以用来进行内存对齐。例如,C++11标准库中的alignas和alignof模板可以用来指定变量或结构体的对齐方式。另外,一些第三方库和工具,如Intel的IPP和Microsoft的SSE2,也可以用来进行内存对齐。
内存对齐的注意事项
在进行内存对齐时,需要注意以下事项:
- 不同编译器和操作系统的内存对齐规则可能不同。因此,要确保在不同的平台上都能正确地进行内存对齐。
- 取消内存对齐可能会导致程序的安全性和可移植性下降。因此,应该避免取消内存对齐。
- 应根据实际情况选择合适的内存对齐方式。例如,对于一些需要高效访问的数据结构,可以选择更严格的内存对齐方式。而对于一些不需要高效访问的数据结构,可以选择更宽松的内存对齐方式。
- 在进行内存对齐时,应该注意变量的大小和类型。不同的变量类型可能具有不同的内存对齐方式。
- 当使用位域时,需要注意位域的类型和位域的顺序。位域可能会影响内存对齐方式。
实例分析
假设有以下结构体:
struct Test {char a;int b;short c; };
该结构体中包含一个char类型的变量、一个int类型的变量和一个short类型的变量。如果不进行内存对齐,该结构体大小为7字节(1 + 4 + 2)。但是,由于不同类型的变量有不同的内存对齐方式,因此编译器会在结构体中自动填充字节,使得结构体中的成员变量按照对齐方式排列。在x86平台上,char类型的变量对齐方式为1字节,int类型的变量对齐方式为4字节,short类型的变量对齐方式为2字节。因此,该结构体的内存对齐方式为4字节,结构体大小为12字节(1 + 3填充 + 4 + 2 + 2填充)。
假设有以下代码:
void foo(int a, char b, short c) {// do something }
该函数包含一个int类型的参数、一个char类型的参数和一个short类型的参数。在调用该函数时,这些参数将按照顺序依次压入栈中。在x86平台上,int类型的参数需要4字节对齐,char类型的参数需要1字节对齐,short类型的参数需要2字节对齐。因此,编译器会在压栈时自动进行内存对齐,使得参数按照对齐方式排列。在这个例子中,参数的内存对齐方式为4字节,因此在压栈时需要在char类型的参数后面填充3字节的空间,使得short类型的参数可以正确对齐。
假设有以下代码:
int main() {int *p = (int *)malloc(8 * sizeof(int));int *q = (int *)aligned_alloc(16, 8 * sizeof(int));return 0; }
该代码中使用了malloc函数和aligned_alloc函数分配内存。在使用malloc函数时,分配的内存可能不是按照特定的对齐方式进行对齐的。因此,使用malloc函数分配的内存需要进行手动的内存对齐。在这个例子中,可以使用aligned_alloc函数分配16字节对齐的内存,以保证内存对齐的正确性。
内存对齐的实现原理
在硬件层面,内存对齐是通过 CPU 的访问机制来实现的。CPU 通常会将内存划分为若干个字节的单元,每个单元都有一个地址。在进行内存读写操作时,CPU 需要将数据从内存中读取到寄存器中或者将数据从寄存器中写入到内存中,这个过程需要消耗一定的时间。如果数据不是按照对齐方式存储的,就需要额外的时间来进行调整,从而降低程序的运行效率。
为了提高访问速度,CPU 通常会支持按照一定的对齐方式进行访问。在 x86 平台上,CPU 支持按照 1 字节、2 字节、4 字节和 8 字节对齐方式进行访问。如果数据按照对齐方式存储,CPU 可以直接从内存中读取数据,从而提高程序的运行效率。如果数据不是按照对齐方式存储,CPU 就需要额外的时间来进行调整,从而降低程序的运行效率。在软件层面,内存对齐是通过编译器来实现的。编译器会根据变量类型的大小和平台字长的大小来决定对齐方式。通常情况下,变量的对齐方式是变量类型的大小和平台字长中的较小值。
在进行内存对齐时,编译器会自动插入填充字节,使得变量按照对齐方式排列。填充字节通常是无效的字节,只是为了填充内存空间。如果某个变量的对齐方式比其他变量的对齐方式要大,编译器就会在这个变量后面自动填充一定数量的字节,以保证后面的变量可以按照对齐方式排列。需要注意的是,不同的编译器可能会有不同的内存对齐规则,因此在进行内存对齐时需要注意。另外,在使用结构体和类成员变量时,需要注意成员变量的地址和内存对齐方式,以避免指针错误和数据读写错误。
总之,内存对齐的实现原理是通过硬件和软件两个层面来实现的。在硬件层面,CPU 支持按照一定的对齐方式进行访问,以提高程序的运行效率。在软件层面,编译器会根据变量类型的大小和平台字长的大小来决定对齐方式,并自动插入填充字节,使得变量按照对齐方式排列。
总结取消和控制内存对齐的方式
- 结构体内存对齐:可以使用 gcc 编译器的 attribute((packed)) 属性来取消结构体成员变量的内存对齐,或者使用 attribute((aligned(n))) 属性来指定特定的对齐方式。
- 压栈内存对齐:可以使用 gcc 编译器的 -mpreferred-stack-boundary=n 选项来指定栈内存的对齐方式,其中 n 表示对齐方式的字节数。
- 类成员变量的内存对齐:可以使用 gcc 编译器的 attribute((aligned(n))) 属性来指定特定的对齐方式。
- 指针的内存对齐:可以使用 gcc 编译器的 attribute((aligned(n))) 属性来指定指针的对齐方式,或者使用 posix_memalign 函数来申请特定对齐方式的内存块。
- 动态内存分配的内存对齐:可以使用 posix_memalign 函数来申请特定对齐方式的内存块,或者使用 malloc、calloc 或 realloc 函数来申请内存块,并使用 attribute((aligned(n))) 属性来指定内存块的对齐方式。
需要注意的是,取消或控制内存对齐可能会影响程序的运行效率和正确性,因此需要根据具体情况进行权衡。
结语
内存对齐是编程中非常重要的一部分,可以提高程序的运行效率和内存利用率。
在进行内存对齐时,需要注意不同的数据类型和平台的内存对齐规则,避免取消内存对齐,选择合适的内存对齐方式,以保证程序的正确性和可移植性。
相关文章:
内存对齐:C/C++编程中的重要性和技巧
C/C中的内存对齐前言基本概念 什么是内存对齐?内存对齐的定义内存对齐的作用数据类型的大小ARM 64 位架构和 x86_64 架构下的数据类型大小ARM 32 位架构下的数据类型大小内存对齐的边界填充字节的作用内存对齐的原理结构体中的内存对齐结构体的定义和使用结构体中成…...
C++ Primer第五版_第七章习题答案(41~50)
文章目录练习7.411、头文件2、源文件3、主函数练习7.42练习7.43练习7.44练习7.45练习7.46练习7.47练习7.48练习7.49练习7.50练习7.41 使用委托构造函数重新编写你的Sales_data 类,给每个构造函数体添加一条语句,令其一旦执行就打印一条信息。用各种可能的…...
python玄阶斗技--NumPy入门
目录 一.NumPy介绍 二.创建数组 1.一维数组创建 2.二维数组创建 3.zeros函数 4.ones函数 5.empty函数 6.arange函数 三.NumPy的数学操作 1.基本运算 2.矩阵运算 3.ndarray类的方法 四.数组堆叠 五.数组分隔 一.NumPy介绍 在这里对NumPy的介绍我不想扯太多…...

VR黑科技丨远离拥挤,VR直播开启沉浸式赏樱新姿势
春光兮婉转,珞樱兮盛绽,又是一年樱花季,全国各地大部分地区的樱花进入盛花期,尤其是武汉,东湖樱园踏青赏花的游人如织、摩肩擦踵,勾勒一幅“人人人人人人人花人人人人人”的盛景。 为了一睹樱花“芳容”&am…...
ts的一些用法
1.交叉类型 & ---多个类型属性的集合 1.1类型别名实现 type Person {name:string} type Children Person & {age:number} let newPerson:Children {// name:hahah,name:hhaah,age:18 } 1.2 接口类型实现 interface Inter1{name:string } interface Inter2{name:…...
云计算面试总结
shell脚本对日志进行备份 shell 对日志备份 #!/bin/bash if [ -d /log/bak/ ] || mkdir -p /log/bak/ thentar Pcf /log/bak/log_$(date %Y%m%d)$(date %H%M%S).tar.gz /var/log/*.logecho "干完!可以约会啦" fi存在的问题: 说的太快&a…...
(DP)买不到的数目【蓝桥杯】(裴蜀定理)
买不到的数目 小明开了一家糖果店。 他别出心裁:把水果糖包成4颗一包和7颗一包的两种。 糖果不能拆包卖。 小朋友来买糖的时候,他就用这两种包装来组合。 当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。 你可以用计算机测试一下&#…...
Docker使用DockerFile部署Go项目
Docker使用DockerFile部署Go项目1. 文章说明2. Go项目打包到Linux2.1 学习链接与知识点2.2. 打包生成 main 文件2.3 Docker部署Go项目1. 文章说明 目的:将打包生成的 main 文件,在Docker里面,使用Dockerfile文件,生成镜像与容器&…...
C++ Primer第五版_第七章习题答案(31~40)
文章目录练习7.31练习7.32练习7.33练习7.34练习7.35练习7.36练习7.37练习7.38练习7.29练习7.40练习7.31 定义一对类X 和Y,其中X 包含一个指向 Y 的指针,而Y 包含一个类型为 X 的对象。 class Y;class X{Y* y nullptr; };class Y{X x; };练习7.32 定义你…...

基于springboot实现学生成绩管理系统【源码+论文】分享
基于springboot实现学生成绩管理系统演示开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包&…...
Linux diff 命令
Linux diff 命令用于比较文件的差异。 diff 以逐行的方式,比较文本文件的异同处。如果指定要比较目录,则 diff 会比较目录中相同文件名的文件,但不会比较其中子目录。 语法 diff [-abBcdefHilnNpPqrstTuvwy][-<行数>][-C <行数&g…...
unity动画状态机
介绍: 动画状态机(Animation State Machine)是Unity中用于控制动画状态转换的工具,它由多个状态(State)和转换(Transition)组成,可以通过状态转换来控制动画的播放行为。…...

溯源(五)之攻击源的获取
溯源(一)之溯源的概念与意义 溯源(二)之 windows-还原攻击路径 溯源(三)之Linux-入侵排查 溯源(四)之流量分析-Wireshark使用 溯源整体流程的思维导图 攻击源的获取 1、获取哪些数…...
【redis】redis淘汰策略
一、说明 1.redis key没有设置过期时间被redis主动删除了 2.当redis已用内存超过maxmemory限定时,触发主动清理策略 3.主动清理策略在redis4.0之前一共实现了6种内存淘汰策略,在4.0之后,增加了2种,总共8种 二、淘汰策略 2.1 针对…...
指针和数组(二)
目录 指针和数组 数组名和指针的区别 多维数组 数组指针 语法 作用 内存大小 自增运算 【】运算 指针和数组 结论:数组的本质就是指针。数组的【】运算同样可以用指针来运算 证明 C代码 int array[5];int* ptr{ &array[0] };*ptr 5;array[0] 5;arr…...

Linux WIFI 驱动实验
目录WIFI 驱动添加与编译向Linux 内核添加WIFI 驱动配置Linux 内核编译WIFI 驱动驱动加载测试wireless tools 工具移植与测试wireless tools 移植wireless tools 工具测试wpa_supplicant 移植openssl 移植libnl 库移植wpa_supplicant 移植WIFI 联网测试RTL8188 USB WIFI 联网测…...
UART驱动情景分析-write
一、write过程分析 App写: 使用行规程来写数据最终存入uart_state->xmit的buffer里 硬件发送: 使用硬件驱动中uart_ops->start_tx开始发送具体的发送方式有两种:通过DMA、通过中断 中断方式: 方法1:直接使能tx …...
Metasploit入门到高级【第四章】
来自公粽号:Kali与编程预计更新第一章:Metasploit 简介 Metasploit 是什么Metasploit 的历史和发展Metasploit 的组成部分 第二章:Kali Linux 入门 Kali Linux 简介Kali Linux 安装和配置常用命令和工具介绍 第三章:Metasploi…...

java 继承super
在java继承中,如果子类继承父类,在子类中要给用构造器给父类的属性赋值,需要用到 super 举例,Son类继承Father 类,便于理解 在 new Son(String name, int age) 传入name,和age的值 将会调用Son这个构造器…...

Java学习笔记——多态
2.1 概述 引入 多态是继封装、继承之后,面向对象的第三大特征。 生活中,比如跑的动作,小猫,小狗和大象,跑起来都是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来是不一样的。可见&#x…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

基于Java+MySQL实现(GUI)客户管理系统
客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息,对客户进行统一管理,可以把所有客户信息录入系统,进行维护和统计功能。可通过文件的方式保存相关录入数据,对…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...