52、32位汇编语言与C/C++的链接及相关编程实践

32位汇编语言与C/C++的链接及相关编程实践

1. 乘法表示例

我们将编写一个简单的应用程序,该程序会提示用户输入一个整数,使用位运算将其乘以2的升幂(从2^1到2^10),并显示每个乘积,同时在前面填充空格。这里使用C++进行输入输出,汇编语言模块会调用三个用C++编写的函数,程序由一个C++模块启动。

1.1 汇编语言模块

汇编语言模块包含一个名为 DisplayTable 的函数,它调用一个名为 askForInteger 的C++函数来获取用户输入的整数,然后使用循环将名为 intVal 的整数左移并通过调用 showInt 函数显示结果。

; ASM function called from C++
INCLUDE Irvine32.inc
; External C++ functions:
askForInteger PROTO C
showInt PROTO C, value:SDWORD, outWidth:DWORD
newLine PROTO C
OUT_WIDTH = 8
ENDING_POWER = 10
.data
intVal DWORD ?
.code
;---------------------------------------------
SetTextOutColor PROC C, 
color:DWORD
;
; Sets the text colors and clears the console
; window. Calls Irvine32 library functions.
;---------------------------------------------
mov
eax,color
call
SetTextColor
call
Clrscr
ret
SetTextOutColor ENDP
;---------------------------------------------
DisplayTable PROC C
;
; Inputs an integer n and displays a
; multiplication table ranging from n * 2^1
; to n * 2^10.
;----------------------------------------------
INVOKE askForInteger
; call C++ function
mov
intVal,eax 
; save the integer
mov
ecx,ENDING_POWER
; loop counter
L1:
push
ecx
; save loop counter
shl
intVal,1
; multiply by 2
INVOKE showInt,intVal,OUT_WIDTH
INVOKE newLine
; output CR/LF
pop
ecx
; restore loop counter
loop
L1
ret
DisplayTable ENDP
END

DisplayTable 过程中,在调用 showInt newLine 之前必须对 ECX 进行压栈和出栈操作,因为Visual C++函数不会保存和恢复通用寄存器。 askForInteger 函数将结果返回在 EAX 寄存器中。

DisplayTable 在调用C++函数时不一定要使用 INVOKE ,也可以使用 PUSH CALL 指令,例如调用 showInt 的代码如下:

push  OUT_WIDTH
; push last argument first
push  intVal
call  showInt
; call the function
add
 esp,8
; clean up stack

必须遵循C语言调用约定,即参数以相反的顺序压入栈中,调用者负责在调用后从栈中移除参数。

1.2 C++测试程序

下面是启动程序的C++模块,其入口点是 main() ,确保执行所需的C++语言初始化代码。它包含外部汇编语言过程和三个导出函数的函数原型:

// main.cpp
// Demonstrates function calls between a C++ program
// and an external assembly language module.
#include <iostream>
#include <iomanip>
using namespace std;
extern "C" {
// external ASM procedures:
void DisplayTable();
void SetTextOutColor(unsigned color);
// local C++ functions:
int askForInteger();
void showInt(int value, int width);
}
// program entry point
int main()
{
SetTextOutColor( 0x1E );
// yellow on blue
DisplayTable();
// call ASM procedure
return 0;
}
// Prompt the user for an integer. 
int askForInteger()
{
int n;
cout << "Enter an integer between 1 and 90,000:";
cin >> n;
return n;
}
// Display a signed integer with a specified width.
void showInt( int value, int width )
{
cout << setw(width) << value;
}
1.3 项目构建

将C++和汇编语言模块都添加到Visual Studio项目中,然后从“项目”菜单中选择“生成解决方案”。

1.4 程序输出

当用户输入90000时,乘法表程序的示例输出如下:

Enter an integer between 1 and 90,000: 90000
180000
  360000
  720000
 1440000
 2880000
 5760000
11520000
23040000
46080000
92160000
1.5 Visual Studio项目属性

如果使用Visual Studio构建集成C++和汇编语言并调用Irvine32库的程序,需要更改一些项目设置。以 Multiplication_Table 程序为例,操作步骤如下:
1. 从“项目”菜单中选择“属性”。
2. 在窗口左侧的“配置属性”下,选择“链接器”。
3. 在右侧面板的“附加库目录”中输入 c:\Irvine
4. 点击“确定”关闭“公共属性页”窗口。

这样Visual Studio就可以找到Irvine32库了。

2. 调用C库函数

C语言有一个标准化的函数集合,称为标准C库。这些函数同样适用于C++程序,因此也适用于与C和C++程序关联的汇编语言模块。汇编语言模块在调用每个C函数时必须包含其原型,通常可以通过访问C++编译器提供的帮助系统找到C函数原型,在从程序中调用这些函数之前,必须将C函数原型转换为汇编语言原型。

2.1 printf函数

printf 函数的C/C++语言原型如下,第一个参数是指向字符的指针,后面可以跟可变数量的参数:

int printf(
   const char *format [, argument]... 
);

汇编语言中的等效原型将 char * 改为 PTR BYTE ,并将可变长度参数列表改为 VARARG 类型:

printf PROTO C, pString:PTR BYTE, args:VARARG
2.2 scanf函数

scanf 函数用于从标准输入(键盘)输入字符、数字和字符串,并将输入值赋给变量,其原型如下:

scanf PROTO C, format:PTR BYTE, args:VARARG
2.3 使用printf函数显示格式化实数

编写格式化和显示浮点值的汇编语言函数并不容易,我们可以利用C库的 printf 函数。在Visual C++ .NET中设置这样的程序的步骤如下:
1. 在Visual C++中创建一个Win32控制台程序,创建一个名为 main.cpp 的文件,并插入一个调用 asmMain main 函数:

extern "C" void asmMain( );
int main( )
{
asmMain( );
return 0;
}
  1. 在与 main.cpp 相同的文件夹中,创建一个名为 asmMain.asm 的汇编语言模块,其中应包含一个使用C调用约定声明的 asmMain 过程:
; asmMain.asm
.386
.model flat,stdcall
.stack 2000
.code
asmMain PROC C
ret
asmMain ENDP
END
  1. 汇编 asmMain.asm (但不链接),生成 asmMain.obj
  2. asmMain.obj 添加到C++项目中。
  3. 构建并运行项目。如果修改了 asmMain.asm ,则再次汇编并重新构建项目后再运行。

程序正确设置后,就可以在 asmMain.asm 中添加调用C/C++语言函数的代码。例如,以下汇编语言代码在 asmMain 中通过调用 printf 打印一个 REAL8 类型的值:

.data
double1 REAL8  1234567.890123
formatStr BYTE "%.3f",0dh,0ah,0
.code
INVOKE printf, ADDR formatStr, double1

对应的输出为:

1234567.890

传递给 printf 的格式字符串与C++中的略有不同,不能嵌入转义字符(如 \n ),而必须插入ASCII码( 0dh, 0ah )。

2.4 多个参数

printf 函数可以接受可变数量的参数,因此可以在一次函数调用中轻松格式化和显示两个数字:

TAB = 9
.data
formatTwo BYTE "%.2f",TAB,"%.3f",0dh,0ah,0
val1 REAL8 456.789
val2 REAL8 864.231
.code
INVOKE printf, ADDR formatTwo, val1, val2

对应的输出为:

456.79  864.231
2.5 使用scanf函数输入实数

可以调用 scanf 从用户输入中获取浮点值,示例代码如下:

.data
strSingle BYTE "%f",0
strDouble BYTE "%lf",0
single1 REAL4 ?
double1 REAL8 ?
.code
INVOKE scanf, ADDR strSingle, ADDR single1
INVOKE scanf, ADDR strDouble, ADDR double1

必须从C或C++启动程序中调用汇编语言代码。传递给 printf 的浮点参数应声明为 REAL8 类型,虽然也可以传递 REAL4 类型的值,但需要一些巧妙的编程技巧。

3. 目录列表程序

我们将编写一个简短的程序,该程序会清屏、显示当前磁盘目录,并要求用户输入一个文件名(你可能希望扩展此程序以打开并显示所选文件)。

3.1 C++存根模块

C++模块只包含一个对 asm_main 的调用,因此可以称其为存根模块:

// main.cpp
// stub module: launches assembly language program
extern "C" void asm_main();
// asm startup proc
void main()
{
asm_main();
}
3.2 汇编语言模块

汇编语言模块包含函数原型、几个字符串和一个 fileName 变量。它两次调用 system 函数,分别传递“cls”和“dir”命令,然后调用 printf 显示文件名提示,调用 scanf 让用户输入文件名。由于它不调用Irvine32库,因此可以将 .MODEL 指令设置为C语言约定:

; ASM program launched from C++      (asmMain.asm)
.586
.MODEL flat,C
; Standard C library functions:
system PROTO, pCommand:PTR BYTE
printf PROTO, pString:PTR BYTE, args:VARARG
scanf  PROTO, pFormat:PTR BYTE,pBuffer:PTR BYTE, args:VARARG
fopen  PROTO, mode:PTR BYTE, filename:PTR BYTE
fclose PROTO, pFile:DWORD
BUFFER_SIZE = 5000
.data
str1 BYTE "cls",0
str2 BYTE "dir/w",0
str3 BYTE "Enter the name of a file:",0
str4 BYTE "%s",0
str5 BYTE "cannot open file",0dh,0ah,0
str6 BYTE "The file has been opened",0dh,0ah,0
modeStr BYTE "r",0
fileName BYTE 60 DUP(0)
pBuf  DWORD ?
pFile DWORD ?
.code
asm_main PROC
; clear the screen, display disk directory
INVOKE system,ADDR str1
INVOKE system,ADDR str2
; ask for a filename
INVOKE printf,ADDR str3
INVOKE scanf, ADDR str4, ADDR filename
; try to open the file
INVOKE fopen, ADDR fileName, ADDR modeStr
mov
pFile,eax
.IF eax == 0
; cannot open file?
INVOKE printf,ADDR str5
  jmp quit
.ELSE
INVOKE printf,ADDR str6
.ENDIF
; Close the file
INVOKE fclose, pFile
quit:
ret             
; return to C++ main
asm_main ENDP
END

scanf 函数需要两个参数:第一个是指向格式字符串(“%s”)的指针,第二个是指向输入字符串变量( fileName )的指针。

以下是相关操作的流程图:

graph TD;
    A[开始] --> B[清屏并显示磁盘目录];
    B --> C[提示输入文件名];
    C --> D[获取用户输入的文件名];
    D --> E[尝试打开文件];
    E --> F{文件是否打开成功};
    F -- 是 --> G[显示文件已打开信息];
    F -- 否 --> H[显示无法打开文件信息];
    G --> I[关闭文件];
    H --> I;
    I --> J[结束];
4. 相关总结与思考

通过上述内容,我们了解了如何将32位汇编语言代码与C/C++程序进行链接,以及如何调用C库函数。在实际编程中,需要注意以下几点:
- 当从一种语言的程序调用汇编语言过程时,两种语言共享的标识符必须兼容,并且过程中使用的段名必须与调用程序兼容。
- 编写过程时,需要使用高级语言的调用约定来确定如何接收参数,调用约定会影响栈指针是由被调用过程还是调用程序恢复。
- 在Visual C++中, __asm 指令用于在C++源程序中编写内联汇编代码。
- 调用标准C(C++)库函数时,需要创建一个包含 main() 函数的存根程序,从 main() 调用汇编语言模块中的启动过程,汇编语言模块可以调用C标准库中的任何函数。

以下是一些复习问题和编程练习,帮助我们进一步巩固所学知识:

4.1 复习问题
  1. 当高级语言程序调用汇编语言编写的过程时,调用程序和过程是否必须使用相同的内存模型?
  2. 为什么在从C和C++程序调用汇编语言过程时,大小写敏感性很重要?
  3. 语言的调用约定是否包括过程对某些寄存器的保存?
  4. (是/否): EVEN ALIGN 指令是否都可以在内联汇编代码中使用?
  5. (是/否): OFFSET 运算符是否可以在内联汇编代码中使用?
  6. (是/否):是否可以在内联汇编代码中使用 DW DUP 运算符定义变量?
  7. 使用 __fastcall 调用约定时,如果内联汇编代码修改了寄存器,可能会发生什么?
  8. 除了使用 OFFSET 运算符,还有其他方法将变量的偏移量移动到索引寄存器中吗?
  9. LENGTH 运算符应用于32位整数数组时返回什么值?
  10. SIZE 运算符应用于长整数数组时返回什么值?
  11. 标准C printf() 函数的有效汇编语言 PROTO 声明是什么?
  12. 调用以下C语言函数时,参数 x 是先压入栈还是最后压入栈?
void MySub( x, y, z );
  1. 在从C++调用的过程的 extern 声明中,“C”指定符的作用是什么?
  2. 为什么在从C++调用外部汇编语言过程时,名称修饰很重要?
  3. 通过互联网搜索,列出一些C/C++编译器使用的优化技巧。
4.2 编程练习
  1. 数组与整数相乘 :编写一个汇编语言子程序,将一个双字数组乘以一个整数。编写一个C/C++测试程序,创建一个数组,将其传递给子程序,并打印结果数组的值。
  2. 最长递增序列 :编写一个汇编语言子程序,接收两个输入参数:数组的偏移量和数组的大小。它必须返回整数序列中最长递增序列的长度。例如,在数组 [ -5, 10, 20, 14, 17, 26, 42, 22, 19, -5 ] 中,最长严格递增序列从索引3开始,长度为4( { 14, 17, 26, 42 } )。从C/C++程序中调用该子程序,创建数组,传递参数,并打印子程序返回的值。
  3. 三个数组求和 :编写一个汇编语言子程序,接收三个等长数组的偏移量,将第二个和第三个数组的值加到第一个数组中。返回时,第一个数组将包含所有新值。编写一个C/C++测试程序,创建数组,将其传递给子程序,并打印第一个数组的内容。
  4. 素数程序 :编写一个汇编语言过程,如果传递到 EAX 寄存器中的32位整数是素数,则返回1;如果不是素数,则返回0。从高级语言程序中调用该过程,让用户输入一系列整数,并让程序为每个整数显示是否为素数的消息。建议在第一次调用该过程时使用埃拉托斯特尼筛法初始化一个布尔数组。
  5. LastIndexOf过程 :修改 IndexOf 过程,将函数命名为 LastIndexOf ,让它从数组末尾向后搜索。返回第一个匹配值的索引,如果未找到匹配项,则返回 -1。

通过这些复习问题和编程练习,我们可以更好地掌握32位汇编语言与C/C++的链接以及相关编程技巧,进一步提升编程能力。

以上就是关于32位汇编语言与C/C++链接及相关编程实践的详细介绍,希望对大家有所帮助。在实际应用中,我们可以根据具体需求灵活运用这些知识,实现更高效、更复杂的程序。

32位汇编语言与C/C++的链接及相关编程实践

5. 16位MS - DOS编程基础

在早期的计算机系统中,16位MS - DOS编程有着重要的地位。下面将介绍MS - DOS和IBM - PC相关的一些基础知识。

5.1 MS - DOS和IBM - PC概述

IBM的PC - DOS是第一个在使用Intel 8088处理器的IBM个人计算机上实现实地址模式的操作系统,后来发展成了Microsoft MS - DOS。实地址模式也被称为16位模式,因为地址是由16位值构成的。为了确保与相关程序的完全兼容,建议安装早期版本的Windows,如Windows 98,也可以使用软件工具在计算机上创建虚拟机进行实验。

5.2 内存组织

在MS - DOS环境下,内存组织有其特定的方式。但具体的内存组织细节这里暂不详细展开,后续可以根据实际需求进一步深入研究。

5.3 重定向输入 - 输出

重定向输入 - 输出是MS - DOS编程中的一个重要功能。它允许程序将输入从标准输入(通常是键盘)重定向到文件,或者将输出从标准输出(通常是屏幕)重定向到文件。例如,在命令行中可以使用 > 符号将程序的输出重定向到一个文件,使用 < 符号将文件内容作为程序的输入。

5.4 软件中断

软件中断是MS - DOS编程中实现系统功能调用的重要方式。通过执行特定的中断指令,程序可以请求操作系统提供各种服务,如输入输出、文件操作等。在MS - DOS中,常用的中断是 INT 21h ,它提供了丰富的功能调用。

5.5 INT指令

INT 指令用于触发软件中断。例如, INT 21h 就是调用MS - DOS的功能服务。在汇编语言中,可以使用 INT 指令来调用这些服务,实现各种操作。

5.6 16位程序编码

编写16位MS - DOS程序时,需要注意一些特殊的编码规则。例如,地址的表示方式、寄存器的使用等都与32位编程有所不同。以下是一个简单的16位汇编程序示例:

.MODEL SMALL
.STACK 100H
.DATA
    msg DB 'Hello, MS - DOS!', 0DH, 0AH, '$'
.CODE
MAIN PROC
    MOV AX, @DATA
    MOV DS, AX
    LEA DX, msg
    MOV AH, 09H
    INT 21H
    MOV AH, 4CH
    INT 21H
MAIN ENDP
END MAIN

这个程序的功能是在屏幕上显示 Hello, MS - DOS! ,然后退出程序。

6. MS - DOS函数调用(INT 21h)

INT 21h 是MS - DOS中非常重要的一个中断,它提供了众多的功能调用,涵盖了输出、输入、日期时间等多个方面。

6.1 部分输出函数
  • 显示字符 :使用 AH = 02H 功能调用可以在屏幕上显示一个字符。例如:
MOV DL, 'A'
MOV AH, 02H
INT 21H

这段代码会在屏幕上显示字符 A
- 显示字符串 :使用 AH = 09H 功能调用可以显示一个以 $ 结尾的字符串。例如前面示例中的显示 Hello, MS - DOS! 的代码。

6.2 “Hello World”程序示例

以下是一个完整的“Hello World”程序示例,使用 INT 21h 的功能调用:

.MODEL SMALL
.STACK 100H
.DATA
    hello_msg DB 'Hello World!', 0DH, 0AH, '$'
.CODE
MAIN PROC
    MOV AX, @DATA
    MOV DS, AX
    LEA DX, hello_msg
    MOV AH, 09H
    INT 21H
    MOV AH, 4CH
    INT 21H
MAIN ENDP
END MAIN

这个程序的执行流程如下:

graph TD;
    A[开始] --> B[初始化数据段寄存器];
    B --> C[加载字符串地址到DX];
    C --> D[设置功能号AH为09H];
    D --> E[调用INT 21h显示字符串];
    E --> F[设置功能号AH为4CH];
    F --> G[调用INT 21h退出程序];
    G --> H[结束];
6.3 部分输入函数
  • 读取字符 :使用 AH = 01H 功能调用可以从键盘读取一个字符。例如:
MOV AH, 01H
INT 21H
MOV BL, AL

这段代码会从键盘读取一个字符,并将其存储在 BL 寄存器中。
- 读取字符串 :使用 AH = 0AH 功能调用可以从键盘读取一个字符串。需要预先定义一个缓冲区来存储输入的字符串。

6.4 日期/时间函数

INT 21h 还提供了一些日期和时间相关的功能调用。例如, AH = 2AH 可以获取当前日期, AH = 2CH 可以获取当前时间。以下是获取当前日期的示例代码:

MOV AH, 2AH
INT 21H
MOV AL, DH ; 年的高8位
MOV BL, DL ; 年的低8位
MOV CL, CH ; 月
MOV DL, DH ; 日
6.5 相关总结

通过 INT 21h 的各种功能调用,我们可以实现丰富的输入输出、日期时间等操作。在使用这些功能调用时,需要注意每个功能调用的参数设置和返回值的处理。

7. 标准MS - DOS文件I/O服务

在MS - DOS环境下,文件的输入输出操作也是非常重要的。下面介绍一些标准的MS - DOS文件I/O服务。

7.1 创建或打开文件(716Ch)

使用特定的功能调用可以创建或打开一个文件。在汇编语言中,需要设置相应的参数并调用中断来完成操作。例如:

MOV AH, 716Ch
; 设置其他参数
INT 21H

具体的参数设置需要根据实际需求进行调整。

7.2 关闭文件句柄(3Eh)

当文件使用完毕后,需要关闭文件句柄以释放系统资源。可以使用 AH = 3Eh 功能调用来关闭文件句柄。示例代码如下:

MOV AH, 3Eh
; 设置文件句柄
INT 21H
7.3 移动文件指针(42h)

在文件操作中,有时需要移动文件指针到指定的位置。可以使用 AH = 42h 功能调用来实现。例如:

MOV AH, 42h
; 设置移动方式和偏移量
INT 21H
7.4 获取文件创建日期和时间

可以通过特定的功能调用获取文件的创建日期和时间。具体的功能调用和参数设置需要根据实际情况进行调整。

7.5 示例:读取和复制文本文件

以下是一个读取和复制文本文件的示例代码:

.MODEL SMALL
.STACK 100H
.DATA
    input_file DB 'input.txt', 0
    output_file DB 'output.txt', 0
    buffer DB 100 DUP(?)
    handle_in DW ?
    handle_out DW ?
.CODE
MAIN PROC
    MOV AX, @DATA
    MOV DS, AX
    ; 打开输入文件
    MOV AH, 3Dh
    MOV AL, 0 ; 只读模式
    LEA DX, input_file
    INT 21H
    MOV handle_in, AX
    ; 打开输出文件
    MOV AH, 3Dh
    MOV AL, 1 ; 只写模式
    LEA DX, output_file
    INT 21H
    MOV handle_out, AX
    ; 读取文件内容并复制
READ_LOOP:
    MOV AH, 3Fh
    MOV BX, handle_in
    MOV CX, 100
    LEA DX, buffer
    INT 21H
    CMP AX, 0
    JE END_READ
    MOV AH, 40h
    MOV BX, handle_out
    MOV CX, AX
    LEA DX, buffer
    INT 21H
    JMP READ_LOOP
END_READ:
    ; 关闭文件
    MOV AH, 3Eh
    MOV BX, handle_in
    INT 21H
    MOV AH, 3Eh
    MOV BX, handle_out
    INT 21H
    MOV AH, 4CH
    INT 21H
MAIN ENDP
END MAIN

这个程序的执行流程如下:

graph TD;
    A[开始] --> B[初始化数据段寄存器];
    B --> C[打开输入文件];
    C --> D[打开输出文件];
    D --> E[进入读取循环];
    E --> F{是否读取到文件末尾};
    F -- 否 --> G[读取文件内容];
    G --> H[将读取内容写入输出文件];
    H --> E;
    F -- 是 --> I[关闭输入文件];
    I --> J[关闭输出文件];
    J --> K[退出程序];
    K --> L[结束];
7.6 读取MS - DOS命令尾

在MS - DOS中,可以读取命令行输入的参数。通过特定的功能调用,可以获取命令尾的信息,从而实现根据不同的命令行参数执行不同的操作。

7.7 示例:创建二进制文件

以下是一个创建二进制文件的示例代码:

.MODEL SMALL
.STACK 100H
.DATA
    binary_file DB 'binary.bin', 0
    data DB 1, 2, 3, 4, 5
    handle DW ?
.CODE
MAIN PROC
    MOV AX, @DATA
    MOV DS, AX
    ; 打开二进制文件
    MOV AH, 3Dh
    MOV AL, 2 ; 创建并只写模式
    LEA DX, binary_file
    INT 21H
    MOV handle, AX
    ; 写入数据
    MOV AH, 40h
    MOV BX, handle
    MOV CX, 5
    LEA DX, data
    INT 21H
    ; 关闭文件
    MOV AH, 3Eh
    MOV BX, handle
    INT 21H
    MOV AH, 4CH
    INT 21H
MAIN ENDP
END MAIN
8. 总结与展望

通过对32位汇编语言与C/C++的链接以及16位MS - DOS编程的学习,我们掌握了多种编程技巧和方法。在32位编程中,学会了如何将汇编语言代码与C/C++程序结合,调用C库函数实现复杂的功能。在16位MS - DOS编程中,了解了MS - DOS的基本原理、内存组织、中断调用和文件I/O操作等知识。

在实际应用中,我们可以根据具体的需求选择合适的编程方式。例如,对于性能要求极高的部分可以使用汇编语言进行优化,而对于界面和高级功能的实现可以使用C/C++。未来,随着计算机技术的不断发展,虽然16位MS - DOS编程的应用场景逐渐减少,但其中的一些原理和方法仍然具有重要的参考价值。同时,32位汇编语言与C/C++的结合编程在嵌入式系统、驱动开发等领域仍然有着广泛的应用前景。

以下是一个简单的对比表格,展示32位编程和16位MS - DOS编程的一些特点:
| 编程类型 | 地址模式 | 功能调用方式 | 应用场景 |
| ---- | ---- | ---- | ---- |
| 32位编程 | 32位地址 | 调用C库函数、使用高级语言调用约定 | 现代操作系统下的应用程序开发、性能优化 |
| 16位MS - DOS编程 | 16位地址 | 软件中断(如INT 21h) | 早期计算机系统、嵌入式系统开发 |

无论是32位编程还是16位MS - DOS编程,都需要不断地实践和学习,才能更好地掌握相关知识和技能,实现更高效、更复杂的程序。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值