程序的机器级表示

程序的机器级表示

有关CSAPP第三章一些我关注到的重点的记录

操作指令

.c->.exe的流程

image-20231127125503391

1.选项 -E : 预编译过程,处理宏定义和include,并作语法检查

gcc -E hello.c -o hello.i              #将hello.c预处理输出为hello.i文件

2.选项 -S : 编译过程,生成通用的汇编代码

gcc -S hello.c                         #生成汇编代码hello.s

生成的汇编文件以“.”开头的行都是指导汇编器和链接器工作的伪指令

3.选项 -c : 汇编过程,生成ELF格式的可重定位目标文件,目标文件(机器代码),用文本编辑器打开是乱码

gcc -c hello.c                         #生成目标代码hello.o(中间文件),不能执行,在Makefile中应用广泛

4.选项 -L : 链接过程,将.o文件与所需库文件链接合并成ELF格式的可执行目标文件,分静态链接和动态链接

gcc hello.o -L dir(如./lib)            #指定库搜索路径,有多个则从前往后搜索

5.选项 -l : 链接过程,指定链接库,库命名规则是libxxx.a,指定库名时使用的格式是-lxxx

gcc hello.c -o hello -lm              #链接数学库
ld -o hello hello.o -lxxx             #链接xxx库

6.选项 -o : 将源文件预处理、编译、汇编并链接形成可执行目标文件,-o选项指定可执行文件的文件名,加载到内存中即可执行

gcc hello.c -o hello                  #生成可执行文件hello

7.部分选项 :
选项 -Wall : 编译时打开警告信息开关
选项 -D : 在文件中定义宏INFO,编译时加上-D INFO使其生效
选项 -O : 后指定数字,使用编译优化级别1~3优化程序
选项 -g : 产生调试信息

8.选项 -static : 使用静态链接库,将使用的静态库对象嵌入至可执行映像文件中,加载时无需进一步的链接

gcc -c -Wall x1.c x2.c       #生成目标文件
ar -cru libxxx.a x1.o x2.o   #创建静态库
#定义静态库的应用接口xxx.h,里面显式引用上面的源文件函数和对象
gcc -O2 -c main.c            #测试用例调用静态库的函数
gcc -static -o p main.o ./libxxx.a  #链接静态库和目标文件生成可执行文件p

9.选项 -share : 使用共享库,在运行时动态加载目标程序所需要的信息
选项 -fPIC : 指示编译器生成与地址无关的目标文件(position-independent code)

gcc -shared -fPIC -o libxxx.so x1.c x2.c  #生成共享库libvector.so
gcc -o p1 main.c ./libvector.so           #共享库中的目标对象并未嵌入可执行文件中,执行时完成链接过程

.c->.exe

linux> gcc -Og -o p -g p.c
  • -Og优化等级比较符合原始C代码整体结构,方便学习(为了更高的性能可以使用-O1或-O2甚至更高的编译优化选项)
  • -o转化成可执行文件
  • -g生成调试信息
  • p为转化成可执行文件的文件名
  • p.c为源文件名

.c->.s 编译生成汇编文件

linux> gcc -Og -S p.c

.c->.o 汇编生成目标文件

linux> gcc -Og -c p.c

.o/.exe->.s 反汇编

linux> objdump -d p.o

C语言嵌套汇编语言

C编译器在把程序中表达的计算转换到机器代码中表现很出色,但仍然有一些及其特性是C语言访问不到的。例如x86-64处理器执行算术或逻辑运算时,修改奇偶标志位寄存器PF的值时,用汇编语言的效率远高于C语言,故如果能在C语言中嵌套C语言,会提供大大的方便。

第一种方法:源代码中插入汇编代码

#include <stdio.h>
#include <stdlib.h>int main(void)
{/* basic command demo */__asm__("movl %eax, %ecx");/* set b = 10 */int a = 10, b = 0;__asm__("movl %1, %%eax;""movl %%eax, %0;":"=r" (b)	/* output */:"r" (a)	/* input */:"%eax"	/* clobbered register */);printf("%s: b = %d\n", __func__, b);return 0;
}

第二种方法:写好汇编文件和C文件,用汇编器和链接器把它们合并起来

保存寄存器

假设现在有两个函数funcA和funcB,函数A称为调用者,函数B称为被调用者,由于调用了函数B,寄存器rbx在函数B中被修改了,而逻辑上rbx寄存器的内容在调用函数B的前后应该保持一致,解决这个问题有两个策略,调用者保存和被调用者保存。

func_A:...movq $123, %rbxcall func_Badd %rbx, %rax...ret
func_B:...addq $456, %rbx...rer

调用者保存

func_A:...movq $123, %rbx保存rbxcall func_B恢复rbxadd %rbx, %rax...ret
func_B:...addq $456, %rbx...rer

被调用者保存

func_A:...movq $123, %rbxcall func_Badd %rbx, %rax...ret
func_B:...保存rbxaddq $456, %rbx恢复rbx...rer

具体使用哪种策略取决于寄存器被定义为那种类型,下图是寄存器类型

image-20231119162344823

c语言基本类型对应汇编后缀表示

image-20231119162527234

访问信息

各存储部件的性价比

image-20231119162939434

通用寄存器
寄存器用途
%eax操作数运算
%ebx指向DS段中数据的指针
%ecx字符串操作和循环计数器
%edx输入输出指针
%esi指向DS段中数据的指针或字符串操作中字符串的复制源
%edi指向ES段中数据的指针或字符串操作中字符串的复制地
%esp栈指针(SS段)
%ebp指向SS段上数据的指针
段寄存器
寄存器用途
CS代码段
DS数据段
SS堆栈段
ES数据段
FS数据段
GS数据段

C类型长度

C声明Intel数据类型汇编代码后缀大小(字节)
char字节b1
shortw2
int双字l4
long四字q8
char*四字q8
float单精度s4
double双精度l8

指令

指令包含操作码和操作数。

	操作码		操作数movq	 (%rdi), %raxaddq	 $8,	%rsxsubq	 %rdi,	%raxxorq	 %rsi,	%rdiret

操作码决定CPU执行操作的类型

指令可以有一个、多个或没有操作数

操作数分为3类,分别为立即数、寄存器以及内存引用

数据寻址模式

image-20231127125528494

这里的比例因子s会根据数据类型取

数据传送命令

image-20231127125732051

以上命令中没有movzlq,是因为一个结论:当复制和生成字节以寄存器为目标时,对于生成4字节的指令,会把高位4个字节置为0,所以用movl就能代替命令movzlq,例如

movl %eax,%edx

实际上除了将低32位数据由eax传递给rdx的低32位之外,还把高32位设置为0

这里注意到练习题3.3的一题,找以下代码的错误

movl %eax,%rdx

在这里错误是源操作数和目标操作数类型不匹配,虽然eax传值后会扩展为64位,但在写代码时依然需要保持操作数类型的统一

movq指令的限制:

当movq指令的源操作数是立即数时,只能是32位的立即数,此时会对该立即数进行符号扩展到64位,再将得到的64位立即数传送到目的位置。

那么当源操作数是64位立即数时就引入了一个新的指令movabsq,此时就能将64位立即数作为源操作数,但目的操作数只能是寄存器

cltq指令

cltq = movslq %eax,%rax

算术和逻辑操作

操作指令

image-20231127125536013

具体操作如下图,之所以z被分为两步操作,是因为比例因子只能取1、2、4、8这四个数中的一个

image-20231120193534676

移位操作

移位量可以是一个立即数,或者放在单字节寄存器%cl中。

移位操作对w位长的数据值进行操作,移位量是由**%cl寄存器**的低m位决定的,这里2m=w,高位被忽略。所以,例如寄存器%cl的十六进制值为0xFF时,指令salb会移7位,salw会移15位,sall会移31位,而salq会移63位。

SAR算术右移,高位补符号位;SHR逻辑右移,高位补0;

以下操作使用移位操作而不使用乘法操作的原因是因为乘法指令执行需要更长时间,因此编译器在生成汇编指令时,会优先考虑更高效的方式。

image-20231120194527867

特殊的算术操作

image-20231127125544066

控制

条件码

CPU除了提供上面的几个整数寄存器外,还维护着一组单个比特位的条件码,描述最近的算术或逻辑操作特性,用于执行条件分支指令。

  • CF: 进位标志,表示最近的操作使最高位产生了进位。用于检查无符号操作数的溢出,如下图image-20231120195141469

  • ZF: 零标志,表示最近的操作得出的结果为0,如下图

    image-20231120195223613

  • SF: 符号标志,表示最近的操作得出的结果为负数

  • OF: 溢出标志,表示最近的操作使补码溢出-正溢出或负溢出

条件码寄存器的值是由ALU执行算术逻辑运算指令改变的

有几种设置条件码的情形

image-20231120195517929

INC(加一)和DEC(减一)指令会设置OF(溢出)标志和ZF(零)标志,但不会改变CF(进位)标志。

因为指令系统设计人员考虑该指令主要用于对指针(即地址)进行增加,不存在进位问题,所以没有设计让INC影响进位标志CF。
INC,DEC指令不影响CF标志位,这个是Intel规定的!其原因是硬件设计造成的,总之,对软件人员来制说不重要!
INC,DEC指令不影响CF标志位,这表明执行INC/DEC指令之后,CF不能反映进位情况。

INC 0000000011111111

0000000011111111+1当然要进位,但不设置CF为1。
我们的问题就在于,将进位与CF等同
CF被称为进位标志位,在多数情况下,它确实反映进位情况,但不是绝对的,INC/DEC就是其中两例
INC/DEC指令不影响CF标志位,这句话就是明明白白地告诉你,此时,CF与进位无关

A. 比较和测试指令:它们只设置条件码而不改变任何其他寄存器

cmp S2,S1 通过S1-S2的结果,比较两者的大小
test S2,S1 通过S1&S2的结果(按位与),比如testl %eax,%eax用来检查%eax是正数,负数还是0或者其中一个操作数是掩码,用来指示哪些位应该被测试

B. 根据条件码的组合,使用set指令,不同后缀名表示不同条件

set指令的目的操作数是8个单字节寄存器或者存储一个字节的存储器位置,把该字节位置设置成0或1。它的基本思路是执行比较或测试指令,根据set指令的类型决定计算结果t=a-b:操作数的大小,是有符号的还是无符号的,程序值的数据类型。如图所示为set指令的常见情形

image-20231127125554540

跳转指令

image-20231127125601201

关于跳转指令如何编码

image-20231127125810240

可以看到第2行中跳转指令目标指明位0x8,第5行中跳转指令跳转目标是0x5,这里有一个规则,在指令的字节编码中,我们可以看到第二个字节中编码位0x3,再将其加上下一条指令的地址,即0x5,就可以得到跳转目标地址0x8,同样第5行0xf8(即十进制-8),这个数加上0xd,即为地址0x5

条件分支

用条件控制来实现条件分支
实际上,C语言中有一种语句叫做goto,一般不推荐使用,但是它的控制和汇编代码的条件转移十分相似。

例如我们有这样一段正常的代码,实际上就是得到两数之差的绝对值:

long absdiff(long x, long y)
{
long result;
if (x > y)
result = x-y;
else
result = y-x;
return result;
}

然后我们使用goto语句改写一下:

long absdiff_j(long x, long y)
{
long result;
int ntest = x <= y;
if (ntest) goto Else;
result = x-y;
goto Done;
Else:
result = y-x;
Done:
return result;
}

从控制流的角度来看,这两个代码基本上是一样的。

用条件传送来实现条件分支
条件传送,和set指令有些相似,也就是根据条件码部分来判断是否要进行数据传送,使用的是cmov(conditional move),比如当相等的时候进行条件传送,也就是cmove。

现代处理器会使用一种特殊的技术,叫做流水线(pipeline),它的名字就是取自工厂流水线,在CPU中,也就是说当你执行一条指令的时候,下一条指令的一部分会被执行,下下一条指令的一部分也会被执行,这样就提高了并行的程序。

但是条件转移会破坏流水线的运作,于是我们会把两个条件的结果都计算一遍,然后再根据跳转选择其中的一条。这里也就用到了cmov。

比如还是之前的程序,我们汇编变成如下这个样子,也就是把x-y和y-x都计算了,然后再根据条件,选择其中一个结果返回:

absdiff:
movq %rdi, %rax # x
subq %rsi, %rax # result = x-y
movq %rsi, %rdx
subq %rdi, %rdx # eval = y-x
cmpq %rsi, %rdi # x:y
cmovle %rdx, %rax # if <=, result = eval
ret
但是,使用cmov也会有一些负面影响:

只有当计算较为简单时,才用cmov进行优化,如果两条分支都较为复杂,那么使用cmov反而不好
对于某个分支而言,计算它可能没有什么用,只是浪费时间。
两个分支可能会存在关联性,比如val = x > 0 ? x*=7 : x+=3;,如果两个都进行计算就会出现错误。

指令同义名传送条件描述
cmove S,RcmovzZF相等/零
cmovne S,Rcmovnz~ZF不相等/非零
cmovs S,RSF负数
cmovns S,R~SF非负数
cmovg S,Rcmovnle~(SF^OF) & ~ZF大于(有符号>)
cmovge S,Rcmovnl~(SF^OF)大于或等于(有符号>=)
cmovl S,RcmovngeSF^OF小于(有符号<)
cmovle S,Rcmovng(SF^OF) | ZF小于或等于(有符号<=)
cmova S,Rcmovnbe~CF & ~ZF超过(无符号>)
cmovae S,Rcmovnb~CF超过或相等(无符号>=)
cmovb S,RcmovnaeCF低于(无符号<)
cmovbe S,RcmovnaCF | ZF低于或相等(无符号<=)

练习题3.20

在这里发现一个规则,当负数做被除数时,需要将该数先加上2k-1,k为要右移的位数。这是为了保证,正数向下舍入,负数向上舍入

image-20231127125626439

循环

一、do-while

如果用C的goto来实现,则如下面的代码:

loop:Bodyif (Test)goto loop

实际上就是先循环体,然后进行测试,如果测试成功,那么跳回到loop再继续循环。

举个例子,比如我们有这样一个C程序的goto版本:

long pcount_goto (unsigned long x) {long result = 0;loop:result += x & 0x1;x >>= 1;if(x) goto loop;return result;
}

那么会发现,汇编的版本也类似:

movl    $0, %eax		#  result = 0
.L2:			# loop:movq    %rdi, %rdx	andl    $1, %edx		#  t = x & 0x1addq    %rdx, %rax	#  result += tshrq    %rdi		#  x >>= 1jne     .L2		#  if (x) goto looprep; ret

二、while

while和do-while的区别就在于do-while第一次不进行测试,所以总会执行一遍循环体,而while在开始就测试,如果不满足就跳出,不执行。

while的实现有2种方式,第一种方式就是先跳到了do-while的中间,然后进行测试。

C的goto版本如下:

goto test;
loop:Body
test:if (Test)goto loop;
done:

我们还是用pcount这个程序,那么while就是下面这种实现:

long pcount_goto_jtm(unsigned long x) {long result = 0;goto test;loop:result += x & 0x1;x >>= 1;test:if(x) goto loop;return result;
}

第二种实现方式比较传统,就是一开始进行判断,如果不满足直接goto跳出,满足那么进入到和do-while相同的语句块中。

 if (!Test)goto done;
loop:Bodyif (Test)goto loop;
done:

pcount的第二种while实现如下:

long pcount_goto_dw(unsigned long x) {long result = 0;if (!x) goto done;loop:result += x & 0x1;x >>= 1;if(x) goto loop;done:return result;
}

三、for

for循环实际上包含了4个部分,例如一个C语言的for循环for(int i = 0;i < 5;i++){body},包括了初始化(int i = 0),测试(i < 5),更新(i++)和循环体。

如果用while循环来表示for循环,那么就是先进行初始化,然后是while循环,在while循环体的最后加上更新操作。

Init;
while (Test ) {BodyUpdate;
}

还是之前的例子,我们使用for循环(用while实现for)实现:

long pcount_for_while(unsigned long x)
{size_t i;long result = 0;i = 0;while (i < WSIZE){unsigned bit = (x >> i) & 0x1;result += bit;i++;}return result;
}

然后我们用goto替代:

long pcount_for_goto_dw(unsigned long x) {size_t i;long result = 0;i = 0;if (!(i < WSIZE))goto done;loop:{unsigned bit = (x >> i) & 0x1;result += bit;}i++;if (i < WSIZE)goto loop;done:return result;
}

如果使用了-O1优化级别,那么第一次的判断很有可能不需要了,编译器会将其舍弃

这里需要提示一下,再跳转指令后若跟随ret会出现一些判断问题,所以我们需要在中间加一个rep;这个什么都不会做,所以也不需要管

过程

栈帧

当函数执行所需要的存储空间超出寄存器能够存放的大小时,就会借助栈上的存储空间,这部分存储空间称为函数的栈帧

image-20231120202419343

如果一个函数的参数数量大于6,超出部分就要使用栈来传递。

image-20231120202519873

两点注意

1.通过栈传递参数时,所有数据大小向8对齐

image-20231120202751702

2.使用寄存器进行参数传递时,寄存器的使用是由特殊顺序规定的

image-20231120202835825

局部变量在栈帧存储不需要对齐,参数才需要对齐

image-20231120203257254

数组

不同类型指针加1,得到结果不同

image-20231120203818045

数组元素的计算

image-20231120204106003

xd表示数组的起始地址,L表示数组类型T的大小,如果T时int类型,L就等于4,T是char类型,L就等于1

例如下图

image-20231120204236228

使用以下汇编代码,将A[i][j]的值复制到寄存器eax中,如下图所示

image-20231120204338191

结构体

结构体在内存中的存储遵循内存对齐,如下图,由于变量j是int类型,占4个字节,它的起始地址必须是4的倍数,所以,在变量c和变量j之间插入了一个3字节的间隙,结构体大小也就变成了12个字节。

image-20231120205126457

如果我们变更顺序,如下图,此时能满足结构体的对齐要求,但无法满足结构体数组的对齐要求,所以如果定义结构体数组,需要在末端加入3个字节的间隔。

image-20231120205242216

**复杂示例:**每个元素的偏移地址都必须是它数据大小的倍数,且为满足每个元素都对齐,最后要在结构体末端填充间隙(根据结构体最大类型的长度),如下图所示。

image-20231120205456633

联合体

联合体中所有字段共享同一存储区域,因此联合体的大小取决于它最大字段的大小,如下图,变量v和数组i的大小都是8个字节,因此该联合体占8个字节的存储空间,两个不同字段的使用是互斥的,那么我们就可以将这两个字段声明为一个联合体。

image-20231120205849564

示例:一个二叉树,包含叶子节点(只包含两个double数据)和内部节点(只包含左右节点指针),其定义如下图,使用结构体定义需要占用32个字节,而使用联合体只用占用16个字节。

image-20231120210147693

但此时有一个问题,就是无法确定节点是哪种节点,解决办法是引入一个枚举类型,如下图所示,type占4个字节(枚举占4个字节),加上最后末尾间隔的4个字节,最终这个结构体占24个字节

image-20231120210512788

**类型转换:**一种类型来存储,另一种类型来访问

image-20231120210926653

栈溢出攻击

解决通过栈溢出攻击系统的三种办法

1.栈随机化

栈的位置在程序每次运行时都发生变化,在Linux系统中,栈随机化已经成为了一种标准行为(ASLR)

2.栈破坏检测

编译器会在产生的汇编代码中加入一种栈保护者的机制来检测缓冲区越界,就是在缓冲区与栈保存的状态值之间存储一个特殊值(金丝雀值,canary),函数返回之前检测金丝雀值是否被修改来判断是否遭受攻击

image-20231121005508305

3.限制可执行代码区域

这三种机制都不需要程序员做额外的操作,都是通过编译器和操作系统实现的,单独每一种机制都能降低用户的等级,组合起来使用会更有效,不幸的是,仍然有方法能对计算机进行攻击。

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

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

相关文章

条形码格式

条形码格式 简述EAN码EAN-13EAN-8 UPC码UPC-AUPC-E 简述 EAN码 EAN码&#xff08;European Article Number&#xff09;是国际物品编码协会制定的一种全球通用的商用条码。EAN码分为&#xff1a;标准版&#xff08;EAN-13&#xff09; 和 缩短版&#xff08;EAN-8&#xff09…

“yum history”命令示例,用于显示、回滚、重做、撤消 yum 事务

yum 的“yum history”功能是 Linux 中一个经常被忽视但非常强大的实用程序。它可用于将 yum 事务回滚/重做/撤消到一切正常的状态。 yum history “yum history list”命令在不带任何参数的情况下运行时会产生类似于下图所示的输出。“yum history”或者“yum history list”…

不会提问不打紧,不敢提问才要命

最近在星球里回答了球友提出来的一些问题&#xff0c;我都给了回复&#xff0c;不经过在明确问题、探索问题的过程&#xff0c;对我启发挺大&#xff0c;特此来记录下感受和感悟。 缘起 最近新加入球友提的问题&#xff0c;有几次&#xff0c;我第一时间没看懂&#xff0c;甚…

基于自然语言处理的地铁工程车辆故障智能诊断研究

源自&#xff1a;《兵器装备工程学报》 作者&#xff1a;严硕&#xff0c; 徐永能&#xff0c; 何文韬 “人工智能技术与咨询” 发布 摘要 针对地铁工程车辆故障文本数据未得到合理利用的现象&#xff0c;提出了一种基于自然语言处理的故障智能诊断方法。该方法对故障文本进…

Servlet自定义异常处理类

话不多说&#xff0c;直接上代码。。。 异常处理类GlobalExceptionHandler public class GlobalExceptionHandler {ExceptionHandler(UnknownServiceException.class)public static void UnknownServiceExceptionHandle(Exception ex, HttpServletRequest request, HttpServ…

交流回馈负载的主要工作方式

交流回馈负载是一种电力电子装置&#xff0c;其主要工作方式是将电能从交流电源转换为直流电&#xff0c;然后再将直流电转换为交流电。这种转换过程是可逆的&#xff0c;因此可以用于调节电网的电压和频率&#xff0c;提高能源利用效率&#xff0c;减少能源浪费。 交流回馈负载…

Java(八)(可变参数,Collections,小案例:斗地主游戏小案例:斗地主游戏,Map集合,Stream流)

目录 可变参数 Collections 小案例:斗地主游戏 Map集合 Map的常用方法 map集合的遍历 键找值 键值对 Lambda 表达式 HashMap底层原理 集合的嵌套 Stream流 获取集合或数组的Stream流 Stream流的方法 可变参数 就是一种特殊的形参,定义在方法和构造器的形参列表中,…

小吉和希亦内衣洗衣机选哪个好?小型洗衣机测评对比

在生活质量不断提高的今天&#xff0c;人们对健康、卫生的要求也日益提高。内衣是女性的贴身衣物&#xff0c;它的清洁和卫生是非常重要的。尤其是小孩的衣服&#xff0c;毛巾&#xff0c;袜子等要分开洗&#xff0c;这样就不会和大人的衣服一起洗了&#xff0c;更加的卫生和方…

专业课问题 | 667和972问题集锦

专业课备考问题集锦 表1是我最近收集的667相关问题&#xff1a; 表1 667相关备考问题与简要回答 类型序号问题回答关于背书1整本书已经背了N轮了,后期只可以背诵知识清单吗?背书的总体思路:看书(1-2遍)——>知识清单(N轮循环,掌握90%的知识点)——>看书(1-2遍)2后面五个…

java-netty知识点笔记和注意事项

如何获取ctx的id 使用ctx.ctx.toString()就可以了 public void channelRead(ChannelHandlerContext ctx, Object msg) {//传来的消息包装成字节缓冲区String byteBuf (String) msg; // ByteBuf byteBuf (ByteBuf) msg;//Netty提供了字节缓冲区的toString方法&#xff…

Android 虚拟机与类加载机制

1、Dalvik 虚拟机 Android 应用程序运行在 Dalvik/Art 虚拟机上&#xff0c;并且每一个应用程序都有一个单独的 Dalvik/Art 虚拟机实例。 1.1 JVM 与 Dalvik Dalvik 虚拟机也算是一个 Java 虚拟机&#xff0c;它是按照 JVM 虚拟机规范实现的&#xff0c;二者的特性差不多&am…

Springboot 使用 RabbitMq 延迟插件 实现订单到期未支付取消订单、设置提醒消息

示例业务场景&#xff1a; 场景1&#xff1a;客户下单后&#xff0c;15分钟内未支付取消订单&#xff01; 场景2&#xff1a;客户下单支付成功后&#xff0c;5分钟内商家未处理订单&#xff0c;需要推送一条消息提醒商家。如依旧未处理&#xff0c;则需要每隔2分钟消息提醒一下…

STL常用算法-C++

概述&#xff1a; 算法主要是由头文件 <algorithm> <functional> <numeric> 组成。<algorithm> 是所有 STL 头文件中最大的一个&#xff0c;范围涉及是比较、交换、查找、遍历操作、复制、修改等等。<functional> 定义了一些模板类&#xff0c;…

机器人制作开源方案 | 网球自动拾取机

作者&#xff1a;柳文浩、李浩杰、苏伟男、贾思萌、张天芸 单位&#xff1a;西安外事学院 指导老师&#xff1a;胡宝权、陈小虎 1. 产品说明 1.1 设计目的 近年来&#xff0c;网球运动越来越受到老百姓的欢迎&#xff0c;各种规模的比赛层出不穷。然而由于网球运动极为激烈…

中国改性聚丙烯产业调研与投资战略报告(2023版)

内容介绍&#xff1a; 改性聚丙烯就是基于聚丙烯原料对其性能和其他方面的一些改进&#xff0c;如增强聚丙烯材料的冲击、拉伸强度、弹性等。聚丙烯改性主要通过化学改性和物理改性进行调整&#xff0c;使其在拥有本身优异性能的同时可以兼顾其他有利于生产和使用的性能。改性…

Python3基础

导包 在 python 用 import 或者 from...import 来导入相应的模块。 将整个模块(somemodule)导入&#xff0c;格式为&#xff1a; import somemodule 从某个模块中导入某个函数,格式为&#xff1a; from somemodule import somefunction 从某个模块中导入多个函数,格式为&#…

C++基础 -4- C/C++混合编程

引用格式(图片代码段呈现) extern "C" {#include "string.h" }代码验证 &#xff08;分别使用了C/C 的标准输出&#xff09; #include "iostream"using namespace std;extern "C" { #include "stdio.h" #include "…

Win11修改用户名(超详细图文)

新买的电脑一般预装Windows11系统&#xff08;家庭与学生版&#xff09;&#xff0c;新电脑初次开机使用微软邮箱账号登录&#xff0c;则系统将用户名自动设置成邮箱前5位字符。我的用户名便是一串数字【231xx】&#xff08;qq邮箱前5位&#xff09;&#xff0c;看着很不舒服&a…

快速掌握Pyqt5的10种容器(Containers)

快速掌握Pyqt5的三种主窗口 快速掌握Pyqt5的2种弹簧 快速掌握Pyqt5的5种布局 快速弄懂Pyqt5的5种项目视图&#xff08;Item View&#xff09; 快速弄懂Pyqt5的4种项目部件&#xff08;Item Widget&#xff09; 快速掌握Pyqt5的6种按钮 快速掌握Pyqt5的20种输入控件&#xff08;…

属性级情感分析

笔记为自我总结整理的学习笔记&#xff0c;若有错误欢迎指出哟~ 属性级情感分析 简介数据集介绍数据加载和预处理&#xff08;data_utils.py&#xff09;预训练模型&#xff08;skep&#xff09;模型定义模块&#xff08;model.py&#xff09;训练配置&#xff08;config.py&am…