前言:
今天朋友们对“c语言程序开发范例宝典”大体比较讲究,姐妹们都想要剖析一些“c语言程序开发范例宝典”的相关内容。那么小编在网摘上收集了一些有关“c语言程序开发范例宝典””的相关文章,希望同学们能喜欢,看官们一起来了解一下吧!《现代X86汇编语言程序设计》的示范程序在windows操作系统“Core”CPU的操作环境下,采用C++语言嵌套intel风格的MASN汇编语言进行编写,本文拟在Linux操作系统、“Celeron N2840”CPU的环境下,使用C语言嵌套AT&T汇编格式,对书中第一个示范程序ch02_01(中文版第18页)进行改写。改写的程序中变量名称、变量值、程序结构与示范程序一致,其余内容均属原创。文中主要涉及的知识包括:1、如何在C语言中嵌套汇编语言;2、如何使用gdb调试工具;3、C语言中对变量的定义和赋值存放在哪些硬件空间中。程序实现的功能是对变量a、b、c、d进行赋值,并输出a+b+c-d的计算值。
一、在同一文件夹内创建.c、.asm、Makefile文件
$touch ch02_01.c
$touch ch02_01_01.asm
$touch Makefile
说明:书中的汇编程序文件名为ch02_01.asm,若采用这个文件名,在编译时c程序与汇编程序都会产生同名的目标文件ch02_01.o,导致编译失败。因此汇编程序与C程序不能同名。
二、编辑ch02_01.c文件如下:
#include<stdio.h>
#include<inttypes.h> //该头文件的作用是进行转换进制,如果没有转换进制,汇编引用时会被认为是十六进制数
int64_t IntegerAddSub_(int64_t a,int64_t b,int64_t c,int64_t d); //定义即将引用的汇编函数IntegerAddSub_
int main(void)
{
int64_t a;
int64_t b;
int64_t c;
int64_t d;
int result; //由于IntegerAddSub_函数已经转化为十进制,因此result变量不需在转进制
a=10;
b=20;
c=30;
d=18;
result=IntegerAddSub_(a,b,c,d); //引起汇编函数
printf("test1\n %ld\n %ld\n %ld\n %ld\n result=%" PRIi64 "\n",a,b,c,d,result);
//PRIi64两边有空格,表示打印64位有符号数据,i表示有符号,本案例也可换为%ld
a=101;
b=34;
c=5; //原书案例中此数为负数,本例直接采用负数会出现计算错误,因时间关系,以后在继续研究如何采用负值计算
d=18;
result=IntegerAddSub_(a,b,c,d); //再次引用汇编函数
printf("test2\n %ld\n %ld\n %ld\n %ld\n result=%" PRIi64 "\n",a,b,c,d,result);
return 0;
}
说明:书中的示范程序可以对有符号的负数进行计算,由于时间关系,这次改写尚不能满足负数计算,暂时只能进行无符号的正整数计算。
三、编辑ch02_01_01.asm文件(以下先展示错误代码,错误代码由书中代码直接改写,后面再说明如何调试、修正)
.text //以下为代码段
.global IntegerAddSub_ //定义标签IntegerAddSub_,该标签处的代码将被C程序作为函数引用
IntegerAddSub_: //标签入口
movq %rcx,%rax //将寄存器rcx的值赋予rax,意味着第一个变量a的值在rcx中
addq %rdx,%rax //将寄存器rdx与rax的值相加,并将和值存入rax中,意味着第二个变量b存放在rdx中
addq %r8d,%rax //同上
subq %r9d,%rax //将寄存器rax的值减去r9d中,计算值存入rax
ret
四、编辑Makefile文件
ch02_01:ch02_01_01.o ch02_01.c //表示可执行程序ch02_01由ch02_01_01.o和ch02_01.c共同形成
gcc ch02_01_01.o ch02_01.c -o ch02_01 //用gcc编译、链接ch02_01_01.o和ch02_01.c,形成可执行文件ch02,主要开头缩进要用“TAB”键,不能用空格键。
ch02_01_01.o:ch02_01_01.asm //表示目标文件ch02_01_01由汇编程序ch02_01_01.asm形成
as ch02_01_01.asm -o ch02_01_01.o //用as编译汇编程序ch02_01_01.asm,形成目标文件ch02_01_01.o
五、编译、运行可执行文件
$make
$./ch02_01
六、调试
步骤五中程序可以正常通过编译,但是执行可执行文件后产生错误的计算结果,用gdb调试,查看错误原因。
$gdb ./ch02_01
(gdb)break main //在C程序main 函数处打断
(gdb)run //运行程序,运行至main函数入口处会被中断
(gdb)disas //显示汇编代码
显示的汇编代码如下:
push %rbp //在为变量提供地址前,将rbp的地址压栈保存
mov %rsp,%rbp //将rsp的地址赋值于rbp
sub $0x30,%rsp
movq $0xa,-0x8(%rbp) //rbp地址偏移-0x8的位置处存放数值0xa,即十进制10
movq $0x14,-0x10(%rbp) //rbp地址偏移-0x10的位置处存放数值0x14,即十进制20
movq $0x1e,-0x18(%rbp) //rbp地址偏移-0x18的位置处存放数值0x1e,即十进制30
movq $0x12,-0x20(%rbp) //rbp地址偏移-0x20的位置处存放数值0x12,即十进制18
mov -0x20(%rbp),%rcx //将rbp地址偏移-0x20处的数据赋值于rcx
mov -0x18(%rbp),%rdx //将rbp地址偏移-0x18处的数据赋值于rdx
mov -0x10(%rbp),%rsi //将rbp地址偏移-0x10处的数据赋值于rsi
mov -0x8(%rbp),%rax //将rbp地址偏移-0x8处的数据赋值于rax
mov %rax,%rdi
call ...<IntegerAddSub_> //调用IntegerAddSub_函数
说明:由上可知变量a、b、c、d的赋值分别存放在寄存器rax、rsi、rdx、rcx中,变量赋值位置与书中代码展示的分别位于ecx、edx、r8d、r9d不同,原因可能是因为使用的CPU不同,所以CPU指令集不同。
七、修正汇编程序如下
.text
.global IntegerAddSub_
IntegerAddSub_:
addq %rsi,%rax
addq %rdx,%rax
subq %rcx,%rax
ret
八、编译、运行
$make
$./ch02_01
最终产生正确计算结果。
标签: #c语言程序开发范例宝典