汇编语言 数组排序键盘输入6个0-100整数到数组中

2005年1月 VB大版内专家分月排行榜第二
2005年3月 VB大版内专家分月排行榜第三
2005年1月 VB大版内专家分月排行榜第二
2005年3月 VB大版内专家分月排行榜第三
2005年1月 VB大版内专家分月排行榜第二
2005年3月 VB大版内专家分月排行榜第三
匿名用户不能发表回复!|c语言,如何将键盘输入的n个整数存入一个数组中,n的大小未知_百度知道
c语言,如何将键盘输入的n个整数存入一个数组中,n的大小未知
我有更好的答案
printf(&quot,用回车结束输入;
scanf(&%d&;);\n');
for(j=0;j&lt,&a[i++]);
}while(getchar()!=',a[j]);
if(j%5==0)
printf(&\n&)
printf(&%3d&).h&void main()
int a[50];
int i=0,j=0;
printf(&quot这样来实现#include&):\n&quot,中间用空格间隔;请输入数据;\n&i
采纳率:42%
0结尾一样,一个程序的确定性是程序运行的前提,计算机只能认识你输入的东西,你要告诉它你结束了,就要让它从你的输入中找到记号你要把n个整数依次存入数组,有多种方法,如果一个一个输入,循环的终止条件必定要设立一个标志位的,就像你输入一个字符串要有&#92
请问大神,除了一个一个输入,还有什么办法?
额。我不是大神,就是个菜鸟。。。昨天写得有点快了写错了,呵呵,我意思是输入到内存中是有多种方法,比如可以弄到链表里面等等,引起歧义不好意思。我是不知道你的本意是什么,如果想在程序中随时随地写入一个值,但是不想重复写什么scanf等语句,可以把输入一个数作为一个单独的功能,写一个void InputANum(int *Source, unsigned int Position, int input); 如果你要输入一个数到数组里面就调用一下这个函数,在程序的各个地方都可以方便的把数据放到数组里面,这个也是模仿一下链表的新建节点的操作。希望可以帮助到你
int a[100];//看你的需要来确定数组大小int i,n;(n要是确定的数就不要定义不要输入
直接用)scanf(&%d&,&n);for(i=0;i&n;i++)scanf(&%d&,&a[i]);
你这个n未知的话,那个for就是死循环了啊,我试了,它会一直让我输入。。。
当你知道n是什么的时候,先输入n啊不确定n的组
有数字个数定的话int i=0,k;while(scanf(&%d&,&k)!=EOF){a[i]=k;i++;}输入结束时按ctrl+z就行了
我运行试了一下,不行诶,应该是scanf(&%d&,&k)!=EOF的问题,一般都是(c=getchar())!=EOF,scanf的话,应该是没有字符就停吧,读不到EOF
#include &stdio.h&int main(){ static int i=0,a[20];
while(scanf(&%d&,&k)!=EOF) {
i++; } for(k=0;k&i;k++)
printf(&%d &,a[k]); return 0;}测试数据
1 2 3 4crtl+z输出
1 2 3 4 你试试
记得输完数据先回车
指针,动态分配
1、可以预先定义足够大的数组(浪费在所难免),然后将键盘输入的整数存入数组,并累计数据个数。...................................................................int i,n = 0,a[1024]while(1) {
printf(&输入整数(q: 结束输入过程)%d : &, n + 1);
if(scanf(&&,&a[n]) != 1)
++n;}for(i = 0; i & ++i) printf(&%d &,a[i]);printf(&\n&);.............................................................2、动态申请空间.................................................................#include &stdlib.h&int i,n,*a;printf(&数据个数 : &);scanf(&%d&,&n);a = (int *)malloc(n * sizeof(int));for(ii = 0; i & ++i) {
printf(&数%d = &,i + 1);
scanf(&%d&,&a[i]);}for(i = 0; i & ++i) printf(&%d &,a[i]);printf(&\n&);......................................................... // 程序结束前要释放动态内存
#include &stdio.h&#define MAXN 1000int main (){ int num[MAXN]; int id = 0; while (scanf(&%d&,&num[id])!=EOF) {
for(i=0;i&i++) {
printf(&%d &,num[i]); } printf(&\n&); return 0;}可以采用文件结束为结束位置,输入ctrl+z,回车结束输入n个数
不行的,你把它放在编译器里试试吧,编译都通不过,将i的声明移到开头,编译倒是通过了,但运行无结果,就是那!=EOF有问题,这在getchar中才可以这样用
其他7条回答
为您推荐:
其他类似问题
您可能关注的内容
键盘输入的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。欢迎加入我们,一同切磋技术。 &
用户名: &&&
密 码: &
共有 17848 人关注过本帖
标题:. 编写程序,从键盘输入 10 个整数存储到数组中,运用冒泡排序法将 10 个数 ...
来 自:江西樟树
等 级:新手上路
帖 子:16
结帖率:75%
&&问题点数:0&&回复次数:7&&&
. 编写程序,从键盘输入 10 个整数存储到数组中,运用冒泡排序法将 10 个数按升序排序并输出,求指错
#include&stdio.h&
int a[10];
int i,j,t;
for(i=0;i&10;i++)
scanf(&%d&,&a[i]);
printf(&\n&);
&for(j=0;j&9;j++)
&&&for(i=0;i&9-j;i++)
&&&if(a[i]&a[i+1]);
&&&t=a[i];
&&&a[i]=a[i+1];
&&&a[i+1]=t;
&for(i=0;i&10;i++)
&printf(&%d&,a[i]);
搜索更多相关主题的帖子:
来 自:陕西
等 级:业余侠客
帖 子:127
专家分:216
如果把数组中的元素初始定义就可以了。
来 自:江西樟树
等 级:新手上路
帖 子:16
等 级:论坛游民
帖 子:31
专家分:48
#include&stdio.h&
&int a[10];
&int i,j,t;
&for(i=0;i&10;i++)
&scanf(&%d&,&a[i]);
&printf(&\n&);
&for(j=0;j&9;j++){
&&& for(i=0;i&9-j;i++){
&&& if(a[i]&a[i+1])
&&& t=a[i];
&&& a[i]=a[i+1];
&&& a[i+1]=t;
&for(i=0;i&10;i++)
&printf(&%d &,a[i]);
你对比一下,你的前两个for循环&&没有花括号&&循环体不明确
等 级:论坛游民
帖 子:43
专家分:19
#include&stdio.h&
&int a[10];
&int i,j,t;
&for(i=0;i&10;i++)
&scanf(&%d&,&a[i]);
&printf(&\n&);
&for(j=0;j&9;j++)
&&& for(i=0;i&9-j;i++)
&&&&&&&&if(a[i]&a[i+1]);
&&&&&&&&t=a[i];
&&&&&&&&a[i]=a[i+1];
&&&&&&&&a[i+1]=t;
&for(j=0;j&10;j++)
&printf(&%d &,a[j]);
加花括号!!
||||||||||||^_^COMEONFORGOODLIFE^_^||||||||||||
等 级:论坛游民
帖 子:43
专家分:19
#include&stdio.h&
&int a[10];
&int i,j,t;
&for(i=0;i&10;i++)
&scanf(&%d&,&a[i]);
&printf(&\n&);
&for(j=0;j&9;j++)
&&& for(i=0;i&9-j;i++)
&&&&&&&&if(a[i]&a[i+1]);
&&&&&&&&t=a[i];
&&&&&&&&a[i]=a[i+1];
&&&&&&&&a[i+1]=t;
&for(j=0;j&10;j++)
&printf(&%d &,a[j]);
加花括号!!
||||||||||||^_^COMEONFORGOODLIFE^_^||||||||||||
来 自:江西樟树
等 级:新手上路
帖 子:16
等 级:新手上路
这个用函数怎么做啊
版权所有,并保留所有权利。
Powered by , Processed in 0.046082 second(s), 7 queries.
Copyright&, BCCN.NET, All Rights Reserved程序语言的底层描述(5)——数组、指针的汇编实现以及C程序嵌入汇编
这一节我讨论下数组和指针的汇编实现,以及在C程序中嵌入简单汇编代码的内容,至于条件判断、循环、goto等内容,资料很多且比较程式化,就不再涉及了。
一:数组的汇编实现
& & & & 对指针和数组的理解程度是衡量C语言水平的分水岭,其概念略显抽象,现在要讨论汇编的实现,当然就更抽象,所以在看这节之前,先确保自己对C语言指针和数组的基本概念的明确是很重要的。
& & & & 汇编语言没有指针和数组的概念,汇编某种程度上可以看成是寄存器语言。寄存器里面存了值,至于这个值代表的是地址还是常数值,CPU在执行汇编时本身是不清楚的,这些都由编译器根据上层语言来确定的。某个空间存的值,不能确定是地址还是常数,人理解起来当然觉得抽象,因此C才有了指针变量这种特殊的对象;同理,内存空间地址的随意跳变和间接引用取值也会让人深感心里无底,因此C又有了数组这种类似“变量串”的对象。
& & & & 下面是一个数组引用的例子和对应的汇编代码,分析完此例,数组的汇编实现应该就比较清楚了。
int decimal(int *x)
& & int val = 0;
& & for (i = 0; i & 5; i++)
& & & & val = (10 * val) + *(&x[i]+i); &
&decimal5&:
& &0: & 55 & & & & & & & & & & & &push & %ebp
& &1: & 89 e5 & & & & & & & & & mov & &%esp,%ebp
& &3: & 53 & & & & & & & & & & & &push & %ebx
& &4: & 8b 5d 08 & & & & & & &mov & &0x8(%ebp),%ebx
& &7: & b8 00 00 00 00 & &mov & &$0x0,%eax
& &c: & b9 00 00 00 00 & &mov & &$0x0,%ecx
& 11: & 8d 14 80 & & & & & & lea & &(%eax,%eax,4),%edx
& 14: & 8b 04 cb & & & & & & mov & &(%ebx,%ecx,8),%eax
& 17: & 8d 04 50 & & & & & & lea & &(%eax,%edx,2),%eax
& 1a: & 41 & & & & & & & & & & & inc & &%ecx
& 1b: & 83 f9 04 & & & & & & &cmp & &$0x4,%ecx
& 1e: & 7e f1 & & & & & & & & & jle & &11 &decimal+0x11&
& 20: & 5b & & & & & & & & & & & pop & &%ebx
& 21: & c9 & & & & & & & & & & & leave &
& 22: & c3 & & & & & & & & & & & ret & &&&
& & & & 程序计数器3的压栈是实现“被调用者保存”寄存器,接下来计数器4,获取参数x的值,传送给ebx,注意x是指针,里面存了整型变量的地址,因此这个地址值就顺利的被ebx保存鸟,既然都存地址,因此指针变量x和%ebx可以看成是等价的了(如果把mov替换成lea,那ebx得到的可就是参数x本身的地址了,而不是它存的地址O(∩_∩)O~)。
& & & & 计数器7和c明显是初始化局部变量,eax和ecx明显是“调用者保存”寄存器,所以decimal作为被调用者就随便用啦,从后面的语句就能得知eax对应val,ecx对应i,可千万别仅仅依赖顺序哦!计数器11的操作很奇妙,实际运算效果是%eax+4*%eax=5*%eax,将5*val赋值给edx,不管先往下走,计数器14,%ebx+8*%ecx,就是*(x+8*i)赋值给eax,加*是因为这里是mov了不是上面的lea!是不是很高端?为什么要把x移动8倍的i呢?这就要从*(&x[i]+i)说起。
& & & & &x[i]是某个int型变量的地址,由于它的这一特殊属性,使得做加法运算时,你必须按照int型的步进来计算。因此&x[i]+1就是下一个int型变量的地址,也就是&x[i+1],同理&x[i]+i就是&x[2i],而既然int型是4字节的步进,因此我们很轻易的得出x+2i*4 的结论。
& & & & 好了,%eax现在存储了*(&x[i]+i); 下一步计数器17,计算的是%eax+2*edx,上面我们知道edx存的是5*val,很自然就推出%eax+2*edx计算的是*(&x[i]+i) + 10*val的值,并将其传送给eax保存。好了,计数器1a是让i做自加,1b和1e是循环的判断和跳转,代码的分析暂告一段落。
& & & & 这里有个问题,为啥要先计算5*val,然后再计算10*val呢?编译器有个很明显的倾向就是,不到万不得已不会用乘法指令imul,因为早期的CPU处理乘法非常费时,而加法运算要快上很多倍,所以才会使用lea的性质代替乘法和加法得出数值,事实上现在的CPU做乘法的速度已经非常接近加法了,不过目前GCC还没有为此作出修改的动作。
& & & & 上面的例子我们看到,编译器利用lea和mov很好的实现了数组的功能,不过注意这只是一级数组,多级数组的情况又如何呢?看下面的例子。
typedef &int row34_t[3][4]; &
int decimal_m(row34_t *x)
& & & & return x[1][2][3];
&decimal_m&:
& 23: & 55 & & & & & & & & & & &push & %ebp
& 24: & 89 e5 & & & & & & & & mov & &%esp,%ebp
& 26: & 8b 45 08 & & & & & &mov & &0x8(%ebp),%eax
& 29: & 8b 40 5c & & & & & &mov & &0x5c(%eax),%eax
& 2c: & c9 & & & & & & & & & & &leave &
& 2d: & c3 & & & & & & & & & & &ret &
& & & & 一个很简单的三维数组,row34_t声明了一个二维数组,然后由于是row34_t *x,因此x本身是存储二维数组的指针变量,至于函数调用时传的是指针还是数组,其实没有区别:如果传的是数组,那么一定是三维数组的首地址,本质就是二维数组的地址;如果传的是指针,参数调用只不过多了一步变量引用,将指针抛弃,里面的二维数组地址拿出来用。
& & & & 这里要理一下数组首地址和数组地址的概念,绝大部分C教材在这方面描述的都很模糊:比如a[10];a是数组的首地址,&a才是数组的地址,a确实代表数组,但由于它在数值上等于&a[0],因此容易把a误认成数组的地址。a数值上等于首地址是为了通过a计算各元素地址,这是特殊变量的特殊应用,比如无符整型数组首地址和数组地址的区别:前者是无符整型变量的地址,后者是无符整型数组的地址。a本身既然是变量,那变量就该有自己的地址,所以&a应该比较好理解,&a+1跳转整个数组也就理所当然了。事实上,当你定义数组指针时,int
a[10],int &(*p)[10],p = &a;而不是p = a;只有int *q时,q = a才正确;如果是a[2][10],a[0]是第一个末层数组(10个元素的那个数组)的首地址,那a或者&a[0]就是第一个末层数组的地址,因此p = a,q=a[0]都是合法赋值;而a又是二维数组的首地址,因此&a才是二维数组的地址,因此int &(*k)[2][10], k = &a才是合法赋值。写到这里,你应该没有理由再对多维数组、首地址、数组地址、数组指针这些概念有任何问题了吧?
& & & & 由于多维数组的每一维数组的元素所存储的值,都是下一维数组的首地址,而对数组元素地址(首地址也一样)的加减运算是按该元素所存储内容的大小(下一维的维度越深,要计算跳变步进的层次也越深)进行跳变的,因此x[1][2][3]的寻址就很好计算了。首先是x[1],计算row34的步进,3*4*(sizeof(int)) = 48,也就是x+1或&x[0]+1的跳变步进;接下来是x[1][2],那就是x[1]+2的跳变步进是2*4*(sizeof(int)) = 32;最后是x[1][2]+3的跳变步进:3*(sizeof(int))
= 12。48 + 32 + 12 = 92 = 0x5c,应该不用我多废话了。
& & & & 接着我们玩一个更狠的,下例:
#define N 16
typedef int fix_matrix[N][N];
int fix_prod (fix_matrix A, fix_matrix B, &int i, int k)
& & int result = 0;
& & for (j = 0; j & N; j++)
& & & & result += A[i][j] * B[j][k];
&fix_prod&:
& &0: & 55 & & & & & & & & & & & & & & & & &push & %ebp
& &1: & 89 e5 & & & & & & & & & & & & & & mov & &%esp,%ebp
& &3: & 8b 55 10 & & & & & & & & & & & &mov & &0x10(%ebp),%edx & & & &//先用edx存储参数i
& &6: & 8b 45 14 & & & & & & & & & & & &mov & &0x14(%ebp),%eax & & & &//先用eax存储参数k
& &9: & 56 & & & & & & & & & & & & & & & & &push & %esi
& &a: & 8d 0c 85 00 00 00 00 & &lea & & &0x0(,%eax,4),%ecx & & &//ecx = 4*k
& 11: & c1 e2 06 & & & & & & & & & & & shl & & &$0x6,%edx & & & & & & & & &&//64*i
& 14: & 53 & & & & & & & & & & & & & & & & push & %ebx
& 15: & 31 f6 & & & & & & & & & & & & & & xor & & &%esi,%esi & & & & & & & & &//result = 0;
& 17: & 03 4d 0c & & & & & & & & & & & add & &0xc(%ebp),%ecx & & & &//将参数B加上4*k,存储到ecx
& 1a: & 03 55 08 & & & & & & & & & & & add & &0x8(%ebp),%edx & & & //将参数A加上64*i, &存储到edx
& 1d: & bb 0f 00 00 00 & & & & & & &mov & &$0xf,%ebx
& 22: & 89 f6 & & & & & & & & & & & & & & mov & &%esi,%esi
& 24: & 8b 01 & & & & & & & & & & & & & &mov & &(%ecx),%eax
& 26: & 0f af 02 & & & & & & & & & & & & imul & (%edx),%eax & & & & & //eax:A[i][j] * B[j][k];
& 29: & 01 c6 & & & & & & & & & & & & & &add & &%eax,%esi
& 2b: & 83 c2 04 & & & & & & & & & & & add & &$0x4,%edx & & & & & & // edx:A ;ecx:B
& 2e: & 83 c1 40 & & & & & & & & & & & add & &$0x40,%ecx
& 31: & 4b & & & & & & & & & & & & & & & & dec & &%ebx
& 32: & 79 f0 & & & & & & & & & & & & & & jns & & 24 &fix_prod+0x24&
& 34: & 5b & & & & & & & & & & & & & & & & pop & &%ebx
& 35: & 89 f0 & & & & & & & & & & & & & & mov & &%esi,%eax
& 37: & 5e & & & & & & & & & & & & & & & & pop & &%esi
& 38: & c9 & & & & & & & & & & & & & & & & leave &
& 39: & c3 & & & & & & & & & & & & & & & & ret & &
& & & & 我们可以明显的看到,fix_matrix本身是定义成N*N二维数组的,但是在函数内部,却是按i、j、k进行维度跳变访问,首先看A[i][j],i作为参数传入是不变的,j随着循环在改变;再看B[j][k],k作为参数是不变的,j随着循环在增加。我们假设i、k都比N小(即使都比N大也无妨,照样玩),好,我们来分析汇编部分。
& & & & 晕,说得容易,真看看还是有点头晕,我大脑的即时存储差,所以边看边在关键的地方写了注释,这样才勉强看懂,嚯嚯。先看参数,程序计数器3、6是用两个寄存器临时读取i和k,接下来计数器a,利用lea生成4*k的数,用ecx存储。计数器11利用左移6位操作,用edx存储64*i,计数器15利用异或使得esi赋值为0。关键是计数器17和1a,利用add操作,将前面生成的4*k加到参数A上,使得ecx存储A+4*k;同理edx存储B+64*i。也许到这里你还是看不懂编译器的用意,先往下继续。
& & & & 计数器1d、31、32是实现j的N次循环,我们看到汇编把循环优化成减法,为啥我也不清楚,同样不清楚的是计数器22,干嘛呢?好了关键点又出现:计数器24和26,分别对ecx和edx间接寻址然后相乘,如果ecx和edx真的分别存储了A[i][j]和B[j][k],那么这句自然好理解。接下来计数器29,将乘积结果存储到esi,计数器2b和2e是指针移动,分别将A和B增加4和64,注意这个是汇编,没有步进的概念,是做的纯加法,接着由循环又跳回到24语句执行……
& & & & 理到这,最需要搞清楚的就是edx和ecx为什么通过4*k和64*i初始化,又为什么每次循环完就通过4和64增加,它们到底如何与A[i][j] * B[j][k]对应上的?如果你对多维数组步进比较熟悉,相信已经心算出原委了^_^
& & & & 回到C代码,由于是j的递增,因此反映在A[i][j]上时,就是A[i][0]、A[i][1]、A[i][2]……原来就是末层一维数组的跳变,因此只要我们把edx初始化成&A[i][0]的地址数值,以后循环每次加sizeof(int)就能实现这样的计算;
& & & & 再看B[j][k], 由于是j的递增,因此反映在B[j][k]上时,就是B[0][k]、B[1][k]、B[2][k]……根据上面讲的多维数组原理,B[j]存储的是末层一维数组的首地址,以j进行跳变,步进就应该是N*sizeof(int),因此只要我们把ecx初始化成&B[0][k]的地址数值,以后循环每次加N*sizeof(int),就能实现上面的跳转。
& & & & &而&A[i][0]就是A + i*N*sizeof(int) = A+64*i,&B[0][k]就是B + &k*&sizeof(int) = B + 4*k,谜底彻底揭开O(∩_∩)O~注意一点,汇编里代表的&A[i][0]在数值上加上4,和在C语言中写&A[i][0]+4这个语句的结果可以完全不通哦!前者是纯算数,后者是要计算步进的,回忆下之前的讲解。
二:动态分配以及指针的汇编实现
int var(var_matrix A, int i, int j)
& & & & A = (var_matrix)malloc(N*N);
& & & & return A[(i*N) + j];& & & & & & & & & &&
0000003c &var&:
& 3c: & 55 & & & & & & & & & & & &push & %ebp
& 3d: & 89 e5 & & & & & & & & & mov & &%esp,%ebp
& 3f: & &53 & & & & & & & & & & & &push & %ebx
& 40: & 83 ec 10 & & & & & & &sub & &$0x10,%esp & & & & & &//esp开辟16字节
& 43: & 8b 5d 0c & & & & & & &mov & &0xc(%ebp),%ebx & &//ebx存i
& 46: & 68 00 01 00 00 & &push & $0x100 & & & & & & & & & //存16*16
& 4b: & c1 e3 04 & & & & & & &shl & &$0x4,%ebx & & & & & & & &//ebx存16*i
& 4e: & e8 fc ff ff ff & & & & & call & 4f &var+0x13& & & & & &//直接跳转到4e理论的下一个计数器4f
& 53: & 03 5d 10 & & & & & & &add & &0x10(%ebp),%ebx &//ebx存16*i+j
& 56: & 8b 04 98 & & & & & & &mov & &(%eax,%ebx,4),%eax & //4*(16*i+j) + %eax
& 59: & 8b 5d fc & & & & & & & mov & &0xfffffffc(%ebp),%ebx & //恢复ebx旧值
& 5c: & c9 & & & & & & & & & & & &leave &
& 5d: & c3 & & & & & & & & & & & &ret & &
& 5e: & 89 f6 & & & & & & & & & &mov & &%esi,%esi
& & & & 这个简单的例子,汇编看起来也有些意思。我在注释里已经写得比较清楚,计数器46,将16*16作为malloc的参数压入栈,计数器4e调用malloc,由于是动态链接所以.o文件的汇编代码如我注释的解释。计数器56,malloc返回申请的空间地址%eax,经过一维数组的跳变结果再写入eax,作为var函数的返回值。
三:在C中嵌入汇编代码
& & & & 很多初学者和初级C程序员经常觉得在C语言中嵌入临时汇编语言是多么高大上的牛程,现在有了前面的汇编铺垫,是不是觉得没那么神秘了?事实上,在C里嵌入有效汇编的技巧仍然是高水平的体现,尤其是从场景来看,很少是应用程序,往往是OS原码或者其他的系统级编程,反正我本人写的代码总量非常少而且浅显,自然也没涉及到这种情况,也只能是简单的扯扯教科书上的内容,大家一起作为一个了解。
& & & & 由于目前的编译器非常强大,单纯追求更高效优化已经不是嵌入汇编的理由了。因此现在嵌入汇编的目的,是诸如访问特殊寄存器中存放的进程状态信息;输入输出操作特殊指令或访问特殊的存储器位置。还有应用程序中可能需要用到的条件码值,例如进位、溢出、符号、零等标志,是不能直接用C读出来的。我们就以应用程序举例。
& & & & 我们要实现一个乘法函数,当乘数溢出时,返回0,否则返回1,先来看有符数的实现:
int ok_smul(int x, int y, int *dest)&
& & *dest = x*y;&
& & asm(&setae %% movzbl %%bl,%0& &
: &=r& (result) & /* Output & &*/ &
: & & & & & & & & & & &/* No inputs */ &
: &%ebx& & & & & /* Overwrites */ &
1、%在嵌入汇编语句表示寄存器,而汇编语句里本身又用%引用寄存器,所以出现两个%
2、%0、%1……表示引用的操作数,此例只有result被引用,因此对应%0
3、输入和输出比较好理解,此例只想通过进位标志改变result,就是输出
4、由于需要借用%ebx的低位%bl,因此%ebx在嵌入汇编时会被覆盖,声明其为overwrites的作用就是让编译器增加对%ebx的备份和保护。
& & & & 上面的代码,通过setae %%bl,使得溢出信息被设置到%bl中,然后用类似逻辑扩展的movzbl对%bl的其余位进行清零(你用ebx之前也不知道它的旧值是多少),只有%dl才能客观的反映溢出信息0或者1,并且传送给%0,也就是参数result。
&ok_smul&:
& &0: & 55 & & & & & & & & & & & &push & %ebp
& &1: & 89 e5 & & & & & & & & & mov & &%esp,%ebp
& &3: & 8b 45 0c & & & & & & &mov & &0xc(%ebp),%eax & & //y
& &6: & 0f af 45 08 & & & & & imul & &0x8(%ebp),%eax & &//x*y
& &a: & 8b 55 10 & & & & & & &mov & &0x10(%ebp),%edx &//dest
& &d: & 53 & & & & & & & & & & & &push & %ebx
& &e: & 89 02 & & & & & & & & & mov & &%eax,(%edx) & & & & &//dest = x*y
& 10: & 0f 93 c3 & & & & & & &setae &%bl
& 13: & 0f b6 c3 & & & & & & &movzbl %bl,%eax
& 16: & 5b & & & & & & & & & & & pop & &%ebx
& 17: & c9 & & & & & & & & & & & leave &
& 18: & c3 & & & & & & & & & & & ret & &
& & & & 我们来看对应的汇编代码,很明显,计数器d是对ebx旧值的备份,本来%ebx就是“被调用者保存”,你函数内部嵌入的汇编要修改%ebx,当然也是在函数中执行压栈。如果你不用bl而是用cl,那根据%ecx是“调用者保存”,汇编代码中直接就看不到压栈备份,不信自己试试^_^。计数器10、13就是我们嵌入的汇编语言,计数器16将ebx恢复旧值。
& & & & 接下来要实现32位无符数乘法,很明显,无符数少了一个符号位,因此比有符数乘法更容易产生溢出,因此对溢出的判断是有区别的,编译器优先选择imull并且它也能够计算出无符数乘法正确值,但为了判断溢出信息,我们必须强制采用mull操作指令。下面是实现代码:
int ok_umul(unsigned x, unsigned y, unsigned *dest)
& & asm(&movl %2,%% mull %3; movl %%eax,%0; setae %% movzbl %%dl,%1&
& & & & : &=r& (*dest), &=r& (result) & /* Outputs &%0、 %1 &*/
& & & & : &r& &(x), & & &r& &(y) & & & & & & &/* Inputs & & &%2、 %3*/
& & & & : &%eax&, &%edx& & & & & & & &/* Overwrites */
& & & & );
1、mull规定,%eax是默认的被乘数寄存器,而mull指令后面跟的是乘数寄存器,乘法计算结果的高32位写入乘数寄存器,低32位写入被乘数寄存器%eax;
2、由于强行使用mull,因此整个乘法的输入和输出全由汇编实现,x、y就是输入,分别用%2、%3表示,dest和resault就是输出,分别用%0、%1表示;
3、%eax和%edx是必用的临时寄存器,申请汇编保护。
& & & & 根据提示1,先构造被乘数%eax,于是x被传送;接着mull执行32位无符数乘法,乘法结果的低32位在%eax里,然后mov将它的值赋给%0也就是dest,接下来就是设置标志位,以及清零传送给%1(result),打完收工。来看看汇编的实现:
&ok_umul&:
& 18: & 55 & & & & & & & & & & &push & %ebp
& 19: & 89 e5 & & & & & & & & mov & &%esp,%ebp
& 1b: & 8b 4d 0c & & & & & &mov & &0xc(%ebp),%ecx & &//y传送给ecx
& 1e: & 53 & & & & & & & & & & &push & %ebx & & & & & & & & & & //看到没?还有“被调用者保存”的ebx寄存器需要压栈备份,其他的随便用
& 1f: & 8b 5d 08 & & & & & & mov & &0x8(%ebp),%ebx
& 22: & 89 d8 & & & & & & & & mov & &%ebx,%eax & & & & & //x传送给%eax
& 24: & f7 e1 & & & & & & & & &mul & &%ecx & & & & & & & & & & &//做x*y,高位存入%ecx,低位存数eax
& 26: & 89 c3 & & & & & & & & mov & &%eax,%ebx & & & & & //乘法结果的低位传送给ebx
& 28: & 0f 93 c2 & & & & & & setae &%dl & & & & & & & & & & & //%edx低8位获得溢出标志信息
& 2b: & 0f b6 ca & & & & & & movzbl %dl,%ecx & & & & & & & & //%ecx获得溢出标志
& 2e: & 8b 45 10 & & & & & &mov & &0x10(%ebp),%eax & &//%eax存储参数dest的值(指针里的地址值)
& 31: & 89 18 & & & & & & & & mov & &%ebx,(%eax) & & & & & &//将乘法结果低位通过地址间接寻址赋值给dest指向的空间
& 33: & 5b & & & & & & & & & & &pop & &%ebx
& 34: & 89 c8 & & & & & & & & mov & &%ecx,%eax
& 36: & c9 & & & & & & & & & & &leave &
& 37: & c3 & & & & & & & & & & &ret & &
& & & & 我写的注释应该很清楚明了了。看不懂的请面壁思过O(∩_∩)O~
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?

我要回帖

更多关于 汇编语言 数组排序 的文章

 

随机推荐