




已阅读5页,还剩32页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
ARM 函数调用处理机制ATPCS即ARM-THUMBprocedure call standard的简称。PCS规定了应用程序的函数可以如何分开地写,分开地编译,最后将它们连接在一起,所以它实际上定义了一套有关过程(函数)调用者与被调用者之间的协议。PCS强制实现如下约定:调用函数如何传递参数(即压栈方法,以何种方式存放参数),被调用函数如何获取参数,以何种方式传递函数返回值。PCS的制订是一系列指标的“tradeoff(折衷)”(因为很大程度上涉及系统的一些性 能),如会涉及生成代码的大小,调试功能的支持,函数调用上下文处理速度以及内存消耗。当然,通过编译器的支持可以让生成的代码有不同的特性,如gcc编 译选项可以支持或不支持framepointer来支持深入调试功能或提高程序运行性能。PCS是体系结构密切相关的,直接涉及编译器如何使用处理器提供的应用寄存器,如编译器使用什么寄存器作为栈指针,利用哪些寄存器作直接传参等。值得注意的是,没有谁规定说PCS是必须这样而不是那样的。它是应用相关的。任何一个操作系统和应用可以处于它自身的考虑定义自己的PCS。当然,如果那样,也必须有自己的编译器。而实际上,在一个处理器设计时,都会有某种假设,所以PCS某种程度上应该 是一样的。ATPCS就是基于ARM指令集和THUMB指令集过程调用的规范。ATPCS概述 为了使单独编译的C语言程序和汇编程序之间能够相互调用,必须为子程序之间的调用规定一定的规则.ATPCS就是ARM程序和THUMB程序中子程序调用的基本规则.一.ATPCS概述.ATPCS规定了一些子程序之间调用的基本规则.这些基本规则包括子程序调用过程中寄存器的使用规则,数据栈的使用规则,参数的传递规则.为适应一些特定的需要,对这些基本的调用规则进行一些修改得到几种不同的子程序调用规则,这些特定的调用规则包括:支持数据栈限制检查的ATPCS.支持只读段位置无关的ATPCS.支持可读写段位置无关的ATPCS.支持ARM程序和THUMB程序混合使用的ATPCS.处理浮点运算的ATPCS.有调用关系的所有子程序必须遵守同一种ATPCS.编译器或者汇编器在ELF格式的目标文件中设置相应的属性,标识用户选定的ATPCS类型.对应不同类型的ATPCS规则,有相应的C语言库,连接器根据用户指定的ATPCS类型连接相应的C语言库.使用ADS的C语言编译器编译的C语言子程序满足用户指定的ATPCS类型.而对于汇编语言程序来说,完全要依赖用户来保证各子程序满足选定的ATPCS类型.具体来说,汇编语言子程序必须满足下面三个条件:在子程序编写时必须遵守相应的ATPCS规则;数据栈的使用要遵守ATPCS规则;在汇编编译器中使用-apcs选项.二.基本ATPCS.基本ATPCS规定了在子程序调用时的一些基本规则,包括以下三个方面的内容:各寄存器的使用规则及其相应的名字;数据栈的使用规则;参数传递的规则.相对于其他类型的ATPCS,满足基本ATPCS的程序的执行速度更快,所占用的内存更少.但是它不能提供以下的支持: ARM程序和THUMB程序相互调用;数据以及代码的位置无关的支持;子程序的可重入性;数据栈检查的支持.而派生的其他几种特定的ATPCS就是在基本ATPCS的基础上再添加其他的规则而形成的.其目的就是提供上述的功能.寄存器的使用规则:1.子程序通过寄存器R0R3来传递参数.这时寄存器可以记作: A1A4 ,被调用的子程序在返回前无需恢复寄存器R0R3的内容.2.在子程序中,使用R4R11来保存局部变量.这时寄存器R4R11可以记作: V1V8 .如果在子程序中使用到V1V8的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值,对于子程序中没有用到的寄存器则不必执行这些操作.在THUMB程序中,通常只能使用寄存器R4R7来保存局部变量.3.寄存器R12用作子程序间scratch寄存器,记作ip;在子程序的连接代码段中经常会有这种使用规则.4.寄存器R13用作数据栈指针,记做SP,在子程序中寄存器R13不能用做其他用途.寄存器SP在进入子程序时的值和退出子程序时的值必须相等.5.寄存器R14用作连接寄存器,记作lr ;它用于保存子程序的返回地址,如果在子程序中保存了返回地址,则R14可用作其它的用途.6.寄存器R15是程序计数器,记作PC ;它不能用作其他用途.7. ATPCS中的各寄存器在ARM编译器和汇编器中都是预定义的.参数的传递规则.根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变的子程序.这两种子程序的参数传递规则是不同的.1.参数个数可变的子程序参数传递规则对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0R3来进行参数传递,当参数超过4个时,还可以使用数据栈来传递参数.在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器R0,R1,R2,R3;如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈.按照上面的规则,一个浮点数参数可以通过寄存器传递,也可以通过数据栈传递,也可能一半通过寄存器传递,另一半通过数据栈传递.2.参数个数固定的子程序参数传递规则对于参数个数固定的子程序,参数传递与参数个数可变的子程序参数传递规则不同,如果系统包含浮 点运算的硬件部件,浮点参数将按照下面的规则传递:各个浮点参数按顺序处理;为每个浮点参数分配FP寄存器;分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP寄存器.第一个整数参数通过寄存 器R0R3来传递,其他参数通过数据栈传递.子程序结果返回规则1.结果为一个32位的整数时,可以通过寄存器R0返回.2.结果为一个64位整数时,可以通过R0和R1返回,依此类推.3.结果为一个浮点数时,可以通过浮点运算部件的寄存器f0,d0或者s0来返回. 4.结果为一个复合的浮点数时,可以通过寄存器f0-fN或者d0dN来返回. 5.对于位数更多的结果,需要通过调用内存来传递.ARM函数调用时参数传递规则时间:2009-11-08 21:56来源:PCB设计网作者:网络点击:352次之前在学习如何在C语言中嵌入汇编时有了解到C语言之前的参数调用是使用寄存器R0传递第一个参数,R1传递到第二个.一直到R3传递第四个参数.但是 实际上有时可能传递的参数非常多,超过8个,或是参数中有浮点数之类,参数也会超过4个寄存器,对于超出的部份并不使用 之前在学习如何在C语言中嵌入汇编时有了解到C语言之前的参数调用是使用寄存器R0传递第一个参数,R1传递到第二个.一直到R3传递第四个参数.但是 实际上有时可能传递的参数非常多,超过8个,或是参数中有浮点数之类,参数也会超过4个寄存器,对于超出的部份并不使用R4,而是使用堆栈的方式,但具体 是如何的方式很多网站就没了下文了,好在在GG的帮助下,让我在凌晨1.30找到了(为啥老是在半夜呢?)对于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_argstest_asm_args STR lr, sp, #-4!;保存当前lr ldr r0,=010 ;参数 1 ldr r1,=020 ;参数 2 ldr r2,=030 ;参数 3 ldr r3,=040 ;参数 4 bl test_c_args ;调用C函数 LDR pc, sp, #4;将lr装进pc(返回main函数) ENDtest_c_args.c/void test_c_args(int a,int b,int c,int d) printk(”test_c_args:n”); printk(”%0x %0x %0x %0xn”,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定义了两个函数,test_asm_args 和 test_c_args,test_asm_args调用了test_c_args,其参数的传递方式就是向R0R3分别写入参数值,之后使用bl语句 对test_c_args进行调用。其中值得注意的地方是用红色标记的语句,test_asm_args在调用test_c_args之前必须把当前的 lr入栈,调用完test_c_args之后再把刚才保存在栈中的lr写回pc,这样才能返回到main函数中。如果test_c_args的参数是8个呢?这种情况test_asm_args应该怎样传递参数呢?实例2:test_asm_args.asm/ IMPORT test_c_args;声明test_c_args函数 AREA TEST_ASM, CODE, READONLY EXPORT test_asm_argstest_asm_args STR lr, sp, #-4!;保存当前lr ldr r0,=01;参数 1 ldr r1,=02;参数 2 ldr r2,=03;参数 3 ldr r3,=04;参数 4 ldr r4,=08 str r4,sp,#-4! ;参数 8 入栈 ldr r4,=07 str r4,sp,#-4! ;参数 7 入栈 ldr r4,=06 str r4,sp,#-4! ;参数 6 入栈 ldr r4,=05 str r4,sp,#-4! ;参数 5 入栈 bl test_c_args_lots ADD sp, sp, #4 ;清除栈中参数 5,本语句执行完后sp指向 参数6 ADD sp, sp, #4 ;清除栈中参数 6,本语句执行完后sp指向 参数7 ADD sp, sp, #4 ;清除栈中参数 7,本语句执行完后sp指向 参数8 ADD sp, sp, #4 ;清除栈中参数 8,本语句执行完后sp指向 lr LDR pc, sp,#4 ;将lr装进pc(返回main函数) ENDtest_c_args.c/void test_c_args(int a,int b,int c,int d,int e,int f,int g,int h) printk(”test_c_args_lots:n”); printk(”%0x %0x %0x %0x %0x %0x %0x %0xn”, a,b,c,d,e,f,g,h);main.c/int main() test_asm_args(); for(;);这部分的代码和实例1的代码大部分是相同的,不同的地方是test_c_args的参数个数和test_asm_args的参数传递方式。在test_asm_args中,参数1参数4还是通过R0R3进行传递,而参数5参数8则是通过把其压入堆栈的方式进行传递,不过要注意这四个入栈参数的入栈顺序,是以参数8-参数7-参数6-参数5的顺序入栈的。直到调用test_c_args之前,堆栈内容如下:sp-+-+ |参数5| +-+ |参数6| +-+ |参数7| +-+ |参数8| +-+ | lr | +-+test_c_args执行返回后,则设置sp,对之前入栈的参数进行清除,最后将lr装入pc返回main函数,在执行LDR pc, sp,#4 指令之前堆栈内容如下: +-+ |参数5| +-+ |参数6| +-+ |参数7| +-+ |参数8|sp-+-+ | lr | +-+但实际上可能不同的编译器可能用着不同的处理方式,于我们所使用的编译器我们可以写一个简单的代码,调用10个参数的函数,然后升成汇编再查看它是如何处理,这样再根据编译器进行特殊的优化.1、读程序,回答问题int main(int argc,char *argv)int c=9,d=0;c=c+%5;d=c;printf(d=%dn,d);return 0;a)、写出程序的结果;b)、在一个可移植的系统中这种表达式是否存在风险?why?答:a)、4 b)、存在风险,因为c=c+%5;在这个表达式中,对C有两次修改,行末未定义,c的值不明确。2、#include stdio.hint a=0; /data sectionint b; /data sectionstatic char c; /BSSint main(int arg c,char *argv)char d=4; /stackstatic short e;/BSSa+;b=100;c=(char)+a;e=(+d)+;printf(a=%d, b=%d, c=%d, d= %d, e=%d,a,b,c,d,e);return 0;a) 写出程序输出b) 编译器如果安排各个变量(a,b,c,d)在内存中的布局(eg. stack,heap,data section,bss section),最好用图形方式描述。答: a=2 b=100 c=2 d=6 e=53 C/C+基础知识问题a) 关键字volatile在编译时有什么含义?并给出三个不同使用场景的例子(可以伪代码或者文字描述)。b) C语言中static关键字的具体作用有哪些 ?c) 请问下面三种变量声明有何区别?请给出具体含义int const *p;int* const p;int const* const p;答:a)、用volatile关键字定义变量,相当于告诉编译器,这个变量的值会随时发生变化,每次使用的时候都需要去内存里面重新读取他的值,并不要随意去针对他做优化。建议使用volatile关键字的地方:1、并行设备的硬件寄存器2、一个中断服务子程序中会访问到的非自动变量3、多线程应用中被几个任务共享的非自动变量b) 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。 static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用; static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值; static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝c) 一个指向常整型数的指针 一个指向整型数的常指针 一个指向常整型数的常指针4 嵌入式系统相关问题a) 对于整形变量A=0x12345678,请画出在little endian及big endian的方式下在内存中是如何存储的。b) 在ARM系统中,函数调用的时候,参数是通过哪种方式传递的?c) 中断(interrupt,如键盘中断)与异常(exception,如除零异常)有何区别?a)little: 高地址 0x12 0x34 0x56 0x78 低地址big: 低地址 0x12 0x34 0x56 0x78 高地址b)参数4的通过压栈方式传递。具体参数传递见下一篇。c) 异常:在产生时必须考虑与处理器的时钟同步,实践上,异常也称为同步中断。在处理器执行到由于编程失误而导致的错误指令时,或者在执行期间出现特殊情况(如缺页),必须靠内核处理的时候,处理器就会产生一个异常。所谓中断应该是指外部硬件产生的一个电信号,从cpu的中断引脚进入,打断cpu当前的运行;所谓异常,是指软件运行中发生了一些必须作出处理的事件,cpu自动产生一个陷入来打断当前运行,转入异常处理流程。1、 如何在C中初始化一个字符数组。2、 如何在C中为一个数组分配空间。3、 如何初始化一个指针数组。4、如何定义一个有10个元素的整数型指针数组。5、 s10的另外一种表达方式是什么。6、 GCC3.2.2版本中支持哪几种编程语言。7、 要使用CHAR_BIT需要包含哪个头文件。8、 对(-1.2345)取整是多少?9、 如何让局部变量具有全局生命期。10、C中的常量字符串应在何时定义?11、如何在两个.c文件中引用对方的变量。12、使用malloc之前需要做什么准备工作。13、realloc函数在使用上要注意什么问题。14、strtok函数在使用上要注意什么问题。15、gets函数在使用上要注意什么问题。16、C语言的词法分析在长度规则方面采用的是什么策略?17、a+b所表示的是什么意思?有什么问题?18、如何定义Bool变量的TRUE和FALSE的值。19、C语言的const的含义是什么。在定义常量时,为什么推荐使用const,而不是#define。20、C语言的volatile的含义是什么。使用时会对编译器有什么暗示。这部分是ANSI C的一些问题,题目的前提是必须都答对,看似很变态,但是细想一下,这些都是最基础的,虽然我们在使用他们的时候会犯这样那样的错误,但是最终目的是不犯错误,不是么,那么好,从最基础的开始。1、 如何在C中初始化一个字符数组。这个问题看似很简单,但是我们要将最简单的问题用最严谨的态度来对待。关键的地方:初始化、字符型、数组。最简单的方法是char array;。这个问题看似解决了,但是在初始化上好像还欠缺点什么,个人认为:char array5=1,2,3,4,5;或者char array5=12345;或者char array210=China,Beijing;也许更符合“初始化”的意思。2、 如何在C中为一个数组分配空间。最简单的方法是:char array5;意思是分配给数组array一个5个字节的空间。但是我们要知道在C中数组其实就是一个名字,其实质含义就是指针,比如char array;是到底分配的多少空间?所以我们要将其分成为两种不同的形式给出答案:一种是栈的形式:char array5;一种是堆的形式:char *array; array=(char *)malloc(5);/C+: array=new char5;堆和栈的含义其实我也没弄太透彻,改天明白了再发一篇。我们要明白的是,第一种形式空间分配的大小可能会受操作系统的限制,比如windows会限制在2M;第二种形式成空间分配很灵活,想分配多少分配多少,只要RAM够大。3、 如何初始化一个指针数组。首先明确一个概念,就是指向数组的指针,和存放指针的数组。指向数组的指针:char (*array)5;含义是一个指向存放5个字符的数组的指针。存放指针的数组:char *array5;含义是一个数组中存放了5个指向字符型数据的指针。按照题意,我理解为初始化一个存放指针的数组,char *array2=China,Beijing;其含义是初始化了一个有两个指向字符型数据的指针的数组,这两个指针分别指向字符串China和Beijing。4、如何定义一个有10个元素的整数型指针数组。既然只是定义而不是初始化,那就很简单且没有争议了:int *array10;。5、 s10的另外一种表达方式是什么。前面说过了,数组和指针其实是数据存在形态的两种表现形式,如果说对于数组s,我们知道*s=s0,那么s10的另一种表达方式就是:*(s+10)。6、 GCC3.2.2版本中支持哪几种编程语言。这个问题实在变态,就像问你#error的作用是什么一样。不可否认,gcc是linux下一个亮点,是一个备受无数程序员推崇的编译器,其优点省略1000字,有兴趣可以自己查,我翻了翻书,书上曰:支持C,C+,Java,Obj-C,Ada,Fortran,Pascal,Modula-3等语言,这个“等”比较要命,不过我认为已经很全了,如果认为还是不全,干脆把ASM也加上算了,不过那已经不算是编译了。7、 要使用CHAR_BIT需要包含哪个头文件。如果结合上面的问题,答题的人估计会认为自己撞鬼了,这个问题实在是搜索了一下,应该是limits.h。8、 对(-1.2345)取整是多少?其实不同的取整函数可能有不同的结果,不过这个数没有太大的争议,答案是-1。9、 如何让局部变量具有全局生命期。具体的生命期的概念我觉得我还要好好深入的学习一下,但是这个题目还算比较简单,即用static修饰就可以了,但是只是生命期延长,范围并没有扩大,除非把这个变量定义在函数体外的静态区,不过那样就变成全局变量了,仿佛不符合题目要求。10、C中的常量字符串应在何时定义?这个问题说实话不是很理解题干的意思,据我理解,有两种情况,一种是预处理阶段,用#define定义;还有就是使用const修饰词,不过const修饰的是一个变量,其含义是“只读”,称之为常量并不准确,但是确实可以用操作变量的方法当常量用。所以还是第一种比较靠谱。11、如何在两个.c文件中引用对方的变量。这个问题也问的挺含糊的,怎么说呢,最简单最直接的方法是为变量添加extern修饰词,当然,这个变量必须是全局变量。还有一种就是利用函数调用来进行变量的间接引用,比如这个C文件中的一个函数引用另外一个C中的函数,将变量通过实参的形式传递过去。不过题目既然说是引用,那么还是用第一个答案好了。12、使用malloc之前需要做什么准备工作。其实准备工作很多啊,比如你需要一台计算机之类的。玩笑话,我们首先要知道malloc的用途,简单的说就是动态的分配一段空间,返回这段空间的头指针。实际的准备工作可以这么分:需要这段空间的指针是否存在,若不存在,则定义一个指针用来被赋值,还要清楚要返回一个什么类型的指针,分配的空间是否合理;如果指针已经存在,那么在重新将新的空间头地址赋值给这个指针之前,要先判断指针是否为NULL,如果不是要free一下,否则原来的空间就会被浪费,或者出错,free之后就按照前一种情形考虑就可以了。13、realloc函数在使用上要注意什么问题。这个函数我也才知道的,汗一个。据我的初步理解,这个函数的作用是重新分配空间大小,返回的头指针不变,只是改变空间大小。既然是改变,就有变大、变小和为什么改变的问题。变大,要注意不能大到内存溢出;变小,那变小的那部分空间会被征用,原有数据不再存在;为什么改变,如果是想重新挪作他用,还是先free了吧。14、strtok函数在使用上要注意什么问题。这个问题我不知道能不能回答全面,因为实在是用的很少。这个函数的作用是分割字符串,但是要分割的字符串不能是常量,这是要注意的。比如先定义一个字符串:char array=part1,part2;,strtok的原形是char *strtok(char *string, char *delim);,我们将,作为分隔符,先用pt=strtok(array,);,得到的结果print出来就是part1,那后面的呢,要写成pt=strtok(NULL,);,注意,要用NULL,如果被分割的字符串会被分成N段,那从第二次开始就一直要用NULL。总结起来,需要注意的是:被分割的字符串和分隔符都要使用变量;除第一次使用指向字符串的指针外,之后的都要使用NULL;注意使用这个函数的时候千万别把指针跟丢了,不然就全乱了。15、gets函数在使用上要注意什么问题。这是一个键盘输入函数,将输入字符串的头地址返回。说到要注意的问题,我还是先查了一下网上的一些情况,需要注意的就是gets以输入回车结束,这个地球人都知道,但是很多人不知道的是,当你输入完一个字符串后,这个字符串可能依然存在于这个标准输入流之中,当再次使用gets的时候,也许会把上次输入的东西读出来,所以应该在使用之后用fflush(stdin);处理一下,将输入流清空。最后也还是要注意溢出的问题。关于这个答案我比较含糊,不知道有没有高人高见?16、C语言的词法分析在长度规则方面采用的是什么策略?我无语闻所未闻啊还是搜索了一下,有一篇文章,地址是:/jp2005/20/kcwz/wlkc/wlkc/03/3_5_2.htm,是关于词法分析器的。其中提到了两点策略: (1) 按最长匹配原则确定被选的词型;(2) 如果一个字符串能为若干个词型匹配,则排列在最前面的词型被选中。不知道是不是题干的要求,还是其他什么。我乃一介草民,望达人指点迷津!17、a+b所表示的是什么意思?有什么问题?这个东西(称之为东西一点都不过分)其实并没有语法错误,按照C对运算符等级的划分,+的优先级大于+,那么这句话会被编译器看做:(a+)+(+b),这回明白了吧。有什么问题,语法上没有问题,有的是道德上的问题!作为一个优秀的程序员,我们要力求语句的合法性和可读性,如果写这句的人是在一个team里,那么他基本会被打的半死最后讨论一下结果:假设a之前的值是3,b是4,那么运行完这个变态语句后,a的值是4,b是5,语句的结果是8。18、如何定义Bool变量的TRUE和FALSE的值。不知道这个题有什么陷阱,写到现在神经已经大了,一般来说先要把TURE和FALSE给定义了,使用#define就可以:#define TURE 1#define FALSE 0如果有一个变量需要定义成bool型的,举个例子:bool a=TURE;就可以了。19、C语言的const的含义是什么。在定义常量时,为什么推荐使用const,而不是#define。首先,这个题干抽了10题回答的一个大嘴巴。关于常量的概念看来我要好好看看书了我说过了,const修饰词可以将一个变量修饰为“只读”,这个就能称为常量么?姑且认为可以。回到题目中,const是只读的意思,它限定一个变量不允许被改变,谁都不能改!既然是修饰变量,那么变量的类型就可以丰富多彩,int啊,char啊,只要C认识的都可以;但是#define就不可以了,在预处理阶段缺乏类型检测机制,有可能会出错。还有就是变量可以extern,但是#define就不可以。貌似const还可以节省RAM,这个我倒是没有考证过。至于const的用法和作用,有很多,我会总结后发上来。20、C语言的volatile的含义是什么。使用时会对编译器有什么暗示。终于最后一题了,容易么如果这个测试是一个关于嵌入式的,那么这道题非常重要!从词面上讲,volatile的意思是易变的,也就是说,在程序运行过程中,有一些变量可能会被莫名其妙的改变,而优化器为了节约时间,有时候不会重读这个变量的真实值,而是去读在寄存器的备份,这样的话,这个变量的真实值反而被优化器给“优化”掉了,用时髦的词说就是被“和谐”了。如果使用了这个修饰词,就是通知编译器别犯懒,老老实实去重新读一遍!可能我说的太“通俗”了,那么我引用一下“大师”的标准解释:volatile的本意是“易变的” 。由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化,但有可能会读脏数据。当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:1). 并行设备的硬件寄存器(如:状态寄存器)2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)3). 多线程应用中被几个任务共享的变量嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难好了,答完了,也不知道标准答案是什么。如果有达人看到皱眉头的话,千万停下来,浪费您宝贵的几分钟时间指点一二,不胜感激!、匈牙利命名法有什么优缺点?(2分)2、下面x, y, *p的值是多少,有什么问题?(2分)int x, y, z = 2;int *p=&z;x=sizeof*p;y=x/*p; / x=?, *p=?, y=?, 有什么问题?3、下面的语句是什么意思?如何声明或定义才使它们更易懂?(10分)int (*foo()();int (*foo();int (*foo)();(*(void(*)()0)();void (*signal(int,void(*)(int)(int);4、本题(2分)。一般使用malloc时,需要进行强制类型转换,如:char *s; s = (char *)malloc(31);下面中?该如何填写,才可以正确执行强制类型转换?int (*monthp)31; monthp = (?)malloc(31);5、关于C语言运算符优先级的记忆技巧是什么?(2分)/ 下面r的值是多少int hi, low, r;hi=7;low=3;r=hi4+low;6、指针和数组的区别是什么?用一个简单的声明把它们区分开。(2分)指针和数组的声明在什么情况下是相同的?(2分)7、C语言的左值(lvalue)和右值(rvalue)的含义是什么?(2分)8、为什么C语言可以实现printf(char *format, .)这样可变参数的调用形式?这样有什么缺点?(2分)9、说明C语言中术语声明定义原型的含义?(2分)10、举一个例子,说明使用assert和防错代码的区别。(5分)11、对语句 if else 与操作符 ? : 使用场合的比较。(2分)12、编写一个函数,输入一个的整型数字,可以选择按照8/10/16进制输出字符串。注意边界值。(5分)13、本题(2分)。下面是一个16x16的黑白图标:static unsigned short stopwatch = 0x07c6,0x1ff7,0x383b,0x600c,0x600c,0xc006,0xc006,0xdf06,0xc106,0xc106,0x610c,0x610c,0x3838,0x1ff0,0x07c0,0x0000,;如何修改声明,可以使之在源代码中形象地表现出图形的模样。14、说出可以使用calendar1130变量的四种类型定义。(5分)如:int calendar1231;/ 二维数组15、使用strcmp,当字符串相同时会返回0。但0一般作为逻辑假,因此下面的语句不容易理解:if (!strcmp(s, string) return EQUATION;如何经过简单修改,使之更易懂?(2分)16、编写一个自己的完全C语言版本的memset函数,并且评价这个实现的性能和可移植性。(5分)17、在树和图这些数据结构中,通常使用指针来组织数据。如果我们要把这些数据保存到文件中,指针是没有意义的。我们该如何解决这个问题。(2分)18、用2种不同的方法计算long变量的1bit的个数。(2分)19、任意给出一个C的基本数据类型,如何编码判断这个数据类型是有符号还是无符号的?(2分)不得上机实验,写出下面代码的输出。解释这个行为是标准定义的,还是依赖实现的。(2分)int i;for (i = 0; i 10; i+) int j = i;printf (%dn, j);20、列出5种以上你所看过的C编程的书籍,并写简要书评。(5分)对C的评价。如果要你改造一把菜刀,使之更加安全,你是否会使用这样的菜刀,为什么?(5分)-华丽的分割线-1、匈牙利命名法有什么优缺点?(2分)首先,我们要知道有这么个东西,因为这可能是目前公认的程序员的良好素质。匈牙利命名法是一种编程时的命名规范。基本原则是:变量名属性类型对象描述,其中每一对象的名称都要求有明确含义,可以取对象名字全称或名字的一部分。匈牙利命名法是一位叫 Charles Simonyi 的匈牙利程序员发明的,后来他在微软呆了几年,于是这种命名法就通过微软的各种产品和文档资料向世界传播开了。然后,我们来讨论一下优点。虽然我很少涉足OOP,但是对这个东西多少知道一点。匈牙利命名法非常便于记忆,而且使变量名非常清晰易懂,这样,增强了代码的可读性,方便各程序员之间相互交流代码。而且由于是从微软出来的东西,现在又变成了世界范围内的编码规范,所以使在熟悉这一命名法的程序员之间交流代码变得轻松。我们一直强调要有良好的程序风格,而这就需要从命名开始。最后,我们再来讨论一下缺点。其实很少人说名压力命名法的缺点,所以我认为这道题的目的并不是让你骨头里挑刺,而是要看你有没有“怀疑一切,否定一切”的态度,是要看你是不是喜欢逆来顺受,要看你是不是能对一件看似既定的事情说“NO!”。那么好,给你一个机会,但也不能太离谱。凡是都有两面性,在匈牙利命名法给我们带来清晰的命名规范的同时,我们不得不承认,它的命名成本是很高的。比如一个变量,可能在程序里会出现100次,在debug阶段的时候忽然觉得它应该是另一个类型,那么好,噩梦开始,你要修改100遍啊100遍!还有匈牙利命名法的收益,有一些简单的不能再简单,清楚的不能再清楚的变量,非要加上一串帽子,即便是一个简单的函数和变量,我们是不是也要停下来仔细琢磨一下它想传递一个什么值呢?在类型越来越多,越来越复杂的情况下,匈牙利命名法可能会画蛇添足,火上浇油,比如一个名为ppp的帽子,想说明什么?是Point to Point Protocol么?显然不是,它的意思是指向指针的指针的指针,晕吧。好了,大致是这样,每个人都有心中的不满,导致这个题目没有标准答案,就像我说的,这道题的目的在于你是不是能够说出“不”,而不是走大众路线。引用AMD创始人Jerry Sanders的一句名言:只有偏执狂才能生存。我们虽然不偏执,但是总要有自己的想法和愤怒,不是么。给出一篇参考文献,值得仔细品味:/view/419474.htm2、下面x, y, *p的值是多少,有什么问题?(2分)int x, y, z = 2;int *p=&z;x=sizeof*p;y=x/*p; / x=?, *p=?, y=?, 有什么问题?有什么问题我想说的是,如果你按这个写出来,肯定编译不过去。为什么?最明显的:y=x/*p;,这是什么东西?为p;做个注释?不至于懒得加一个括号吧,y=x/(*p);。类似的问题,x=sizeof(*p);是不是更好看,这是编码素质的问题。好了,解决了这个问题,可以编译了,但是我们需要一个前提:你的宿主机是多少位的?下面我们用32bits举例,因为嵌入式linux都是跑在32bits的ARM上的。一句一句来:int x, y, z = 2; /x=?,y=?,z=2int *p=&z; /等价于int *p;p=&z; 所以p为指向z的数据的地址,*p=z=2x=sizeof(*p);/考点来了,32bits的CPU运行完这句,x=4,同理,如果换成16bits的,x=2y=x/(*p);/y=4/2=2那么最后的结果就是:x=2,y=2,*p=2。很简单吧。3、下面的语句是什么意思?如何声明或定义才使它们更易懂?(10分)int (*foo()();int (*foo();int (*foo)();(*(void(*)()0)();void (*signal(int,void(*)(int)(int);第一个:int (*foo()();先一步一步的分析:首先是被左边的()括起来的*foo()。(1) 里面()的优先级高于*,所以foo()先是一个函数;(2) *foo()说明这个函数返回一个指针;(3) (*foo()()这个函数返回的指针指向一个函数;(4) int (*foo()();(5) 最后这个函数返回一个整型数。好了,我们来一遍全的:这个函数的含义是foo()函数返回的指针指向一个函数,该函数返回一个整型数。(我汗啊)第二个:int (*foo();有上面的打基础了,这回好多了。大致还是那个意思,但是就是由函数变成了数组,所以还更简单些,具体的含义是:foo()函数返回的指针指向一个具有整型数的数组。第三个:int (*foo)();这个就是上两个的翻版:一个存有指针的数组*foo,该指针指向一个函数,该函数返回一个整型数。第四个:(*(void(*)()0)();明显比上面的复杂,但是不要怕,还是那样,一层一层的分析:(1) 最里面的(*)()意思是一个指向函数的指针;(2) 加个前缀void(*)(),表示一个指向返回类型为void的函数的指针;(3) 套个马甲(void(*)()的意思就是类型强制转换;(4) (void(*)()0就是把0强制转换成前面说的那个类型的指针;(5) 那么好,现在(void(*)()0是一个指针了,这个指针指向了一个函数,我们用宏定义简化一下,看着太麻烦:(6) #define ptr (void (*)()0 ,现在(*(void(*)()0)();这个怪物就可以简化成:(*ptr)();(7) 别忘了ptr是一个指向函数的指针,既然这样*ptr
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 站务员边门管理课件下载
- 心理健康课教学课件
- 心理健康课件获奖证书
- 2025版纺织品行业社会责任履行合作协议
- 二零二五年度二手房买卖合同公证操作中的法律咨询与支持
- 二零二五年高端餐饮服务定制合同书
- 二零二五年度工程居间佣金结算及项目进度关联合同
- 2025版电机产品售后服务与维护合同样本
- 2025年度国际贸易借款合同及担保协议执行细则
- 2025年度绿色节能建筑砍割桩施工专项合同
- DB63∕T 2330-2024 小微企业融资信用评价规范
- 2025四川省安全员B证考试题库附答案
- 钢结构工程施工安全要点
- 停呼等三原则培训课件
- 2025年广西中考数学真题试卷及答案
- MT/T 1212-2024煤矿信息综合承载网通用技术规范
- 氢能产业链中的区块链技术如何助力碳足迹认证
- 2025年福建省高考物理试卷真题(含答案解析)
- 2025年《民航服务心理学》课程标准(含课程思政元素)
- 事业单位请假新版制度管理统一规定
- 放疗基本知识介绍-1
评论
0/150
提交评论