MASM32+Visual 2010 编辑字符函数
一.实验目的
利用MASM32+Visual 2010,利用汇编语言,写出如下字符函数:
(1)strset (2)strlen (3)strcmp
(4)strchr (5)memset (6)memcpy
二.环境配置
参考:MASM32+Visual studio 2010写汇编程序入门
连接:https://www.sychzs.cn/MaxWoods/article/details/44649685?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160182254019725222408586%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=160182254019725222408586&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-1-44649685.pc_first_rank_v2_rank_v28&utm_term=MASM32%2bVisual%20studio%202010%E5%86%99%E6%B1%87%E7%BC%96%E7%A8%8B%E5%BA%8F&spm=1018.2118.3001.4187
三.MASM32入门
假如学过微机原理,但是对 VS2010汇编编程不太熟悉的话,请参阅下文:
[入门masm32编写简单汇编程序并做具体分析](https://www.sychzs.cn/codes_first/article/details/78279641?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160179651419724848341514%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160179651419724848341514&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-1-78279641.pc_first_rank_v2_rank_v28&utm_term=MASM32%E5%86%99%E6%B1%87%E7%BC%96%E7%A8%8B%E5%BA%8F%E5%85%A5%E9%97%A8&spm=1018.2118.3001.4187)
四.思路与代码详解
3.1.strset函数
(1)strset函数功能
strset(string s,char c)——>将字符串s中所有的字符设置成c
(2)程序流图
step1:设置指针指向需要改变的字符串
step2:比较:此指针所指的字节值<——>0
step3:如果不相等,将此指针所指的字节值变为字符c,更新指针
step4:否则,更新指针,转到step2
(3)代码实现
.386
.model flat, stdcall
include www.sychzs.cn
includelib kernel32.lib
include www.sychzs.cn
includelib msvcrt.lib
.data
szText db "Reverse Engineering", 0
chr db 'j'
.code
main PROC
LEA EDI, szText
MOV ECX,0FFFFFFFFH
XOR EAX,EAX ;将EAX置为0
MOV AL,chr ;将低位AL置为j
change:
MOV [EDI],AL ;将j放入sztext中,将其中的一个字符变为J
ADD EDI,1 ;改变地址,使得地址增加
CMP BYTE PTR [EDI],0 ;观察一下,看这个字符是不是为0
JNZ change ;如果不是的话,就继续change循环
INVOKE crt_printf, addr szText ;将sztext打印出来
INVOKE crt_getchar
INVOKE ExitProcess, 0
main ENDP
END main
注意:上述代码中:
.386
.model flat,stdcall
include www.sychzs.cn
includelib kernel32.lib
...
这些代码段,均是使用C++进行汇编的一般架构,在上述连接:入门masm32编
写简单汇编程序并做具体分析中,有详细的叙述。
(4)结果展示
将"Reverse Engineering"全部转为"jjjj...jjjjj"
3.2.strlen函数
(1)strlen函数功能
int strset(string s)——>计算字符串s的长度
(2)程序流图
step1:设置指针指向需要计算长度的字符串,设置记录长度的寄存器初始为0
step2:比较:此指针所指的字节值<——>0
step3:如果不为0,将记录长度的寄存器+1,更新指针
step4:否则,说明字符串已经到结尾,结束程序
(3)代码实现
.386
.model flat, stdcall
include www.sychzs.cn
includelib kernel32.lib
include www.sychzs.cn
includelib msvcrt.lib
.data
szText db "xie chuan long is shuai", 0
format db "length = %d", 0AH, 0
.code
main PROC
LEA EDI, szText ;将字符串的初始地址放到EDI中去
MOV ECX,0FFFFFFFFH
MOV EAX,0 ;计数,初始值设为0
count:
CMP BYTE PTR [EDI],0 ;比较[EDI]的值(字符串的某个字符)是否为\0
PUSHFD ;将标志寄存器压入栈中
ADD EDI,1 ;将地址位+1
INC EAX ;还没看出[EDI]是否为\0,先加上再说
POPFD ;将标志寄存器出栈
LOOPNZ count ;观察ZF是否为0,如果不为0,循环count
DEC EAX ;因为最后把\0也加上了,所以要减去1
INVOKE crt_printf, addr format, EAX
INVOKE crt_getchar
INVOKE ExitProcess, 0
main ENDP
END main
(4)结果展示
计算"xie chuan long is shuai"的长度=23
3.3.strcmp函数
(1)strcmp函数功能
int strcmp(string s1,string s2):如果s1=s2,输出为0;如果s1>s2
(s2更短,或s2对应字符的ASCII更小),输出为1;如果前两种情况都不是
,输出为-1。
(2)程序流图
注意:s1(i)的含义为:s1字符串的第i个字符
(3)代码实现
szText db "Reverse Engineering", 0
szText2 db "Reverse Engineering", 0 ;szText==szText2
szText3 db "Reverse Eng", 0 ;szText>szText3
szText4 db "Reverse Engj", 0 ;szText szText5 db "Reverse Engh", 0 ;szText>szText5 .code main PROC LEA ESI, szText LEA EDI, szText2 ;result=0 ;LEA EDI, szText3 ;result=1 ;LEA EDI, szText4 ;result=-1 ;LEA EDI, szText5 ;result=1 ;话不多说,都在图中 compare: MOV BL,[EDI] CMP BYTE PTR [ESI],BL JNE noequal CMP BYTE PTR [ESI],0 JE equal ADD ESI,1 ADD EDI,1 JMP compare equal: MOV EAX,0 JMP print noequal: MOV BL,[EDI] CMP BYTE PTR [ESI],BL JA bigger smaller: MOV EAX,-1 JMP print bigger: MOV EAX,1 print: INVOKE crt_printf, addr format, EAX ;EAX=result INVOKE crt_getchar INVOKE ExitProcess, 0 main ENDP END main (4)结果展示 情况一:s1=szText s2=szText2="Reverse Engineering" 情况二:s1=szText s2=szText3="Reverse Eng" 或者 s1=szText s2=szText5="Reverse Engh"。输出相同结果。 情况三:s1=szText s2=szText4="Reverse Engj" 3.4.strchr函数 (1)strchr函数功能 int strchr(string s,char d)——>找到字符d在字符串s中的位置 (2)程序流图 (3)代码实现 .386 .model flat, stdcall include www.sychzs.cn includelib kernel32.lib include www.sychzs.cn includelib msvcrt.lib .data szText db "Reverse Engineering", 0 chr db 'i' format db "%d", 0AH, 0 result db "The program is over",0 .code main PROC LEA EDI, szText MOV ECX,0FFFFFFFFH MOV EBX,1 ;EBX记录现在在字符串的哪个位置 equ0: MOV CL,chr ;将要寻找的字符给CL CMP BYTE PTR [EDI],0 ;比较现在EDI所在的位置字符是否为0 JE over ;如果为0,则程序结束 CMP BYTE PTR [EDI],CL ;否则,比较EDI所在字符是否为要找的字符'i' JNE noprint ;如果不是,则直接开启下一个equ0循环 INVOKE crt_printf, addr format,EBX ;我们找到了'i'并输出,但是也许后面也有'i'哦! noprint: ADD EDI,1 ADD EBX,1 JMP equ0 over: INVOKE crt_printf, addr result INVOKE crt_getchar INVOKE ExitProcess, 0 main ENDP END main (4)结果展示 输出字符串"xie chuan long,i love you"中'i'字符的位置: 3.5.memset函数 (1)memset函数功能 void *memset(string s,char d,int size)——>将字符串s前size个字节用字符d替换 (2)程序流图 这个程序逻辑很简单,与前面strlen类型相似,在此不做描述。代码中有注释。 (3)代码实现 .386 .model flat, stdcall include www.sychzs.cn includelib kernel32.lib include www.sychzs.cn includelib msvcrt.lib .data szText db "xie chuan long is shuaige", 0 chr db 'x' siz db 3 format db "%d", 0AH, 0 .code main PROC LEA EDI,szText MOV ECX,0FFFFFFFFH XOR ECX,ECX XOR EDX,EDX MOV DH,chr ;将要重复的字符chr->DH MOV CL,siz ;将要复制的长度siz->CL change: CMP CL,0 ;比较CL是否为0,如果为0,则输出转换后的结果 JE print MOV [EDI],DH ;将siz->EDI所处位置 ADD EDI,1 ;更新EDI与CL DEC CL JMP change print: INVOKE crt_printf, addr szText INVOKE crt_getchar INVOKE ExitProcess, 0 main ENDP END main (4)结果展示 将字符串"xie chuan long is shuaige"前三个字符变为"x": 3.6.memcpy函数 (1)memcpy函数功能 void *memcpy(void *destin, void *source, unsigned n)——>将source所指字节按顺序给destin,字节长度为n (2)程序流图 这个程序逻辑也很简单,在此不做描述。代码中有注释。 (3)代码实现 .386 .model flat, stdcall include www.sychzs.cn includelib kernel32.lib include www.sychzs.cn includelib msvcrt.lib .data szText db "xie chuan long is shuaige", 0 chr db 128 dup(0) format db "%d", 0AH, 0 .code main PROC LEA EDI,szText ;EDI指向要复制的字符串,ESI指向目的地址 LEA ESI,chr MOV ECX,0FFFFFFFFH copy: CMP BYTE PTR [EDI],0 ;判断此时[EDI]是否为0 JE print ;如果为0,说明字符串已经到末尾,应该结束了 PUSH [EDI] ;将[EDI]->[ESI] POP [ESI] ADD EDI,1 ;将EDI与ESI增加1 ADD ESI,1 JMP copy print: INVOKE crt_printf, addr chr INVOKE crt_getchar INVOKE ExitProcess, 0 main ENDP END main (4)结果展示 多字节函数在Unicode下对应的函数: strcmp => wcscmp strlen => lstrlen strcpy => wcscpy sscanf => swscanf sprintf => wsprintf char => wchar_t atof => _wtof atoi => _wtoi LPCSTR => LPCTSTR LPCTSTR => LPCWSTR ? 注:更改界面文件为Unicode两个步骤: 1、先将xxx.rc文件以查看代码方式打开, 然后另存为,在【保存】按钮右边三角选择【以编码方式保存】, 选择【Unicode - 代码页1200】保存, 2、右键各个界面资源,选择【插入副本】,选择对应语言, 插入后,修改各个界面各个按钮文本即可 ? ? ? ? ? 1.回调函数范例一,在VS2010下编译通过 #include "stdafx.h" #include void printWelcome(int len) { printf("欢迎欢迎 -- %d\n", len); } void printGoodbye(int len) { printf("送客送客 -- %d\n", len); } void callback(int times, void (* print)(int)) { int i; for (i = 0; i < times; ++i) { print(i); } printf("\n我不知道你是迎客还是送客!\n\n"); } void main(void) { callback(10, printWelcome); callback(10, printGoodbye); printWelcome(5); } 运行结果如下 2.回调函数的范例二,在VS2010 下编译,通过 #include #include #include #include "stdafx.h" int my_strcmp(char* des, char* src); static void cmd_hello(void); static void cmd_hi(void); static void cmd_exit(void); //定义一个函数指针 typedef void (*callback)(void); //定义命令结构体 typedef struct cmd{ char name[10]; //命令的名字 callback func; //与命令相对应的函数指针 }cmd_t; //然后定义一个命令数组: //声明命令数组 cmd_t cmd_tbl[] = { {"hello",cmd_hello}, {"hi",cmd_hi}, {"exit",cmd_exit}, }; // 跟命令相对应的函数为: //相对应的功能函数为: void hello(void) { printf("hello\n"); } void hi(void) { printf("hi\n"); } static void cmd_hello(void) { hello(); } static void cmd_hi(void) { hi(); } static void cmd_exit(void) { //exit(0); } //此时我们还需要一个查找命令函数: cmd_t* my_find( char* val) { int i; for(i = 0; i < sizeof(cmd_tbl)/sizeof(cmd_tbl[0]); i++){ if(!my_strcmp(val,cmd_tbl[i].name)){ return &cmd_tbl[i]; } } return 0; } //字符串比较函数 int my_strcmp(char* des, char* src) { while(*des){ if(*des != *src) return 1; des++; src++; } return *src - *des; } //此函数返回的是一个结构体指针,若没有找到命令则返回0; //main 函数如下: int main(void){ char val[20] = {}; cmd_t *cmd_ptr; while(1){ gets(val); cmd_ptr = my_find(val); if(cmd_ptr){ cmd_ptr->func(); } else{ printf("no cmd\n"); } } return 0; } 运行结果: