当前位置: 首页 > 科技观察

面试中常被问到的16道C语言题,你能答对几道?

时间:2023-03-21 13:32:24 科技观察

1。使用预处理指令#define声明一个常量表示一年有多少秒(忽略闰年问题)#defineSEC_YEAR(365*24*60*60)UL检查要点:#define语法基础知识(例如,不以分号结尾、使用括号等)知道预处理器将为您评估常量表达式,因此与其只写出您计算一年中秒数的实际值,更清晰,而且没有任何成本。意识到这个表达式将溢出一个16位整数-因此长符号L告诉编译器常量是一个长整数。如果你使用UL(代表一个无符号长整数),那么你就有了一个很好的起点。请记住,第一印象很重要。呃。2.写一个“标准”的宏MIN,接受两个参数,返回较小的一个#defineMIN(a,b)((a)<=(b)?(a):(b))检查点:识别基本在宏中应用#define的知识。这个非常重要。由于在内联运算符成为标准C的一部分之前,宏是方便地生成内联代码的唯一方法,因此内联代码通常是嵌入式系统实现所需性能所必需的。知道如何在宏中小心地括起参数我也从这个问题开始讨论宏的副作用,例如:当您编写以下代码时会发生什么?最少=MIN(*p++,b);宏定义的结果MIN(*p++,b)((*p++)<(b)?(*p++),b)指针p会做两次自增操作howtoeliminateMacrosideeffects#include#definemin_i(x,y)((x)<=(y)?(x):(y))//(1)#definemin_t(type,x,y)({type_x=x;\//(2)type_y=y;\_x<_y?_x:_y;\})#definemin(x,y){consttypeof(x)_x=(x);\//(3)consttypeof(y)_y=(y);\(void)(&_x=&_y);\//(4)_x<_y?_x:_y;\})intmain(){inta=10;整数b=20;printf("min_i(a++,b++)=%d\n",min_i(a++,b++));//11printf("a=%d\n",a);//12printf("b=%d\n",b);//21a=10;b=20;printf("min_t(int,a++,b++)=%d\n",min_t(int,a++,b++));//10printf("a=%d\n",a);//11printf("b=%d\n",b);//21a=10;b=20;printf("min(a++,b++)=%d\n",min(a++,b++));//10打印(“a=%d\n",a);//11printf("b=%d\n",b);//21}这个定义分别计算x和y两次(x和y中较小的被计算两次),当参数有副作用时,会产生不正确的结果。使用语句表达式只对参数进行一次计算,避免可能出现的错误。语句表达式通常在宏定义中使用typeof(x)来表示值x的类型,检查参数x和y的类型是否相同(如果x和y的类型不同,编译器会发出警告,不影响后面语句的运行。3.什么是预处理器标志#error的作用是什么?在编译程序的时候,只要遇到#error,就会弹出编译错误。既然是编译错误,那你要它做什么呢?它的目的是确保程序按照你想象的编译出来,这里举个例子:程序中往往有很多预置的处理指令。#ifdefXXX...#else...#endif当程序比较大时,往往会在外部指定一些宏定义(如makefile),或者在系统头文件中指定。在判断当前是否定义了XXX时,可以这样编译:#ifdefXXX...#error"XXXhasbeendefined"#else#endif这样编译时如果出错,则输出XXXhasbeendefined,表示已经定义了宏XXX。4.死循环在嵌入式系统中经常用到,请问C语言怎么写死循环?while(1){}for(;;){}循环:...转到循环;5.用变量a给出如下定义a)一个整数(Aninteger)b)一个整数指针(Apointertoaninteger)c)一个指针指针,它指向一个整数指针(Apointertoapointertoaninteger)d)Anarrayof10integers(Anarrayof10integers)e)Anarrayof10pointers,指针指向一个Integer。(一个包含10个整数的指针的数组)f)一个包含10个整数的数组的指针g)一个指向接受整数参数并返回整数的函数的指针(指向接受整数作为参数并返回整数的函数的指针一个整数)h)一个由10个指针组成的数组,这些指针指向一个以整数作为参数并返回一个整数的函数(一个由十个指针组成的数组,这些指针指向一个以整数作为参数并返回一个整数的函数)a)inta;b)int*a;c)int**a;d)inta[10]e)int*a[10]f)int(*a)[10]g)int(*a)(inta)h)int(*a[10])(int)六。关键字static的作用是什么?在C语言中,关键字static有三个明显的作用:一是在修改变量时,static修饰的static局部变量只执行一次,延长了局部变量的生命周期,直到执行完才释放程序结束。第二,static修饰全局变量时,该全局变量只能在本文件中访问,在其他文件中不能访问,即使是extern外部声明。第三,static修饰一个函数,这个函数只能在本文件中调用,不能被其他文件调用。静态修饰的局部变量存放在全局数据区的静态变量区。初始化时自动初始化为0;(1)当不想被释放时,可以使用静态修饰。例如,数组存放在修改函数中的栈空间中。如果不想在函数调用结束时释放这个数组,可以使用static修饰(2)考虑数据安全(当程序要使用全局变量时,首先要考虑使用static)另外C在C++中的作用,static关键字在类中也有使用。在类中,static可以用来修饰静态数据成员和静态成员方法。共享成员,它在内存中只占用一块空间,如果改变它的值,每个对象中这个数据成员的值都会改变。(2)静态数据成员在程序开始运行时分配空间,程序结束后释放。只要在类中指定了静态数据成员,即使没有定义对象,也会为静态数据成员分配空间。(3)静态数据成员可以初始化,但只能在类外进行。如果没有给静态数据成员赋初值,编译器会自动将它们初始化为0。(4)静态数据成员可以通过对象名来引用,也可以通过类名来引用。静态成员函数(1)静态成员函数与静态数据成员一样,都是属于类的静态成员,而不是对象成员。(2)非静态成员函数有this指针,而静态成员函数没有this指针。(3)静态成员函数主要用于定位静态数据成员,不能访问非静态成员。7.关键字const是什么意思?只要用const修饰一个变量,就意味着该变量中的数据只能被访问,不能被修改,也就是说const是“只读的”。规则:谁离const最近,谁就不能被修改;当const修饰一个变量时,必须为这个变量初始化。如果没有初始化,后面就不能初始化了。const的作用:可以用来定义常量,修改函数参数,修改函数返回值,const修饰的东西受到强制保护,可以防止其他代码被无意中修改,从而提高程序的健壮性(指系统对于超出标准要求的输入,可以判断输入不符合标准要求,并有合理的处理方式。ps:所谓高手写的程序不容易死);让编译器保护那些不想修改的参数,防止无意的代码修改,减少bug;向阅读代码的人传递有用的信息,声明一个参数用于告诉用户这个参数的应用用途;const优点:编译器可以对const进行类型安全检查(所谓类型安全检查可以将程序集相互隔离,这种隔离可以保证程序集之间不会产生负面影响,提高程序的可读性程序);一些集成的调试工具可以调试const常量,让编译器更了解处理内容,消除一些隐患。eg:voidhanshu(constinti){......}编译器会知道i是一个不允许被修改的常量,这样可以节省空间,避免不必要的内存分配,因为编译器通常不会常量分配内存空间,但保存在符号表中,这样读内存中就没有存储操作,提高了效率;修改和调整参数非常方便,同时避免出现不明确的数字8.关键字volatile是什么意思?并给出三个不同的例子。定义为volatile的变量意味着这个变量可能会被意外改变,这样编译器就不会假定这个变量的值。准确地说,优化器每次使用它时都必须仔细地重新读取这个变量的值,而不是使用存储在寄存器中的备份。以下是volatile变量的几个例子:并行设备的硬件寄存器(如:状态寄存器)中断服务子程序中访问的非自动变量(Non-automaticvariables),多线程应用程序中多个任务共享的变量九。嵌入式系统总是需要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一段设置a的第3位,第二段清除a的第3位。以上两个操作中,其他位保持不变。检查点:不知如何下手。受访者从未做过任何嵌入式系统方面的工作。使用位域。位域是被扔进C语言死胡同的东西。它确保您的代码不可在不同编译器之间移植,并且还确保您的代码不可重用。最近不幸看到英飞凌为其比较复杂的通信芯片写的一个驱动,里面用到了位域,对我来说完全没用,因为我的编译器是用其他方式实现位域的。道德:永远不要让一个非嵌入式的人站在实际硬件的一边。使用#defines和位掩码进行操作。这是一种非常便携的方法,也是应该使用的方法。最佳方案如下:#defineBIT3(0x1<<3)staticinta;voidset_bit3(void){a|=BIT3;}voidclear_bit3(void){a&=~BIT3;}十。嵌入式系统通常具有要求程序员访问特定内存位置的功能。在某项目中,需要将一个绝对地址为0x67a9的整型变量的值设置为0xaa66。该编译器是一个纯ANSI编译器。编写代码来完成这个任务。要查找的内容:此问题测试您是否知道将整数类型转换为指针以访问绝对地址是合法的。int*ptr;ptr=(int*)0x67a9;*ptr=0xaa55;......*(int*const)(0x67a9)=0xaa66;11。中断是嵌入式系统的重要组成部分,这导致许多编译器开发人员提供扩展以允许标准C支持中断。表示创建了新关键字__interrupt的事实。下面的代码使用了__interrupt关键字来定义一个中断服务子程序(ISR),请评论这段代码。__interruptdoublecompute_area(doubleradius){doublearea=PI*radius*radius;printf("\n面积=%f",面积);returnarea;}检查点:ISR不能返回值。如果您不了解这一点,那么您将不会被录用。ISR不能传递参数。如果你没有看到这个,你被录用的机会和第一项一样。在许多处理器/编译器中,浮点数通常是不可重入的。一些处理器/编译器需要将寄存器压入堆栈的前端,而一些处理器/编译器不允许在ISR中进行浮点运算。此外,ISR应该简短高效,在ISR中进行浮点运算是不明智的。与第三点相符,printf()经常会出现重入和性能问题。如果你输了第三点和第四点,我就不会给你太大的麻烦了。不用说,如果你能拿到最后两点,你的就业前景会越来越好。12.以下代码的输出是什么,为什么?voidfoo(void){unsignedinta=6;整数b=-20;(a+b>6)?puts(">6"):puts("<=6");}检查点:本题考你了解C语言整数自动转换的原理吗,我发现有些开发者对这些东西了解的很少。无论如何,无符号整数问题的答案是输出为“>6”。原因是当表达式中存在有符号和无符号类型时,所有操作数都会自动转换为无符号类型。所以-20变成了一个非常大的正整数,所以表达式的计算结果大于6。这对于经常使用无符号数据类型的嵌入式系统来说非常重要。如果你答错了这个问题,你就处于找不到工作的边缘。十三。评估以下代码片段:unsignedintzero=0;unsignedintcompzero=0xFFFF;/*1的零的补码*/检查点:对于int类型不是16位的处理器,上面的代码是不正确的。应该这样写:unsignedintcompzero=~0;这个问题真的可以揭示候选人是否理解处理器字长的重要性。根据我的经验,优秀的嵌入式程序员非常准确地了解硬件的细节及其局限性,而PC程序通常将硬件视为不可避免的烦恼。在这个阶段,候选人要么完全气馁,要么相信他们会成功。如果很明显应试者不是很好,那么测试到此结束。但是如果明确考生做的好,那我就抛出下面的附加题,难度比较大,我觉得只有非常优秀的考生才能做的好。通过提出这些问题,我想更多地了解应试者如何处理问题,而不是答案。反正你把它当成娱乐就好了……十四。虽然不像非嵌入式计算机那样普遍,但嵌入式系统仍然有从堆中动态分配内存的过程。在嵌入式系统中,动态分配内存有什么问题?在这里,我希望候选人提到内存碎片、垃圾收集问题、可变持续时间等。ESP杂志(主要是P.J.Plauger,他的解释远远超过我在这里所能提及的任何内容)已经对这个主题进行了广泛的讨论,所以请回头查看这些杂志!在让候选人产生一种虚假的安全感之后,我想出了这个小程序:以下代码片段的输出是什么,为什么?字符*指针;if((ptr=(char*)malloc(0))==NULL)puts("得到一个空指针");elseputs("得到一个有效的指针");这是个有趣的问题。我最近才想到这个问题,是因为我的一位同事无意中将0值传递给函数malloc并得到了一个有效的指针。这是上面的代码,输出“Gotavalidpointer”。我用它来开始讨论受访者是否认为图书馆例程在做正确的事情。得到正确的答案很重要,但解决问题的方法和做出决定的理由更为重要。十五。C语言中经常使用Typedef来声明现有数据类型的同义词。您也可以使用预处理器执行类似的操作。例如,考虑以下示例:#definedPSstructs*typedefstructs*tPS;这两种情况的目的都是将dPS和tPS定义为指向结构s的指针。哪种方法更好?(如果有的话)为什么?调查要点:这是一个非常微妙的问题,任何答对(有充分理由)的人都应该受到祝贺。答案是:typedef更好。考虑以下示例:dPSp1,p2;tPSp3,p4;第一个扩展为structs*p1,p2;上面的代码将p1定义为指向结构的指针,将p2定义为实际的结构,这可能不是您想要的。第二个例子正确定义了两个指针p3和p4。16.C语言支持一些令人震惊的结构,下面的结构是否合法,如果合法,它有什么作用?inta=5,b=7,c;c=a+++b;学习要点:这个问题将作为本次测验的愉快结束。信不信由你,上面的例子完全合乎语法。问题是编译器如何处理它?底层编译器作者其实会争论这个问题,根据最佳处理原则,编译器应该尽可能处理所有合法的用法。因此,上述代码处理为:c=a+++b;因此,执行此代码后,a=6,b=7,c=12。如果您知道答案,或者猜到了正确答案,那就太好了。如果你不知道答案,我也不认为这是一个问题。关于这个问题,我发现的最好的事情是它是一个关于编码风格、代码可读性和代码可修改性的好话题。