⑴ 想自学汇编语言。请问是否需要c语言的基础
学习汇编和C语言没有必然的联系,汇编是学习单片机或计算机的内部硬件的结构,汇编开发需要对各种寻址方式以其一些指令了解,必须了解指令和芯片的时序,控制信号,地址,而C语言开发只需要了解各种芯片的时序、控制信号、地址,用这些知识来编写函数就可以了。
所以说学汇编要多练习,在硬件平台上练习就可以了,而且汇编出错概率多,调试比较麻烦,要养成好的程序编写习惯,掌握基本的方法;
而C只要编译成功,时序,控制信号,地址正确,就是可以用的。
⑵ 汇编语言和C语言混合编程时,它们之间是如何传递参数的请用具体例子说明
对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~R3来进行参数传递,当参数超过4个时,还可以使用数据栈来传递参数. 在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器R0,R1,R2,R3; 如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈.
⑶ C语言与汇编语言有什么区别与联系
C语言当然一种
汇编是对于各种单片机特定的编写语句,对于不同总类的也不同
C语言是一种通用语言,虽然不同总类的单片机的汇编语言不同,但他们大部分可以用C语言编写,建议学好C语言
⑷ 51单片机编程时汇编和C语言如何结合
在keil工程栏中右键.c文件选择OptionsforFile
然后把这2个勾上
⑸ 为什么要C和汇编混合编程
依我的看法,上面两位说的都不对:
一楼:汇编语言是低级语言直接往硬件的寄存器里写入东西:
其实C语言和汇编语言都一样,都会编译成二进制文件机器语言烧录到单片机中,所以没有一楼所说的区别
二楼:在C中也不会有数据损失的,如果你定义好数据类型的话。
单片机中是将C编译成汇编,然后根据汇编编译成机器语言---可执行文件;
其实单片机中经常在C中嵌入汇编,主要是因为时间的关系,因为一条汇编语言执行的时间都是可以知道的,而C指令执行的时间是不可知的,因为C编译器不一样,编译后的汇编也是不一样的,如果用汇编就不会有这种情况。所以在一些精确延时的场合,要嵌入汇编
另外,C的可移植性较强,不同的单片机里,功能要求相同的可以直接拿去用
总之,汇编是程序员可以自己翻译成机器语言的,所以单片机的运行是可以完全掌握的
⑹ 如何在C语言中嵌入汇编
在 Visual C++ 中使用内联汇编- -
使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤。在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工具。这里,我们就以 Visual Studio .NET 2003 为背景,介绍在 Visual C++ 中使用内联汇的相关知识(如果是早期的版本,可能会有些许出入)。
内联汇编代码可以使用 C/C++ 变量和函数,因此它能非常容易地整合到 C/C++ 代码中。它能做一些对于单独使用 C/C++ 来说非常笨重或不可能完成的任务。
一、 优点
使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤。在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工具。这里,我们就以 Visual Studio .NET 2003 为背景,介绍在 Visual C++ 中使用内联汇的相关知识(如果是早期的版本,可能会有些许出入)。
内联汇编代码可以使用 C/C++ 变量和函数,因此它能非常容易地整合到 C/C++ 代码中。它能做一些对于单独使用 C/C++ 来说非常笨重或不可能完成的任务。
内联汇编的用途包括:
使用汇编语言编写特定的函数;
编写对速度要求非常较高的代码;
在设备驱动程序中直接访问硬件;
编写 naked 函数的初始化和结束代码。
二、 关键字
使用内联汇编要用到 __asm 关键字,它可以出现在任何允许 C/C++ 语句出现的地方。我们来看一些例子:
简单的 __asm 块:
__asm
{
MOV AL, 2
MOV DX, 0xD007
OUT AL, DX
}
在每条汇编指令之前加 __asm 关键字:
__asm MOV AL, 2
__asm MOV DX, 0xD007
__asm OUT AL, DX
因为 __asm 关键字是语句分隔符,所以可以把多条汇编指令放在同一行:
__asm MOV AL, 2 __asm MOV DX, 0xD007 __asm OUT AL, DX
显然,第一种方法与 C/C++ 的风格很一致,并且把汇编代码和 C/C++ 代码清楚地分开,还避免了重复输入 __asm 关键字,因此推荐使用第一种方法。
不像在 C/C++ 中的"{ }",__asm 块的"{ }"不会影响 C/C++ 变量的作用范围。同时,__asm 块可以嵌套,而且嵌套也不会影响变量的作用范围。
为了与低版本的 Visual C++ 兼容,_asm 和 __asm 具有相同的意义。另外,Visual C++ 支持标准 C++ 的 asm 关键字,但是它不会生成任何指令,它的作用仅限于使编译器不会出现编译错误。要使用内联汇编,必须使用 __asm 而不是 asm 关键字。
三、 汇编语言
1. 指令集
内联汇编支持 Intel Pentium 4 和 AMD Athlon 的所有指令。更多其它处理器的指令可以通过 _EMIT 伪指令来创建(_EMIT 伪指令说明见下文)。
2. MASM 表达式
在内联汇编代码中,可以使用所有的 MASM 表达式(MASM 表达式是指用来计算一个数值或一个地址的操作符和操作数的组合)。
3. 数据指示符和操作符
虽然 __asm 块中允许使用 C/C++ 的数据类型和对象,但它不能使用 MASM 指示符和操作符来定义数据对象。这里特别指出,__asm 块中不允许 MASM 中的定义指示符(DB、DW、DD、DQ、DT 和 DF),也不允许使用 DUP 和 THIS 操作符。MASM 中的结构和记录也不再有效,内联汇编不接受 STRUC、RECORD、WIDTH 或者 MASK。
4. EVEN 和 ALIGN 指示符
尽管内联汇编不支持大多数 MASM 指示符,但它支持 EVEN 和 ALIGN。当需要的时候,这些指示符在汇编代码里面加入 NOP 指令(空操作)使标号对齐到特定边界。这样可以使某些处理器取指令时具有更高的效率。
5. MASM 宏指示符
内联汇编不是宏汇编,不能使用 MASM 宏指示符(MACRO、REPT、IRC、IRP 和 ENDM)和宏操作符(<>、!、&、% 和 .TYPE)。
6. 段
必须使用寄存器而不是名称来指明段(段名称"_TEXT"是无效的)。并且,段跨越必须显式地说明,如 ES:[EBX]。
7. 类型和变量大小
在内联汇编中,可以用 LENGTH、SIZE 和 TYPE 来获取 C/C++ 变量和类型的大大小。
* LENGTH 操作符用来取得 C/C++ 中数组的元素个数(如果不是一个数组,则结果为 1)。
* SIZE 操作符可以获取 C/C++ 变量的大小(一个变量的大小是 LENGTH 和 TYPE 的乘积)。
* TYPE 操作符可以返回 C/C++ 类型和变量的大小(如果变量是一个数组,它得到的是数组中单个元素的大小)。
例如,程序中定义了一个 8 维的整数型变量:
int iArray[8];
下面是 C 和汇编表达式中得到的 iArray 及其元素的相关值:
__asm C Size
LENGTH iArray sizeof(iArray)/sizeof(iArray[0]) 8
SIZE iArray sizeof(iArray) 32
TYPE iArray sizeof(iArray[0]) 4
8. 注释
内联汇编中可以使用汇编语言的注释,即";"。例如:
__asm MOV EAX, OFFSET pbBuff ; Load address of pbBuff
因为 C/C++ 宏将会展开到一个逻辑行中,为了避免在宏中使用汇编语言注释带来的混乱,内联汇编也允许使用 C/C++ 风格的注释。
9. _EMIT 伪指令
_EMIT 伪指令相当于 MASM 中的 DB,但是 _EMIT 一次只能在当前代码段(.text 段)中定义一个字节。例如:
__asm
{
JMP _CodeLabel
_EMIT 0x00 ; 定义混合在代码段的数据
_EMIT 0x01
_CodeLabel: ; 这里是代码
_EMIT 0x90 ; NOP指令
}
10. 寄存器使用
一般来说,不能假定某个寄存器在 __asm 块开始的时候有已知的值。寄存器的值将不能保证会从 __asm 块保留到另外一个 __asm 块中。
如果一个函数声明为 __fastcall 调用方式,则其参数将通过寄存器而不是堆栈来传递。这将会使 __asm 块产生问题,因为函数无法被告知哪个参数在哪个寄存器中。如果函数接收了 EAX 中的参数并立即储存一个值到 EAX 中的话,原来的参数将丢失掉。另外,在所有声明为 __fastcall 的函数中,ECX 寄存器是必须一直保留的。为了避免以上的冲突,包含 __asm 块的函数不要声明为 __fastcall 调用方式。
提示:如果使用 EAX、EBX、ECX、EDX、ESI 和 EDI 寄存器,你不需要保存它。但如果你用到了 DS、SS、SP、BP 和标志寄存器,那就应该用 PUSH 保存这些寄存器。
提示:如果程序中改变了用于 STD 和 CLD 的方向标志,必须将其恢复到原来的值。
四、 使用 C/C++ 元素
1. 可用的 C/C++ 元素
C/C++ 与汇编语言可以混合使用,在内联汇编中可以使用 C/C++ 变量以及很多其它的 C/C++ 元素,包括:
符号,包括标号、变量和函数名;
常量,包括符号常量和枚举型成员;
宏定义和预处理指示符;
注释,包括"/**/"和"//";
类型名,包括所有 MASM 中合法的类型;
typedef 名称,通常使用 PTR 和 TYPE 操作符,或者使用指定的的结构或枚举成员。
在内联汇编中,可以使用 C/C++ 或汇编语言的基数计数法。例如,0x100 和 100H 是相等的。
2. 操作符使用
内联汇编中不能使用诸如"<<"一类的 C/C++ 操作符。但是,C/C++ 和 MASM 共有的操作符(比如"*"和"[]"操作符),都被认为是汇编语言的操作符,是可以使用的。举个例子:
int iArray[10];
__asm MOV iArray[6], BX ; Store BX at iArray + 6 (Not scaled)
iArray[6] = 0; // Store 0 at iArray+12 (Scaled)
提示:在内联汇编中,可以使用 TYPE 操作符使其与 C/C++ 一致。比如,下面两条语句是一样的:
__asm MOV iArray[6 * TYPE int], 0 ; Store 0 at iArray + 12
iArray[6] = 0; // Store 0 at iArray + 12
3. C/C++ 符号使用
在 __asm 块中可以引用所有在作用范围内的 C/C++ 符号,包括变量名称、函数名称和标号。但是不能访问 C++ 类的成员函数。
下面是在内联汇编中使用 C/C++ 符号的一些限制:
每条汇编语句只能包含一个 C/C++ 符号。在一条汇编指令中,多个符号只能出现在 LENGTH、TYPE 或 SIZE 表达式中。
在 __asm 块中引用函数必须先声明。否则,编译器将不能区别 __asm 块中的函数名和标号。
在 __asm 块中不能使用对于 MASM 来说是保留字的 C/C++ 符号(不区分大小写)。MASM 保留字包含指令名称(如 PUSH)和寄存器名称(如 ESI)等。
在 __asm 块中不能识别结构和联合标签。
4. 访问 C/C++ 中的数据
内联汇编的一个非常大的方便之处是它可以使用名称来引用 C/C++ 变量。例如,如果 C/C++ 变量 iVar 在作用范围内:
__asm MOV EAX, iVar ; Stores the value of iVar in EAX
如果 C/C++ 中的类、结构或者枚举成员具有唯一的名称,则在 __asm 块中可以只通过成员名称来访问(省略"."操作符之前的变量名或 typedef 名称)。然而,如果成员不是唯一的,你必须在"."操作符之前加上变量名或 typedef 名称。例如,下面的两个结构都具有 SameName 这个成员变量:
struct FIRST_TYPE
{
char *pszWeasel;
int SameName;
};
struct SECOND_TYPE
{
int iWonton;
long SameName;
};
如果按下面方式声明变量:
struct FIRST_TYPE ftTest;
struct SECOND_TYPE stTemp;
那么,所有引用 SameName 成员的地方都必须使用变量名,因为 SameName 不是唯一的。另外,由于上面的 pszWeasel 变量具有唯一的名称,你可以仅仅使用它的成员名称来引用它:
__asm
{
MOV EBX, OFFSET ftTest
MOV ECX, [EBX]ftTest.SameName ; 必须使用"ftTest"
MOV ESI, [EBX]. pszWeasel ; 可以省略"ftTest"
}
提示:省略变量名仅仅是为了书写代码方便,生成的汇编指令还是一样的。
5. 用内联汇编写函数
如果用内联汇编写函数的话,要传递参数和返回一个值都是非常容易的。看下面的例子,比较一下用独立汇编和内联汇编写的函数:
; PowerAsm.asm
; Compute the power of an integer
PUBLIC GetPowerAsm
_TEXT SEGMENT WORD PUBLIC 'CODE'
GetPowerAsm PROC
PUSH EBP ; Save EBP
MOV EBP, ESP ; Move ESP into EBP so we can refer
; to arguments on the stack
MOV EAX, [EBP+4] ; Get first argument
MOV ECX, [EBP+6] ; Get second argument
SHL EAX, CL ; EAX = EAX * (2 ^ CL)
POP EBP ; Restore EBP
RET ; Return with sum in EAX
GetPowerAsm ENDP
_TEXT ENDS
END
C/C++ 函数一般用堆栈来传递参数,所以上面的函数中需要通过堆栈位置来访问它的参数(在 MASM 或其它一些汇编工具中,也允许通过名称来访问堆栈参数和局部堆栈变量)。
下面的程序是使用内联汇编写的:
// PowerC.c
#include
int GetPowerC(int iNum, int iPower);
int main()
{
printf("3 times 2 to the power of 5 is %d\n", GetPowerC( 3, 5));
}
int GetPowerC(int iNum, int iPower)
{
__asm
{
MOV EAX, iNum ; Get first argument
MOV ECX, iPower ; Get second argument
SHL EAX, CL ; EAX = EAX * (2 to the power of CL)
}
// Return with result in EAX
}
使用内联汇编写的 GetPowerC 函数可以通过参数名称来引用它的参数。由于 GetPowerC 函数没有执行 C 的 return 语句,所以编译器会给出一个警告信息,我们可以通过 #pragma warning 禁止生成这个警告。
内联汇编的其中一个用途是编写 naked 函数的初始化和结束代码。对于一般的函数,编译器会自动帮我们生成函数的初始化(构建参数指针和分配局部变量等)和结束代码(平衡堆栈和返回一个值等)。使用内联汇编,我们可以自己编写干干净净的函数。当然,此时我们必须自己动手做一些有关函数初始化和扫尾的工作。例如:
void __declspec(naked) MyNakedFunction()
{
// Naked functions must provide their own prolog.
__asm
{
PUSH EBP
MOV ESP, EBP
SUB ESP, __LOCAL_SIZE
}
.
.
.
// And we must provide epilog.
__asm
{
POP EBP
RET
}
}
6. 调用 C/C++ 函数
内联汇编中调用声明为 __cdecl 方式(默认)的 C/C++ 函数必须由调用者清除参数堆栈,下面是一个调用 C/C++ 函数例子:
#include
char szFormat[] = "%s %s\n";
char szHello[] = "Hello";
char szWorld[] = " world";
void main()
{
__asm
{
MOV EAX, OFFSET szWorld
PUSH EAX
MOV EAX, OFFSET szHello
PUSH EAX
MOV EAX, OFFSET szFormat
PUSH EAX
CALL printf
// 压入了 3 个参数在堆栈中,调用函数之后要调整堆栈
ADD ESP, 12
}
}
提示:参数是按从右往左的顺序压入堆栈的。
如果调用 __stdcall 方式的函数,则不需要自己清除堆栈。因为这种函数的返回指令是 RET n,会自动清除堆栈。大多数 Windows API 函数均为 __stdcall 调用方式(仅除 wsprintf 等几个之外),下面是一个调用 MessageBox 函数的例子:
#include
TCHAR g_tszAppName[] = TEXT("API Test");
void main()
{
TCHAR tszHello[] = TEXT("Hello, world!");
__asm
{
PUSH MB_OK OR MB_ICONINFORMATION
PUSH OFFSET g_tszAppName ; 全局变量用 OFFSET
LEA EAX, tszHello ; 局部变量用 LEA
PUSH EAX
PUSH 0
CALL DWORD PTR [MessageBox] ; 注意这里不是 CALL MessageBox,而是调用重定位过的函数地址
}
}
提示:可以不受限制地访问 C++ 成员变量,但是不能访问 C++ 的成员函数。
7. 定义 __asm 块为 C/C++ 宏
使用 C/C++ 宏可以方便地把汇编代码插入到源代码中。但是这其中需要额外地注意,因为宏将会扩展到一个逻辑行中。
为了不会出现问题,请按以下规则编写宏:
使用花括号把 __asm 块包围住;
把 __asm 关键字放在每条汇编指令之前;
使用经典 C 风格的注释("/* comment */"),不要使用汇编风格的注释("; comment")或单行的 C/C++ 注释("// comment");
举个例子,下面定义了一个简单的宏:
#define PORTIO __asm \
/* Port output */ \
{ \
__asm MOV AL, 2 \
__asm MOV DX, 0xD007 \
__asm OUT DX, AL \
}
乍一看来,后面的三个 __asm 关键字好像是多余的。其实它们是需要的,因为宏将被扩展到一个单行中:
__asm /* Port output */ { __asm MOV AL, 2 __asm MOV DX, 0xD007 __asm OUT DX, AL }
从扩展后的代码中可以看出,第三个和第四个 __asm 关键字是必须的(作为语句分隔符)。在 __asm 块中,只有 __asm 关键字和换行符会被认为是语句分隔符,又因为定义为宏的一个语句块会被认为是一个逻辑行,所以必须在每条指令之前使用 __asm 关键字。
括号也是需要的,如果省略了它,编译器将不知道汇编代码在哪里结束,__asm 块后面的 C/C++ 语句看起来会被认为是汇编指令。
同样是由于宏展开的原因,汇编风格的注释("; comment")和单行的 C/C++ 注释("// commen")也可能会出现错误。为了避免这些错误,在定义 __asm 块为宏时请使用经典 C 风格的注释("/* comment */")。
和 C/C++ 宏一样 __asm 块写的宏也可以拥有参数。和 C/C++ 宏不一样的是,__asm 宏不能返回一个值,因此,不能使用这种宏作为 C/C++ 表达式。
不要不加选择地调用这种类型的宏。比如,在声明为 __fastcall 的函数中调用汇编语言宏可能会导致不可预料的结果(请参看前文的说明)。
8. 转跳
可以在 C/C++ 里面使用 goto 转跳到 __asm 块中的标号处,也可以在 __asm 块中转跳到 __asm 块里面或外面的标号处。__asm 块内的标号是不区分大小写的(指令、指示符等也是不区分大小写的)。例如:
void MyFunction()
{
goto C_Dest; /* 正确 */
goto c_dest; /* 错误 */
goto A_Dest; /* 正确 */
goto a_dest; /* 正确 */
__asm
{
JMP C_Dest ; 正确
JMP c_dest ; 错误
JMP A_Dest ; 正确
JMP a_dest ; 正确
a_dest: ; __asm 标号
}
C_Dest: /* C/C++ 标号 */
return;
}
不要使用函数名称当作标号,否则将转跳到函数中执行,而不是标号处。例如,由于 exit 是 C/C++ 的函数,下面的转跳将不会到 exit 标号处:
; 错误:使用函数名作为标号
JNE exit
.
.
.
exit:
.
.
.
美元符号"$"用于指定当前指令位置,常用于条件跳转中,例如:
JNE $+5 ; 下面这条指令的长度是 5 个字节
JMP _Label
NOP ; $+5,转跳到了这里
.
.
.
_Label:
.
.
.
五、在 Visual C++ 工程中使用独立汇编
内联汇编代码不易于移植,如果你的程序打算在不同类型的机器(比如 x86 和 Alpha)上运行,你可能需要在不同的模块中使用特定的机器代码。这时候你可以使用 MASM(Microsoft Macro Assembler),因为 MASM 支持更多方便的宏指令和数据指示符。
这里简单介绍一下在 Visual Studio .NET 2003 中调用 MASM 编译独立汇编文件的步骤。
在 Visual C++ 工程中,添加按 MASM 的要求编写的 .asm 文件。在解决方案资源管理器中,右击这个文件,选择"属性"菜单项,在属性对话框中,点击"自定义生成步骤",设置如下项目:
命令行:ML.exe /nologo /c /coff "-Fo$(IntDir)\$(InputName).obj" "$(InputPath)"
输出:$(IntDir)\$(InputName).obj
如果要生成调试信息,可以在命令行中加入"/Zi"参数,还可以根据需要生成 .lst 和 .sbr 文件。
如果要在汇编文件中调用 Windows API,可以从网上下载 MASM32 包(包含了 MASM 汇编工具、非常完整的 Windows API 头文件/库文件、实用宏以及大量的 Win32 汇编例子等)。相应地,应该在命令行中加入"/I X:\MASM32\INCLUDE"参数指定 Windows API 汇编头文件(.inc)的路径。MASM32 的主页是:http://www.masm32.com,里面可以下载最新版本的 MASM32 包。
⑺ 谁知道汇编与c语言怎样互相调用啊,还有怎样在linux编译啊,如果可以的话,就写个简单的程序介绍。谢谢哈
对于C和汇编语言的接口主要有两个问题需要解决。
一、调用者与被调用者的参数传递
这种数据传递通过堆栈完成,在执行调用时从调用程序参数表中的最后一个参数开始 ,自动依次压入堆栈;将所有参数压入堆栈后,再自动将被调用程序执行结束后的返回地址 (断点)压入堆栈,以使被调程序结束后能返回主调程序的正确位置而继续执行。例如一调用名为add汇编程序模块的主函数:main( ){...... add(dest,op1,op2,flages);......}。在此例中对主函数进行反汇编,主函数在调用add函数前自动组织的堆栈。
.
.
.
lea 0xfffffffe8(%ebp),%eax #flages数组的首地址入栈
push %eax
pushl 0xfffffff8(%ebp) #OP2入栈
pushl 0xfffffffc(%ebp) #OP1 入栈
pushl 0xfffffff0(%ebp) #dest地址入栈
call 0x80483f0 <add> #调用add函数
.
.
进入汇编子程序后,为了能正确获取主调程序并存入堆栈中的数据,被调的汇编子程序先后要做如下一些工作:
1、 保存esp的副本
进入汇编子程序后,子程序中免不了要有压栈和出栈的操作,故ESP时刻在变化。为了能用 ESP访问堆栈中的参数,安全办法是一进入子程序后,先为ESP制副本,以后对传递参数的访问 都用副本进行。一般可用EBP保存ESP,如:
push %ebp
mov %ebp,%esp
2、保留数据空间
如果汇编子程序中需要一些局部数据,可以简单地减小ESP的值,以便在栈空间中保留出一段存贮区,用于存放局部数据,该区域须在子程序结束后恢复。如下语句可以保留一个局部数据区:
push %ebp
mov %ebp ,%esp
subl space,%esp;设space=4
movl $0x0,%ebp
movl $0x0,-2(%ebp)
如上语句段中,space是局部数据的总字节数。在以后的应用中,由于ESP是变化的,而 EBP是 固定的,用负偏移量可以存取局部变量。上例利用EBP及偏移量,将两个字的局部数 据初始化为0。
3、保留寄存器值
如果在被调子程序中用到ESI、EDI等其它寄存器,则应先把它们压入堆栈,以保留寄存器原值 。例如,下例就是将ESI和EDI寄存器的值压栈:
pushl %ebp
movl %ebp ,%esp
subl $space ,%esp,
pushl %esi
pushl %edi
4、获取传递参数
作完了1~3步的操作后,结合上面C程序传送参数这一例子,现在栈结构如图二所示。
由此可见,EBP保留了ESP在参数传递完并将EBP压栈后的一个副本,利用EBP可以很方便地访问各参数。现假设各参数都是2字节的整数值,在小模式编译方式共占用2个字节。如果要将传递的参数op1、op2取出,并分别赋给ebx、ecx寄存器,可由下列语句完成这一功能:
movl 0x8(%ebp),%eax
movl 0xc(%ebp),%ecx
5、子程序返回值
当子程序的执行结果需要返回时,根据返回值的字长,C按如下约定接收返回值:1字节在AL 寄存器中;2字节在EAX寄存器中;4字节则高位部分在EDX中、低位部分在EAX寄存器中。C可从这些寄存器中取出返回值。
6、退出汇编子程序
结束汇编子程序的步骤如下:
1) 若ESS、EDS、ESI或EDI已被压栈,则需按保存它们的相反顺序弹出它们。
2) 若在过程开始时分配了局部数据空间,则以指令 mov %esp和%ebp 恢复%esp。
3) 以指令pop %ebp 恢复%ebp ,该步是必须的。或者可以用leave语句来恢复%ebp 。它相当于movl %ebp, %esp; popl %ebp
4) 最后以ret结束汇编程序。
二、 说明和建立调用者与被调用者间的连系
为了建立调用与被调用模块间的连接关系,被调用的汇编程序应用global,说明其可被外部模块调用;而调用程序则应预先说明要引用的外部模块名。下面通过我的例子进行说明,该例是C调用add0的汇编子程序。程序清单如下:
/* add.c */
#include <stdio.h>
extern void add(int *dest,int op1,int op2,short int*flages);
/*声明调用外部的汇编函数*/
int main(void){
int op1,op2,result;
int *dest=&result;
short int flages[4]={0,0,0,0};
printf("please enter two soure operater:");
scanf("%x%x",&op1,&op2);
add(dest,op1,op2,flages);/*调用add0函数*/
printf("The result of ADD is :%x/n flages N(negative) Z(zero) C(carry) V(overflow:%d,%d,%d,%d/n",*dest,flages[3],flages[2],flages[1],flages[0]);
return 0;
}
#add.s
.text
.align 2
.global add
.type add,function
#定义add为外部可调用的函数
add:
push %ebp #ebp寄存器内容压栈,保存add函数的上级调用函数的栈基地址
mov %esp,%ebp #esp值赋给ebp,设置add函数的栈基地址
mov 0x8(%ebp),%edx
mov 0x10(%ebp),%eax
add 0xc(%ebp),%eax
mov %eax,(%edx)
mov 0x14(%ebp),%eax
jo OF
C:
jc CF
S:
js SF
jz ZF
jmp out
OF:
movw $0x1,(%eax)
jmp C
CF:
movw $0x1,0x2(%eax)
jmp S
SF:
movw $0x1,0x6(%eax)
movw $0x0,0x4(%eax)
jmp out
ZF:
movw $0x1,0x4(%eax)
movw $0x0,0x6(%eax)
out:
leave #将ebp值赋给esp,pop先前栈内的上级函数栈的基地址给#ebp,恢复原栈基址
ret #add函数返回,回到上级的调用函数
其中.text 标志一个代码段的开始,这是AT&T的段格式;global add;/n
type add,function说明add是公用的,可以由外部其它单独编译模块调用。
将C源程序以文件名add.c存盘,汇编语言源程序以add.s 存盘;通过MAKE进行编译和连接连接代码如下:
all: myadd
myadd: adds.o addc.o
gcc –o myadd adds.o adc.o
adds.o: add.s
as –o adds.o add.s
addc.o: add.c
gcc –g –o addc.o add.c
由上可见,在C中调用汇编模块很方便。所以我们在实际软件开发中,可以采用混合编程的技术,从而尽可能利用各语言的优势。既满足实际问题的需要,又简化设计过程,达到事半功倍的效果。
⑻ 编程和C语言有联系和区别
编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程。为了使计算机能够理解人的意图,人类就必须要将需解决的问题的思路、方法、和手段通过计算机能够理解的形式告诉计算机,使得计算机能够根据人的指令一步一步去工作,完成某种特定的任务。这种人和计算机之间交流的过程就是编程。 C语言是Combined Language(组合语言)的中英混合简称。是一种计算机程序设计语言。它既具有高级语言的特点,又具有汇编语言的特点。它可以作为工作系统设计语言,编写系统应用程序,也可以作为应用程序设计语言,编写不依赖计算机硬件的应用程序。因此,它的应用范围广泛,不仅仅是在软件开发上,而且各类科研都需要用到C语言,具体应用比如单片机以及嵌入式系统开发。 编程就是程序编辑,简称编程。C语言是编程的一种手段,我们编程我们也可以用VB,C++,java等。希望楼主能明白。
⑼ 汇编与C语言
汇编语言是直接针对硬件进行操作的语言。
适合编写控制类型的程序。
但是很多人,学习硬件比较吃力,那就去学习C语言吧。
C语言是脱离硬件的,可移植性强,比较适合编写算法为主的程序。
但是数据类型、指针等概念,学习起来比学硬件还难。
⑽ 汇编语言和C语言有什么联系吗,他们分别是干什么的
任何高级语言(包括c),最终都将转化(通过编译或解释)为直接表示机器指令的汇编语言来执行。
每条汇编语句基本上指向一条机器指令。
C语言作为一个中级语言,相对于其他语言,和汇编较为接近,在汇编和c之间相互转变较为容易。