当前位置: 首页 > news >正文

《汇编语言:基于X86处理器》第13章 复习题和编程练习

本篇记录了《汇编语言:基于X86处理器》第13章 复习题和编程练习的学习笔记。

13.6 复习题

1.当汇编过程被高级语言程序调用时,主调程序与被调过程是否应使用相同的内存模式?

答:主调程序与被调过程使用的内存模式必须相同。

2.C 和 C++程序调用汇编过程时,为什么区分大小写是很重要的?

答:C和C++编译器编译时是区分大小写的,所以如果调用汇编过程必须区分大小写,这样才能正确调用相应的汇编过程。

3.一种编程语言的调用规范是否包括了过程对某些寄存器的保存规定?

答:是的

4.(是/否):EVEN 和ALIGN伪指令是否都能用于内嵌汇编代码?

答:是

5.(是/否):OFFSET运算符是否能用于内嵌汇编代码?

答:否

6.(是/否):内嵌汇编代码中,DW和DUP运算符是否都能用于变量定义?

答:否

7.使用 fastcall调用规范时,若内嵌汇编代码修改了寄存器会出现什么情况?

答:_fastcall会使用编译器用寄存器来传递参数,会引起寄存器冲突,使用程序会结果错乱。

8.不使用OFFSET运算符,是否还有其他方法能把变量偏移量送入变址寄存器?

答:要以使用LEA指令得到变量偏移地址。例如 :lea esi,buffer ;将buffer的偏移地址送入ESI.

9.对 32位整数数组使用 LENGTH运算符,其返回值是多少?

答:LENGTH的返回值是数组的元素个数(与每个元素的大小无关 )

10.对长整型数组使用SIZE运算符,其返回值是多少?

答:返回值是元素的个数*单个元素的大小。LENGTH array * TYPE long

11.标准C printf()函数的有效汇编PROTO 声明是怎样的?

答:printf PROTO C, pString:PTR BYTE, args:VARARG

12.调用如下C语言函数,实参x是最先入栈还是最后入栈?

void MySub( x, y, z);

答:X是最后入栈,调用C语言函数时,是从右向左逆向入栈。

13.过程被 C++调用时,其外部声明使用的“C”说明符有什么作用?

答:防止c++的名称修饰。从汇编语言程序员的角度来看,名称修饰存在的问题是:C++编译器让链接器去找的是修饰过的名称,而不是生成可执行文件时的原始名称。

14.C++调用外部汇编过程时,为什么名称修饰是重要的?

答:C++编译器编译代码时会对函数名称进行修饰,例如:sub()函数编译时可能变成了_sub()函数,如果不使用名称修饰就会找不到对应的原始名称。

15.搜索互联网,用简表列出C/C++编译器使用的优化技巧。

答:

1. 高级优化

内联展开(Inline Expansion)
将小函数调用替换为函数体本身,减少调用开销(如-finline-functions)。

循环展开(Loop Unrolling)
减少循环控制开销,通过重复循环体(如#pragma unroll-funroll-loops)。

常量传播(Constant Propagation)
将常量表达式替换为计算结果(如int x = 3 * 5;int x = 15;)。

死代码消除(Dead Code Elimination)
删除不可达的代码(如未使用的变量或条件分支)。

函数返回值优化(RVO/NRVO)
避免临时对象的复制(直接构造返回值到目标内存)。

2. 循环优化

循环不变代码外提(Loop Invariant Code Motion)
将循环内不变的表达式移到循环外。

循环融合(Loop Fusion)
合并相邻的循环以减少迭代次数。

循环分块(Loop Tiling)
优化内存访问局部性(尤其对多维数组)。

3. 内存与指针优化

别名分析(Alias Analysis)
推断指针是否指向同一内存区域(如restrict关键字)。

标量替换(Scalar Replacement)
将数组元素替换为局部变量(减少内存访问)。

写缓冲优化(Write Buffering)
合并多次内存写入操作。

4. 指令级优化

指令调度(Instruction Scheduling)
重新排列指令以避免CPU流水线停顿。

自动向量化(Auto-Vectorization)
使用SIMD指令(如SSE/AVX)并行化计算(-mavx)。

分支预测优化(Branch Prediction)
通过重排代码提高分支预测命中率(如likely/unlikely宏)。

5. 链接时优化(LTO, Link-Time Optimization)

跨编译单元优化(如-flto),允许内联和删除未使用的全局函数。

6. 其他常见优化

尾调用优化(Tail Call Optimization)
将递归尾调用转为循环(避免栈溢出)。

公共子表达式消除(CSE)
重复计算的表达式只计算一次。

强度削减(Strength Reduction)
用低成本操作替换高成本操作(如乘法→加法)。

编译器标志示例(GCC/Clang)

-O1:基础优化(如常量传播、死代码消除)。

-O2:激进优化(包括向量化、循环展开)。

-O3:最高级优化(可能增加代码体积)。

-Os:优化代码大小。

-Ofast:激进优化,忽略严格标准合规性。

注意事项

调试与优化冲突:高优化级别可能导致调试信息不准确(如变量被优化掉)。

未定义行为(UB):依赖UB的代码可能被激进优化破坏(如指针越界)。

性能权衡:某些优化(如循环展开)可能增加代码体积,需根据场景选择。

编译器通过组合这些技术,在保证语义一致性的前提下最大化性能。实际效果可通过反汇编(objdump -d或编译器资源管理器)验证。

13.7编程练习

**1.数组与整数相乘

编写汇编子程序,实现一个双字数组与一个整数的乘法。编写C/C++测试程序,新建数组并将其传递给子程序,再输出运算后的结果数组。

头文件

#pragma once    //  防止头文件被重复包含,   非标准(但广泛支持)
//ArrayMul.h   对应的汇编语言文件ArrayMul.asm    C++测试文件13.7_1.cpp    extern "C" {void ArrayMul(int n, int array[], unsigned count);// Assembly language module
}

汇编语言实现文件

;ArrayMul函数    ArrayMul.asm    C++测试文件13.7_1.cpp    .586
.model flat, C
ArrayMul PROTO,intVal:DWORD, arrayPtr:PTR DWORD, count:DWORD.code 
;数组乘以同一个整数。返回:无
ArrayMul PROC USES ecx esi edi,intVal:DWORD, arrayPtr:PTR DWORD, count:DWORDmov ecx, count				        ;数组大小mov esi, arrayPtr				    ;数组指针mov edi, 0					L1:	mov eax, [esi+edi*4]mul intVal						    ;乘以同一个整数mov [esi+edi*4], eaxinc edi								;下一个数loop L1ret
ArrayMul ENDP 
END

C++测试文件

//13.7_1.cpp      13.7编译练习      **1.数组与整数相乘
//编写汇编子程序,实现一个双字数组与一个整数的乘法。
//编写C / C++测试程序,新建数组并将其传递给子程序,再输出运算后的结果数组。#include <iostream>
#include <time.h>
#include "ArrayMul.h"
using namespace std;int main()
{const int ARRAY_SIZE = 10;int array[ARRAY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};for (unsigned i = 0; i < ARRAY_SIZE; i++)printf("%d\t", array[i]);cout << endl;int intVal;cout << "Enter an integer value: ";cin >> intVal;ArrayMul(intVal,   array,  ARRAY_SIZE);for (unsigned i = 0; i < ARRAY_SIZE; i++)printf("%d\t", array[i]);cout << endl;return 0;
}

运行调试:

***2.最长递增序列

编写汇编子程序,接收两个输人参数:数组偏移量和数组大小。子程序返回数组中最长的递增序列中整数值的个数。比如,数组如下所示,则最长的严格递增序列开始于索引值为3的元素、序列长度为4{14,17、26、42}:

[-5,10,20,14,17,26,42、22,19,-5]

编写 C/C++测试程序调用该子程序,测试程序实现的操作包括:新建数组、传递参数、输出子程序的返回值。

头文件:LongestSelfIncSeq.h

#pragma once    //  防止头文件被重复包含,   非标准(但广泛支持)
//LongestSelfIncSeq.h   对应的汇编语言文件LongestSelfIncSeq.asm    C++测试文件13.7_2.cpp    extern "C" {int LongestSelfIncSeq(int array[], unsigned count);// Assembly language module
}

汇编实现文件: LongestSelfIncSeq.asm

;LongestSelfIncSeq函数    LongestSelfIncSeq.asm    C++测试文件13.7_2.cpp    .586
.model flat, C
LongestSelfIncSeq PROTO,arrayPtr:PTR DWORD, count:DWORD.code 
;查找数组中最长的递增序列中整数值的个数。返回:eax为最长递增序列的个数
LongestSelfIncSeq PROC USES ecx esi edi,arrayPtr:PTR DWORD, count:DWORDlocal counter:DWORDlocal position: dwordmov counter, 0mov esi, arrayPtr					;数组的起始地址mov edi, arrayPtradd edi, TYPE DWORD					;第2个元素   用以相临元素的比较是否自增mov ecx, count					    ;数组大小mov edx, 0							;设置计数器L1:	mov eax,  [esi]mov ebx,  [edi]cmp ebx, eax						;比较相临两个元素是否递增jl L2								;ebx < eax   有符号比较:小于跳转L2inc edx								;否则ebx > eax, 递增序列的个数自增jmp L3L2:										;此处EDX中的当前计数器与counter中先前保存的值进行了比较mov eax, countercmp edx, eaxjb L3								;无符号比较:小于跳转L3,如果EDX中的当前值大于之前保存在 counter中的值,则EDX将保存在计数器中mov counter, edx					;保存最长递增序列的长度mov position, esi					;保存最长序列最后一个成员的索引mov edx, 0
L3:add esi, TYPE DWORD				    ;下一个元素add edi, TYPE DWORDloop L1mov eax, counterinc eaxret
LongestSelfIncSeq ENDP 
END

C++测试文件:13.7_2.cpp

//13.7_2.cpp      13.7编译练习     ***2.最长递增序列
//编写汇编子程序,接收两个输人参数:数组偏移量和数组大小。子程序返回数组中最长的递增序列中整数值的个数。
//比如,数组如下所示,则最长的严格递增序列开始于索引值为3的元素、序列长度为4{ 14,17、26、42 } :
//	[-5,10,20,14,17,26,42、22,19, - 5]
//编写 C / C++测试程序调用该子程序,测试程序实现的操作包括 : 新建数组、传递参数、输出子程序的返回值。#include <iostream>
#include <time.h>
#include "LongestSelfIncSeq.h"using namespace std;int main()
{int array[10] = { -5, 10, 20, 14, 17, 26, 42, 22, 19, -5 };int num = LongestSelfIncSeq(array, sizeof(array) / 4);printf("Reslut is: %d\n", num);return 0;
}

运行调试:

**3.三个数组求和

编写汇编子程序。接收三个同样大小数组的偏移量。将第二个和第三个数组加到第一个数组上。子程序返回时,第一个数组包含结果数值。编写 C/C++测试程序,新建数组并将其传递给子程序,再显示第一个数组的内容。

头文件:ArraySum.h

#pragma once    //  防止头文件被重复包含,   非标准(但广泛支持)
//ArraySum.h   对应的汇编语言文件ArraySum.asm    C++测试文件13.7_3.cpp    extern "C" {void ArraySum(int array1[], int array2[], int array3[], unsigned count);// Assembly language module
}

汇编文件ArraySum.asm

;ArraySum函数    ArraySum.asm    C++测试文件13.7_3.cpp    .586
.model flat, C
ArraySum PROTO,arrayPtr1:PTR DWORD, arrayPtr1:PTR DWORD, arrayPtr3:PTR DWORD, count:DWORD.code 
;把第2个数组与第3个数组相加,和放在第1个数组。返回:无
ArraySum PROC USES ecx esi edi ebx edx,arrayPtr1:PTR DWORD, arrayPtr2:PTR DWORD, arrayPtr3:PTR DWORD, count:DWORDmov ecx, count							;数组大小mov esi, arrayPtr2						;数组指针mov edi, arrayPtr3mov edx, arrayPtr1mov ebx, 0L1:	mov eax, [esi + ebx*4]add eax,  [edi + ebx*4]					;相加mov [edx + ebx*4], eaxinc ebx									;下一个数loop L1ret
ArraySum ENDP 
END

C++测试文件:

//13.7_3.cpp      13.7编译练习      **3.三个数组求和
//编写汇编子程序。接收三个同样大小数组的偏移量。将第二个和第三个数组加到第一个数组上。
//子程序返回时,第一个数组包含结果数值。编写 C / C++测试程序,新建数组并将其传递给子程序,
//再显示第一个数组的内容。#include <iostream>
#include <time.h>
#include "ArraySum.h"
using namespace std;int main()
{const int ARRAY_SIZE = 10;int array1[ARRAY_SIZE] = { 0 };int array2[ARRAY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};int array3[ARRAY_SIZE] = { 11,12, 13, 14, 15, 16, 17, 18, 19, 20 };cout << "array 2: " << endl;for (unsigned i = 0; i < ARRAY_SIZE; i++)printf("%d\t", array2[i]);cout << endl;cout << "array 3: " << endl;for (unsigned i = 0; i < ARRAY_SIZE; i++)printf("%d\t", array3[i]);cout << endl;cout << "array1 = array2 + array3: " << endl;ArraySum(array1, array2, array3, ARRAY_SIZE);for (unsigned i = 0; i < ARRAY_SIZE; i++)printf("%d\t", array1[i]);cout << endl;cout << endl;return 0;
}

运行调试:

***4.质数程序

编写汇编过程实现如下功能:若传递给EAX 的 32 位整数为质数,则返回1;若 EAX 为非质数,则返回 0。要求从高级语言程序调用该过程。由用户输入一组整数,对每个数值,程序都要显示一条信息以示该数是否为质数。建议:第一次调用该过程时,使用厄拉多塞过滤算法(Sieve ofEratosthenes)初始化布尔数组。

头文件:isPrime.h

#pragma once    //  防止头文件被重复包含,   非标准(但广泛支持)
//isPrime.h   对应的汇编语言文件isPrime.asm    C++测试文件13.7_4.cpp    extern "C" {int isPrime(unsigned intVal);// Assembly language module
}

汇编语言过程实现文件:isPrime.asm

;isPrime函数    isPrime.asm    C++测试文件13.7_4.cpp    .586
.model flat, C
isPrime PROTO,intVal:DWORD .code 
;判断是否为素数。返回:是素数eax为1, 不是素数eax为0
isPrime PROC USES ecx edx,intVal:DWORDmov eax, intValcmp eax, 1je isNotcmp eax, 2					    ;2是最小的素数je stopmov ecx, intValsub ecx, 2						;1和本身略过mov ebx, intValdec ebxL1:	mov edx, 0mov eax, intValdiv ebx							;商在eax中,余数在edx中cmp edx, 0je isNotcmp ecx, 1je stopdec ebxloop L1
stop:mov eax, 1ret
isNot:mov eax, 0ret
isPrime ENDP 
END

C++测试文件:13.7_4.cpp

//13.7_4.cpp      13.7编译练习     ***4.质数程序
//编写汇编过程实现如下功能:若传递给EAX 的 32 位整数为质数,则返回1; 若 EAX 为非质数,则返回 0。
//要求从高级语言程序调用该过程。由用户输入一组整数,对每个数值,程序都要显示一条信息以示该数是否为质数。
//建议:第一次调用该过程时,使用厄拉多塞过滤算法(Sieve ofEratosthenes)初始化布尔数组。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "isPrime.h"
using namespace std;int main()
{int array[6] = { 0 };printf("Please input six integers:\n");for (unsigned i = 0; i < 6; i++)scanf("%d", &array[i]);printf("\n");for (unsigned i = 0; i < 6; i++){if (isPrime(array[i]))printf("%d is prime\n", array[i]);elseprintf("%d is not prime\n", array[i]);}printf("\n");return 0;
}

运行调试:

*5.LastindexOf过程

修改13.3.1 节的 IndexOf 过程。将新函数命名为LastlndexOf,使其从数组末尾开始反向搜索。遇到第一个匹配值就返回其索引,否则即为未发现匹配值,返回-1。

.h头文件

#pragma once    //  防止头文件被重复包含,   非标准(但广泛支持)
// indexof.h   对应的汇编语言文件IndexOf.asm    C++测试文件13.7_5.cpp    extern "C" {long IndexOf(long n, long array[], unsigned count);// Assembly language module
}

汇编实现文件

;IndexOf函数    IndexOf.asm    C++测试文件13.7_5.cpp    .586
.model flat, C
IndexOf PROTO,srchVal:DWORD, arrayPtr:PTR DWORD, count:DWORD.code 
;---------------------------------------------------------------
IndexOf PROC USES ecx esi edi,srchVal:DWORD, arrayPtr:PTR DWORD, count:DWORD
;
;对32位整数数组执行线性搜索——从后往前搜索
;寻找指定数值。如果发现匹配数值
;用EAX返回该数值的索引位置
;否则,EAX返回-1.
;----------------------------------------------------------------NOT_FOUND = -1mov eax, srchVal				;搜索数值mov ecx, count				    ;数组大小mov esi, arrayPtr				;数组指针mov edi, count					;索引到最后dec edi							;最后一个元素的索引(数组从0开始)L1:	cmp [esi+edi*4], eaxje founddec edi							;从后往前索引loop L1notFound:mov eax, NOT_FOUNDjmp short exitfound:mov eax, ediexit:ret
IndexOf ENDP 
END

C++测试文件

//13.7_5.cpp      13.7编译练习      *5.LastindexOf过程
//修改13.3.1 节的 IndexOf 过程。将新函数命名为LastlndexOf,使其从数组末尾开始反向搜索。
//遇到第一个匹配值就返回其索引,否则即为未发现匹配值,返回 - 1。
#include <iostream>
#include <time.h>
#include "indexof.h"
using namespace std;int main(){//用伪随机数填充数组。const unsigned ARRAY_SIZE = 100000;const unsigned LOOP_SIZE = 100000;const char* boolstr[] = { "false","true" };long array[ARRAY_SIZE];for (unsigned i = 0; i < ARRAY_SIZE; i++)array[i] = rand() % 50000;long searchVal;time_t startTime, endTime;cout << "Enter an integer value to find: ";cin >> searchVal;cout << "Please wait...\n";//测试汇编函数time(&startTime);long index = 0;for (unsigned n = 0; n < LOOP_SIZE; n++)index = IndexOf( searchVal, array, ARRAY_SIZE );bool found = index != -1;time(&endTime);cout << "Elapsed ASM time: " << long(endTime - startTime)<< " seconds. Found = " << boolstr[found] << endl;return 0;
}

运行调试:

http://www.dtcms.com/a/330074.html

相关文章:

  • VerIF
  • 【R语言】RStudio 中的 Source on Save、Run、Source 辨析
  • [系统架构设计师]系统架构基础知识(一)
  • MySQL表约束
  • 关于大学计算机专业的课程的一些看法
  • windows通过共享网络上网
  • JavaWeb之响应
  • 使用BeautifulReport让自动化测试报告一键生成
  • 开源组件的“暗礁”:第三方库中的输入与边界风险治理
  • 「数据获取」《广西调查年鉴》(2007-2024)(2009缺失)(获取方式看绑定的资源)
  • GISBox工具处理:将高斯泼溅模型导出为3DTiles
  • 【15】Transformers快速入门:添加自定义 Token
  • 服务器安全防护
  • ARM芯片架构之CoreSight Channel Interface 介绍
  • 基于边缘深度学习的棒球击球训练评估研究
  • 模型训练不再“卡脖子”:国产AI训练平台对比与落地实践指南
  • 马力是多少W,常见车辆的马力范围
  • RK3568项目(十四)--linux驱动开发之常用外设
  • 中科米堆CASAIM蓝光三维扫描仪用于焊接件3D尺寸检测
  • 2025 开源语音合成模型全景解析:从工业级性能到创新架构的技术图谱
  • Python实现点云概率ICP(GICP)配准——精配准
  • static 和 extern 关键字
  • 公用表表达式和表变量的用法区别?
  • 【SpringBoot】12 核心功能-配置文件详解:Properties与YAML配置文件
  • WinForm中C#扫描枪功能实现(含USB串口)
  • 终端安全检测与防御
  • 20250813比赛总结
  • C++ list模拟实现
  • 未来AI:微算法科技(NASDAQ:MLGO)开发基于忆阻器网络储层计算MemristorPattern虚拟平台
  • 精准阻断内网渗透:联软科技终端接入方案如何“锁死”横向移动?