文档库 最新最全的文档下载
当前位置:文档库 › C语言中可变参数的用法

C语言中可变参数的用法

C语言中可变参数的用法
C语言中可变参数的用法

C语言中可变参数的用法

文章导读:我们在C语言编程中会遇到一些参数个数可变的函数,例如printf()这个函数,它的定义是这样的:

int printf( const char* format, ...);

它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法:

printf("%d",i);

printf("%s",s);

printf("the number is %d ,string is:%s", i, s);

究竟如何写可变参数的C函数以及这些可变参数的函数编译器是如何实现的呢?本文就这个问题进行一些探讨,希望能对大家有些帮助.会C++的网友知道这些问题在C++里不存在,因为C++具有多态性.但C++是C的一个超集,以下的技术也可以用于C++的程序中.限于本人的水平,文中如果有不当之处,请大家指正.

我们在C语言编程中会遇到一些参数个数可变的函数,例如printf()这个函数,它的定义是这样的:

int printf( const char* format, ...);

它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法:

printf("%d",i);

printf("%s",s);

printf("the number is %d ,string is:%s", i, s);

究竟如何写可变参数的C函数以及这些可变参数的函数编译器是如何实现的呢?本文就这个问题进行一些探讨,希望能对大家有些帮助.会C++的网友知道这些问题在C++里不存在,因为C++具有多态性.但C++是C 的一个超集,以下的技术也可以用于C++的程序中.限于本人的水平,文中如果有不当之处,请大家指正.

(一)写一个简单的可变参数的C函数

下面我们来探讨如何写一个简单的可变参数的C函数.写可变参数的C函数要在程序中用到以下这些宏:

void va_start( va_list arg_ptr, prev_param );

type va_arg( va_list arg_ptr, type );

void va_end( va_list arg_ptr );

va在这里是variable-argument(可变参数)的意思.这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件.下面我们写一个简单的可变参数的函数,改函数至少有一个整数参数,第二个参数也是整数,是可选的.函数只是打印这两个参数的值.

void simple_va_fun(int i, ...)

{

va_list arg_ptr;

int j=0;

va_start(arg_ptr, i);

j=va_arg(arg_ptr, int);

va_end(arg_ptr);

printf("%d %d\n", i, j);

return;

}

我们可以在我们的头文件中这样声明我们的函数:

extern void simple_va_fun(int i, ...);

我们在程序中可以这样调用:

simple_va_fun(100);

simple_va_fun(100,200);

从这个函数的实现可以看到,我们使用可变参数应该有以下步骤: 1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变量是指向参数的指针.

2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数.

3)然后用va_arg返回可变的参数,并赋值给整数j. va_arg的第二个参数是你要返回的参数的类型,这里是int型.

4)最后用va_end宏结束可变参数的获取.然后你就可以在函数里使用第二个参数了.如果函数有多个可变参数的,依次调用va_arg获取各个参数.

如果我们用下面三种方法调用的话,都是合法的,但结果却不一样: 1)

simple_va_fun(100);

结果是:100 -123456789(会变的值)

2)

simple_va_fun(100,200);

结果是:100 200

3)

simple_va_fun(100,200,300);

结果是:100 200

我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果正确,但和我们函数最初的设计有冲突.下面一节我们探讨出现这些结果的原因和可变参数在编译器中是如何处理的.

(二)可变参数在编译器中的处理

我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下面以VC++中stdarg.h里x86平台的宏定义摘录如下(’\’号表示折行): typedef char * va_list;

#define _INTSIZEOF(n) \

((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

#define va_arg(ap,t) \

( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap) ( ap = (va_list)0 )

定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函数是从右向左压入堆栈的,图(1)是函数的参数在堆栈中的分布位置.我们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再看va_start的定义,定义为&v+_INTSIZEOF(v),而&v是固定参数在堆栈的地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆栈的地址,如图:

高地址|-----------------------------|

|函数返回地址 |

|-----------------------------|

|....... |

|-----------------------------|

|第n个参数(第一个可变参数) |

|-----------------------------|<--va_start后ap指向

|第n-1个参数(最后一个固定参数)|

低地址|-----------------------------|<-- &v

图(1)

然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我们看一下va_arg取int型的返回值:

j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) ); 首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回ap-sizeof(int)的int*指针,这正是第一个可变参数在堆栈里的地址(图2).然后用*取得这个地址的内容(参数值)赋给j.

高地址|-----------------------------|

|函数返回地址 |

|-----------------------------|

|....... |

|-----------------------------|<--va_arg后ap指向

|第n个参数(第一个可变参数) |

|-----------------------------|<--va_start后ap指向

|第n-1个参数(最后一个固定参数)|

低地址|-----------------------------|<-- &v

图(2)

最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的.在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型.关于va_start, va_arg, va_end的描述就是这些了,我们要注意的是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.

(三)可变参数在编程中要注意的问题

因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型.有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数printf是从固定参数format字符串来分析出参数的类型,再调用va_arg的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.如果simple_va_fun()改为:

void simple_va_fun(int i, ...)

{

va_list arg_ptr;

char *s=NULL;

va_start(arg_ptr, i);

s=va_arg(arg_ptr, char*);

va_end(arg_ptr);

printf("%d %s\n", i, s);

return;

}

可变参数为char*型,当我们忘记用两个参数来调用该函数时,就会出现core dump(Unix) 或者页面非法的错误(window平台).但也有可能不出错,但错误却是难以发现,不利于我们写出高质量的程序.

以下提一下va系列宏的兼容性.System V Unix把va_start定义为只有一个参数的宏:

va_start(va_list arg_ptr);

而ANSI C则定义为:

va_start(va_list arg_ptr, prev_param);

如果我们要用system V的定义,应该用vararg.h头文件中所定义的宏,ANSI C的宏跟system V的宏是不兼容的,我们一般都用ANSI C,所以用ANSI C的定义就够了,也便于程序的移植.

小结:

可变参数的函数原理其实很简单,而va系列是以宏定义来定义的,实现跟堆栈相关.我们写一个可变函数的C函数时,有利也有弊,所以在不必要的场合,我们无需用到可变参数.如果在C++里,我们应该利用C++的多态性来实现可变参数的功能,尽量避免用C语言的方式来实现.

语言中的可变参数宏/函数,及可变参数在函数中的传递问题全解析

做ScheduleDownload,要做一个logger,这个logger的大致结构如下:

Code: Select all

#ifdef _DEBUG

#define LOGGER(log_level, filename, line, format, ...) \

logger_action(log_level, filename, line, format, __VA_ARGS__);

#else

#define LOGGER

#endif

/*

* Log the strings. filename should be __FILE__ and line should be __LINE__

*/

void logger_action(LOG_LEVEL log_level, LPCTSTR filename, int line, LPCTSTR format, ...);

#define UTILS_RVIF_WITH_LOG(expr, val, log_level, filename, line, format, ...) \

if (!(expr)) { \

LOGGER(log_level, filename, line, format, __VA_ARGS__); \

return val; \

}

这里很清楚了,真正的函数是logger_action,两个宏分别包装了一下。这里:

1. 在宏定义中,使用__VA_ARGS__来表示可变参数,前面用...即可。如果可变参数为空,那么,理论上就会多产生一个逗号导致编译失败(format参数后面多一个逗号),此时,__VA_ARGS__会自动消除多余的逗号。这是VC编译器的动作,如果是GNU的编译器,要这样写##__VA_ARGS__,通过##来将多余的逗号去掉(##一般用来连接字符串的,但是在这里就有去掉前面多余逗号的作用)。

__VA_ARGS__是C99规范中规定出来的关键字,在VC中,要在Visual Studio 2005开始支持。

2. __VA_ARGS__不能出现在函数实现中,只能出现在宏里面。所以这就带来一个问题:在logger_action中,我们其实不是想自己分析format和后面的可变参数,我们仅仅想把这些都传递给StringCchPrintf而已,于是尝试在logger_action中这样处理:Code: Select all

// handle format & args

va_list args;

va_start(args, format);

UTILS_RETURN_IF_FAIL(SUCCEEDED(StringCchPrintf(log_str_buf + log_str_cur_index, _countof(log_str_buf) -

log_str_cur_index, format, args)));

va_end(args);

va_list就是一个char *,va_start是一个宏,它的作用就是将args这

个参数设置成format参数地址+format参数的字节数-- 说白一些就

是,将args设置成函数栈中format以后的位置上,这样args就指向了

可变参数的开头。接着可以使用va_arg参数将可变参数一个一个取

出,这也是为什么va_arg宏要提供一个参数type的原因:va_arg根据

参数type来决定往后取多少字节出来。最后的va_end就是将args设

成NULL。

所以va_list/va_start/va_arg/va_end其实非常简单,就是指针操

作,将不确定的参数从函数堆栈中取出。这里我们只需要让

StringCchPrintf来处理即可,于是我天真的将args参数传递给了StringCchPrintf。

结果是:编译不出错,执行出错,StringCchPrintf生成的字符串是一

堆乱七八糟的东西。开始Debug,通过观察函数的栈,传入的可变参

数是OK的,证明__VA_ARGS__在一堆宏之间传递没有问题。那为

什么StringCchPrintf取不出这些可变参数呢?其实非常简单:

就像前面说的一样,具有可变参数的函数在处理时,使用的是

va_list/va_start...这些宏,这些宏是在本函数的堆栈上进行指针操

作,而我们在调用StringCchPrintf的时候,可变参数部分传入的是

args,前面也说了,args其实类型是char *,就是一个地址,根本代

表不了那一堆可变参数。StringCchPrintf能取出的唯一参数就是

args,里面的值是logger_action函数format参数之后的堆栈地

址!!自然出错了,没crash就不错了。

OK,那应该怎么做呢?结论是:

1. 在logger_action函数中,将可变参数一个一个取出,用汇编将这些

参数一个一个的压入StringCchPrintf函数的栈中。这种做法可移植性

很差,不同编译器和不同平台上运行都有可能出问题,因为牵扯到汇

编。

2. 其实我们相当于在做一个mysprintf,里面调用sprintf。除非用方

法1,否则是无法实现的。幸运的是,sprintf有个兄弟叫vsprintf,这

个带v的函数最后不是接收...的参数,而是接受一个va_list类型的参

数,也就是说,vsprintf和sprintf不同的是,它不是在自己的堆栈上

找可变参数,而是在我们给定的va_list参数地址上找可变参数。

Great!于是查找StringCchPrintf有没有这样一个兄弟-- 有!StringCchVPrintf。于是代码只需要修改一个字符就OK了:

Code: Select all

// handle format & args

va_list args;

va_start(args, format);

UTILS_RETURN_IF_FAIL(SUCCEEDED(StringCchVPrintf( + log_str_cur_index,

_countof(log_str_buf) -

log_str_cur_index, format, args)));

va_end(args);

这样就OK了!StringCchVPrintf会在args参数指定的地址开始,根据

format中的定义,找寻对应的参数。测试通过,程序工作正常。

总结,主要是两点:

1. __VA_ARGS__不能出现在函数中,只能在宏中使用

2. 要将可变参数在函数中传递,要看被传入的函数有没有一个va_list

参数的版本,否则就非常麻烦了。

C语言与汇编语言互相调用

浅谈C程序中调用汇编模块的方法 C语言是目前非常流行的一种编程语言,除具有高级语言使用方便灵活、数据处理能力强、编程简单等优点外,还可实现汇编语言的大部分功能,如可直接对硬件进行操作、生成的目标代码质量较高且执行的速度较快等。所以在工程上对硬件处理速度要求不很高的情况下,基本可以用C代替汇编语言,编写接口电路的控制软件。但C也不能完全取代汇编语言,如在一些对速度要求很高的实时控制系统中,以及对硬件的特殊控制方面,C有时也不能完全很好胜任,还需要汇编语言来编写。因为汇编语言目标代码更精练,对硬件直接控制能力更强和执行速度更快,但汇编语言编程烦难、表达能力差也显而易见。比较好的解决办法是C与汇编语言混合编程,即用C编写软件的调度程序、用户界面以及速度要求不高的控制部分,而用汇编语言对速度敏感部分提供最高速度的处理模块,供C调用。这种方法提供了最佳的软件设计方案,做到了兼顾速度效率高和灵活方便。由于本人的毕业设计需要C 程序中调用汇编模块的方法来提高ARM定点指令的执行速度,故对这方面进行了学习。学习心得如下: 对于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

汇编语言的过程调用与c语言的函数调用

姓名:孙贵森 学号: 汇编语言地过程调用,如果需要传递参数,一般有种方法,通过寄存器来“传递”,或是通过参数来传递.(还有将所有参数制成参数列表并压栈地传递方法,但较少用.)通过寄存器来“传递”,不是真正意义上地传递,其只不过是事先在几个有限地寄存器中设置相应地值后,再调用过程,过程再直接读取这些寄存器地内容.可想而知,此法犹如语言中地全局变量,极易感染.而如果通过参数来传递,又不得不面临手工维护堆栈框架( )地重担.堆栈框架动态地存放着参数、调用过程地返回地址、过程局部变量、过程内地压栈等内容,也是不好对付地.一般情况下,一个普通地过程可能如下编写:文档来自于网络搜索 , ..... 作为遵从调用约定()调用者,则需这样调用上述过程: ; ; ; , * ; 而如果遵从调用约定,则: , ...... , [ ] ; , [ ]; ...... * ; , , ; ...... , [ ]; , [ ]; , [ ; , [ ]; ...... , ; * ;

在被调用地过程内,分为种情况: . 无参数,也无局部变量 . 有参数 . 有局部变量 当无参数且无局部变量时,堆栈中只是保存语句地下一条语句地地址,可以很安全地返回.而当有参数,使用伪指令地接收参数地形式,则会自动生成正确地返回代码.而当有局部变量,使用伪指令来定义局部变量,也会自动地生成正确地返回代码.在将参数压栈时,仍需将其打包为位地,文档来自于网络搜索 ; , ; ; 另一选择是,将用作地变量声明为. ; ; 还有另一种方法,即,总是传递指针. ; (, ) , ; , , , , , [] , , [] 这种方法在保留了我们可以声明仅需地变量类型地同时,也确保位地方法正确压栈.语言中地每一个函数都是一个独立地代码块.一个函数地代码块是隐藏于函数内部地,不能被任何其它函数中地任何语句(除调用它地语句之外)所访问(例如,用语句跳转到另一个函数内部是不可能地).构成一个函数体地代码对程序地其它部分来说是隐蔽地,它既不能影响程序其它部分,也不受其它部分地影响.换言之,由于两个函数有不同地作用域,定义在

汇编语言编程规范

软件设计更多地是一种工程,而不是一种个人艺术。如果不统一编程规范,最终写出的程序,其可读性将较差,这不仅给代码的理解带来障碍,增加维护阶段的工作量,同时不规范的代码隐含错误的可能性也比较大。 分析表明,编码阶段产生的错误当中,语法错误大概占20%左右,而由于未严格检查软件逻辑导致的错误、函数(模块)之间接口错误及由于代码可理解度低导致优化维护阶段对代码的错误修改引起的错误则占了一半以上。 可见,提高软件质量必须降低编码阶段的错误率。如何有效降低编码阶段的错误呢?这需要制定详细的软件编程规范,并培训每一位程序员,最终的结果可以把编码阶段的错误降至10%左右,同时也降低了程序的测试费用,效果相当显著。 本文从代码的可维护性(可读性、可理解性、可修改性)、代码逻辑与效率、函数(模块)接口、可测试性四个方面阐述了软件编程规范,规范分成规则和建议两种,其中规则部分为强制执行项目,而建议部分则不作强制,可根据习惯取舍。 1.排版 规则1 程序块使用缩进方式,函数和标号使用空格缩进,程序段混合使用TAB和空格缩进。缩进的目的是使程序结构清晰,便于阅读和理解。 默认宽度应为8个空格,由于Word中为4个空格,为示范清晰,此处用2个代替(下同)。 例如: MOV R1, #00H MOV R2, #00H MOV PMR, #PMRNORMAL MOV DPS, #FLAGDPTR MOV DPTR, #ADDREEPROM read1kloop: read1kpage: INC R1

MOVX A, @DPTR MOV SBUF, A JNB TI, $ CLR TI INC DPTR CJNE R1, #20H, read1kpage INC R2 MOV R1, #00H CPL WDI CJNE R2, #20H, read1kloop ;END OF EEPROM 规则2 在指令的操作数之间的,使用空格进行间隔,采用这种松散方式编写代码的目的是使代码更加清晰。 例如: CJNE R2, #20H, read1kloop ;END OF EEPROM 规则3 一行最多写一条语句。 规则4 变量定义时,保持对齐。便于阅读和检查内存的使用情况。 例如: RegLEDLOSS EQU 30H ; VARIABLE ; TESTLED==RegLEDLOSS.0 RegLEDRA EQU 31H ; VARIABLE

arm汇编语言调用C函数之参数传递

arm汇编语言调用C函数之参数传递 于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(ARM-Thumb Procedure Call Standard),ATPCS 主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容可以查看ADS1.2 Online Books ——Developer Guide的2.1节。这篇文档要讲的是汇编代码中对C函数调用时如何进行参数的传递以及如何从C函数正确返回。 不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递。 我们先讨论一下形参个数为4的情况. 实例1: test_asm_args.asm //-------------------------------------------------------------------------------- IMPORT test_c_args ;声明test_c_args函数 AREA TEST_ASM, CODE, READONLY EXPORT test_asm_args test_asm_args STR lr, [sp, #-4]! ;保存当前lr ldr r0,=0x10 ;参数 1 ldr r1,=0x20 ;参数 2

ldr r2,=0x30 ;参数 3 ldr r3,=0x40 ;参数 4 bl test_c_args ;调用C函数 LDR pc, [sp], #4 ;将lr装进pc(返回main函数) END test_c_args.c //-------------------------------------------------------------------------------- void test_c_args(int a,int b,int c,int d) { printk("test_c_args:\n"); printk("%0x %0x %0x %0x\n",a,b,c,d); } main.c //-------------------------------------------------------------------------------- int main() { test_asm_args(); for(;;); } 程序从main函数开始执行,main调用了test_asm_args,test_asm_args 调用了test_c_args,最后从test_asm_args返回main。代码分别使用了

C语言调用汇编语言时变量的传递

F2812中C语言调用汇编函数 参考资料: (1) SPRU514 ---- TMS320F28x Optimizing C/C++ Compiler User’s Guide.pdf; (2) spru430d ---- TMS320C28x DSP CPU and Instruction Set Reference Guide; (3) spru513c ---- TMS320C28x Assembly Language Tools User's Guide. 一、编写C语言能调用的.asm文件 以delay.asm为例: 1、将文件保存为.asm文件; 2、在文件中编写如下代码: ;********************************************************** ***********/ ;* 鲍正华*/ ;* 2010.07.09 */

;********************************************************** ***********/ ;//######################################################### ################## ; @FunctionName: Delay_asm ; ; @Brief: 延时1s ; ; @Param: unsigned long cnt 传给ACC ; ; @Return: 无 ;//######################################################### ################## .def _Delay_asm .global _Delay_asm

汇编考前辅导带答案版

一、单项选择题 1、CPU发出的访问存储器的地址是(A )。 A.物理地址 B.偏移地址 C.逻辑地址 D.段地址 2、下列指令中操作数在代码段中的是(A )。 A.MOV AL,42H B.ADD AL,BL C.SUB [BX],DI D.INC [DI] 3、与MOV BX,OFFSET VAR指令完全等效的指令是(D )。 A.MOV BX,VAR B.LDS BX,VAR C.LES BX,VAR D.LEA BX,VAR 4、表示过程定义结束的伪指令是(A )。 A.ENDP B.ENDS C.END D.ENDM 5、BUF1 DB 3 DUP(0,2 DUP(1,2),3) COUNT EQU $ - BUF1 符号COUNT等价的值是(B )。 A.6 B.18 C.16 D.8 6、下列寄存器中,作为16位寄存器的是(D )。 A. AL B. BL C. CH D.DX 7、在程序运行过程中,确定下一条指令的物理地址的计算表达式是(C )。 A.DS*16+SI B. ES*16+DI C. CS*16+IP D. SS*16+SP 8、物理地址(10FF0H )=10H,(10FF1H )=20H,(10FF2H )=30H,从地址10FF1H中取一个字的内容是(B )。 A.1020H B.3020H C.2030H D.2010H

9、将AX 清零,下列指令错误的是(C )。 A.SUB AX,AX B.XOR AX,AX C.OR AX,00H D.AND AX,00H 10、完成同指令XCHG AX,BX相同功能的指令或指令序列是(D )。 A. MOV AX,BX B. MOV BX,AX C. PUSH AX D. MOV CX,AX POP BX MOV AX,BX MOV BX,CX 11、JMP BX 的目标地址的偏移量为(A )。 A. BX 的内容 B. BX 所指向的内存单元的内容 C. IP + BX 的内容 D. IP + [BX] 12、可用作寄存器间接寻址或基址、变址寻址的地址寄存器,正确的是(D )。 A.AX,BX,CX,DX B.DS,ES,CS,SS C.SP,BP,IP,BX D.SI,DI,BP,BX 13、设SP初值为2000H,执行指令“PUSH AX”后,SP的值是(D )。 A.1FFFH B.1998H C.2002H D.1FFEH 14、汇编语言源程序中,每个语句可由四项组成,其中名字项是一个符号,下面列出的有效名字是(A )。 A.Variable B.First&ld C.0FFFFH D.‘Memphis’ 15、要求将A,B两个字符的ASCⅡ码41H,42H顺序存放在连续两个字存储单元中,可选用的语句是(B )。 A.DA1DB‘AB’ B.DA1DW‘AB’ C.DA1DB0ABH D.DA1DW0ABH

汇编语言程序设计2008A,有答案

汇编语言程序设计2008A 一、单项选择题(本大题共20小题,每小题1分,共20分)在每小题列出的四个备选项中只有一个是符合题目要 求的,请将其代码填写在题后的括号内。错选、多选或未选均无分 1.在汇编语言源程序中,以下哪一个编码是二进制形式的编码() (A)1010 (B)01H (C)01000101B (D)22D 2.假设(AL)=34H,(AH)=2FH,执行指令XCHG AL, AH 后,(AH)为() (A)2FH (B)34H(C)00H (D)3FH 3.TF标志位的含义为() (A)单步标志(B)最高位进位标志(C)补码溢出标志(D)零值标志 4.对于指令MOV AL, [BX],其源操作数的寻址方式为() (A)寄存器寻址(B)立即数寻址(C)基址寻址(D)寄存器间接寻址 5.对于指令POP 0100H[BX][SI],其目的操作数的寻址方式为() (A)寄存器寻址(B)变址寻址(C)基址寻址(D)基址变址寻址 6.假设(BX)=0100H,对于指令ADD AL, 0200H[BX],其源操作数的有效地址为() (A)0100H (B)0200H (C)0300H (D)0400H 7.假设(DS)=0C00H,由逻辑地址0C00H:0200H所指示的字类型内存单元内容为02FDH,执行指令LEA BX, [0200H]后,(BX)为() (A)不确定(B)02FDH (C)0200H (D)0C00H 8.以下标志位中,INC指令不影响的标志位是() (A)CF (B)AF (C)OF (D)PF 9.假设(AL)=0F2H,执行指令ADD AL, 34H后,OF标志位的取值为() (A)1 (B)0 (C)2 (D)不确定 10.假设(BL)=01H,执行指令SUB BL, 02H后,CF标志位的取值为() (A)1 (B)0 (C)2 (D)3

实验1 汇编语言 数据传送

实验1 数据传送 实验目的 掌握8086指令系统的数据传送指令及8086的寻址方式 利用调试工具来调试汇编语言程序 实验设备 PC微型计算机一台 实验预习要求: 复习8086指令系统的数据传送指令及8086的寻址方式 学习TD.EXE的使用方法 实验内容1: 通过下面的程序段输入和执行来熟悉TD.EXE的使用,通过显示屏观察程序的执行情况。练习程序段如下: MOV BL, 88H MOV CL, BL MOV AX, 9999H MOV DS:[0002H], AX 操作步骤: 1、启动TD.EXE 方法1 直接打开BIN文件夹下TD.EXE文件,方法2 把BIN 文件夹放在根目录下,如在:D:\BIN,打开“开始—附件—命令提示符”用CD命令使当前目录为D:\BIN 然后输入TD 或TD.EXE 2、输入程序段 把光标移到CS:0100H处开始输入程序 在光标处直接输入练习程序段,键入时屏幕上会弹出一个输入窗口,在这个窗口中输入程序段 3、执行程序段 按F8单步执行程序段,观察寄存器内容的变化情况和内存单元DS:[0002H]的内容变化情况 实验内容2: MOV AX, 1111H MOV BX, 2222H MOV CX, 3333H PUSH AX PUSH BX PUSH CX 第一种出栈方式 POP AX

POP BX POP CX 第二种出栈方式 POP BX POP CX POP AX 第三种出栈方式 POP CX POP BX POP AX 操作步骤: 1、启动TD.EXE 2、输入程序段 把光标移到CS:0100H处开始输入程序 在光标处直接输入练习程序段,键入时屏幕上会弹出一个输入窗口,在这个窗口中输入程序段 3、执行程序段 按F8单步执行程序段,观察寄存器内容的变化情况和内存单元DS:[0002H]的内容变化情况 实验报告要求: 1、写明本次实验的执行结果,填好表1-1 2、设置各寄存器及存储单元的内容如下 BX=0010H , SI=0001H DS:[0010H]=12H, DS:[0011H]=34H, DS:[0012H]=56H, DS:[0013H]=78H, DS:[0014H]=9AH, DS:[0015H]=0ABH, DS:[0016H]=0BCH 说明下列各条指令执行完后AX寄存器中的内容,上机验证(观察寄存器和TD数据区—在窗口的左下角)并填好表1-2(注:输入下列指令前应先用MOV指令把各寄存器和在存储单元的内容设置好) (1)MOV AX, 1200H (2)MOV AX, BX (3)MOV AX, [0010H] (4)MOV AX, [BX] (5)MOV AX, 0005H[BX] (6)MOV AX, [BX][SI] (7)MOV AX, 0003H[BX][SI]

相关文档
相关文档 最新文档