c常考题.docx
- 文档编号:29659144
- 上传时间:2023-07-26
- 格式:DOCX
- 页数:23
- 大小:26.59KB
c常考题.docx
《c常考题.docx》由会员分享,可在线阅读,更多相关《c常考题.docx(23页珍藏版)》请在冰豆网上搜索。
c常考题
关于c语言常考的笔试题,相信考试的前一天看完这个结果不会让你失望,我认为这是一篇对公司来说不地道的日志
最近答了7次笔试题,稍有些心得,企业再面试c语言不会像过去的那种读程序题写结果或者简单的函数编写,再或者考你一些简单的逻辑,c的精华是内存和指针的运用,围绕这些我想写一些东西供自己总结和给同事们、同学们、学弟们参考。
如下是常见到的笔试题,如果有笔试,前一天看一遍这些题结果不会让你失望的。
一、想说一说关于集中数据类型的sizeof问题,这题出现率40%
sizeof就是求在内存总占多少字节的问题,最基本的char1字节short2字节int4字节
任何的指针都是4字节 sizeof(数组名)=数组占的大小(这里必须注意虽然数组名可作为只想数组首地址的指针,但是sizeof(数组名)求得的不是指针的大小,而是数组大小)
还有就是sizeof(结构体) sizeof(联合体)
举两个例子 structstu{
Chara;
Intb;
Charc;
};
structteacher{
Chara;
Charc;
Intb;
};
Sizeof(stu)=12 ;内存中这样存储 char空空空 int char 空空空
内存4字节对其问题
Sizeof(teacher)=8 ;内存中这样存储 charchar空空 int
关于联合体
Unionstd{
Chara[10];
Intb[3];
}
Sizeof(std)=12; 联合体公用内存,取其中所占内存最大的元素为联合体所占内存
以下为WindowsNT下的32位C++程序,请计算sizeof的值
char str[]=“Hello”;
char *p=str;
int n=10;
1.请计算
sizeof(str)= 6
sizeof(p)= 4
sizeof(n)= 4
voidFunc(charstr[100])
{
请计算
sizeof(str)= 4
}
void*p=malloc(100);
请计算
sizeof(p)= 4
还有位段的问题如下
分析:
structbit
{ inta:
3;
int b:
2;
intc:
3;
};
intmain()
{
bits;
char*c=(char*)&s;
cout< *c=0x99; cout< inta=-1; printf("%x",a); return0; } 输出为什么是 4 1 -1 -4 ffffffff 因为0x99在内存中表示为10011001,a=001,b=11,c=100 当c为有符合数时,c=100,最高1为表示c为负数,负数在计算机用补码表示,所以c=-4;同理 b=-1; 当c为有符合数时,c=100,即c=4,同理b=3 位域: 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。 例如在存放一个开关量时,只有0和1两种状态,用一位二进位即可。 为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。 所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。 每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。 一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为: struct位域结构名 {位域列表}; 其中位域列表的形式为: 类型说明符位域名: 位域长度 例如: structbs { inta: 8; intb: 2; intc: 6; }; 位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。 例如: structbs { inta: 8; intb: 2; intc: 6; }data; 说明data为bs变量,共占两个字节。 其中位域a占8位,位域b占2位,位域c占6位。 对于位域的定义尚有以下几点说明: 1.一个位域必须存储在同一个字节中,不能跨两个字节。 如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。 也可以有意使某位域从下一单元开始。 例如: structbs { unsigneda: 4 unsigned: 0/*空域*/ unsignedb: 4/*从下一单元开始存放*/ unsignedc: 4 } 在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。 2.由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。 3.位域可以无位域名,这时它只用来作填充或调整位置。 无名的位域是不能使用的。 例如: structk { inta: 1 int: 2/*该2位不能使用*/ intb: 3 intc: 2 }; 从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。 位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名•位域名位域允许用各种格式输出。 main(){ structbs { unsigneda: 1; unsignedb: 3; unsignedc: 4; }bit,*pbit; bit.a=1; bit.b=7; bit.c=15; pri 二、关于动态申请内存的问题 这题出现率极高,60%不为过 voidGetMemory(char*p) { p=(char*)malloc(100); } voidTest(void) { char*str=NULL; GetMemory(str); strcpy(str,"helloworld"); printf(str); } 请问运行Test函数会有什么样的结果? 答: 试题传入GetMemory(char*p)函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完 char*str=NULL; GetMemory(str); 后的str仍然为NULL; char*GetMemory(void) { charp[]="helloworld"; returnp; } voidTest(void) { char*str=NULL; str=GetMemory(); printf(str); } 请问运行Test函数会有什么样的结果? 答: 可能是乱码。 charp[]="helloworld"; returnp; 的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。 这是许多程序员常犯的错误,其根源在于不理解变量的生存期。 voidGetMemory2(char**p,intnum) { *p=(char*)malloc(num); } voidTest(void) { char*str=NULL; GetMemory(&str,100); strcpy(str,"hello"); printf(str); } 请问运行Test函数会有什么样的结果? 答: (1)能够输出hello (2)Test函数中也未对malloc的内存进行释放。 (3)GetMemory避免了试题1的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句 *p=(char*)malloc(num); 后未判断内存是否申请成功,应加上: if(*p==NULL) { ...//进行申请内存失败处理 } voidTest(void) { char*str=(char*)malloc(100); strcpy(str,“hello”); free(str); if(str! =NULL) { strcpy(str,“world”); printf(str); } } 请问运行Test函数会有什么样的结果? 答: 执行 char*str=(char*)malloc(100); 后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上: str=NULL; 三、基本关键字的提问 常考的 Static extern volatile const register register是把一个变量声明为寄存器变量,和auto相对,auto是让编译器自动决定那些变量放在寄存器里,显然让编译器去决定更合理,所以这个关键字不常用,而声明变量时不写auto的话则自动会成为auto变量,inta;和autointa;效果是一样的,这两个只需要了解。 const是什么含意? 我只要一听到被面试者说: “const意味着常数”,我就知道我正在和一个业余者打交道。 去年DanSaks已经在他的文章里完全概括了const的所有用法,因此ESP(译者: EmbeddedSystemsProgramming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。 尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。 (如果你想知道更详细的答案,仔细读一下Saks的文章吧。 )如果应试者能正确回答这个问题,我将问他一个附加的问题: 下面的声明都是什么意思? constinta; intconsta; constint*a; int*consta; intconst*aconst; 前两个的作用是一样,a是一个常整型数。 第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。 第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。 最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。 如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。 顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢? 我也如下的几下理由: 1).关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。 如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。 (当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。 ) 2).通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 3).合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。 简而言之,这样可以减少bug的出现。 static的作用是什么? 这个简单的问题很少有人能回答完全。 在C语言中,关键字static有三个明显的作用: 1).在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 2).在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。 它是一个本地的全局变量。 3).在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。 那就是,这个函数被限制在声明它的模块的本地范围内使用。 大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。 这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。 volatile有什么含意并给出三个不同的例子。 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。 精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。 下面是volatile变量的几个例子: 1).并行设备的硬件寄存器(如: 状态寄存器) 2).一个中断服务子程序中会访问到的非自动变量(Non-automaticvariables) 3).多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。 我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。 嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。 不懂得volatile内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 1).一个参数既可以是const还可以是volatile吗? 解释为什么。 2).一个指针可以是volatile吗? 解释为什么。 3).下面的函数有什么错误: intsquare(volatileint*ptr) { return*ptr**ptr; } 下面是答案: 1).是的。 一个例子是只读的状态寄存器。 它是volatile因为它可能被意想不到地改变。 它是const因为程序不应该试图去修改它。 2).是的。 尽管这并不很常见。 一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 3).这段代码的有个恶作剧。 这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码: intsquare(volatileint*ptr) { inta,b; a=*ptr; b=*ptr; returna*b; } 由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。 结果,这段代码可能返不是你所期望的平方值! 正确的代码如下: longsquare(volatileint*ptr) { inta; a=*ptr; returna*a; } 关于extern 这样问 在C++程序中调用被C编译器编译后的函数,为什么要加extern“C”? 答: C++语言支持函数重载,C语言不支持函数重载。 函数被C++编译后在库中的名字与C语言的不同。 假设某个函数的原型为: voidfoo(intx,inty); 该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。 C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。 四、宏定义的问题 用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #defineSECONDS_PER_YEAR(60*60*24*365)UL 我在这想看到几件事情: 1).#define语法的基本知识(例如: 不能以分号结束,括号的使用,等等) 2).懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。 3).意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。 4).如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。 记住,第一印象很重要。 2.写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。 #defineMIN(A,B)((A)<=(B)? (A): (B)) 这个测试是为下面的目的而设的: 1).标识#define在宏中应用的基本知识。 这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。 2).三重条件操作符的知识。 这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。 3).懂得在宏中小心地把参数用括号括起来 4).我也用这个问题开始讨论宏的副作用,例如: 当你写下面的代码时会发生什么事? least=MIN(*p++,b); 预处理器标识#error的目的是什么? 如果你不知道答案,请看参考文献1。 这问题对区分一个正常的伙计和一个书呆子是很有用的。 只有书呆子才会读C语言课本的附录去找出象这种 问题的答案。 当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。 头文件中的ifndef/define/endif干什么用? 答: 防止该头文件被重复引用。 Typedef在C语言中频繁用以声明一个已经存在的数据类型的同义字。 也可以用预处理器做类似的事。 例如,思考一下下面的例子: #definedPSstructs* typedefstructs*tPS; 以上两种情况的意图都是要定义dPS和tPS作为一个指向结构s指针。 哪种方法更好呢? (如果有的话)为什么? 这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。 答案是: typedef更好。 思考下面的例子: dPSp1,p2; tPSp3,p4; 第一个扩展为 structs*p1,p2; 上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。 第二个例子正确地定义了p3和p4两个指针。 五、编程问题 我只想说strcpy的原型问题太常考了,太常考了,太常考了。 不会什么也得会这个,总共分四步,1、assert指针是否是空,2声明返回的指针并赋初值 3串拷贝的执行 4返回那个指针 已知strcpy函数的原型是 char*strcpy(char*strDest,constchar*strSrc); 其中strDest是目的字符串,strSrc是源字符串。 (1)不调用C++/C的字符串库函数,请编写函数strcpy. (2)strcpy能把strSrc的内容复制到strDest,为什么还要char*类型的返回值? (1)不调用C++/C的字符串库函数,请编写函数strcpy char*strcpy(char*strDest,constchar*strSrc); { assert((strDest! =NULL)&&(strSrc! =NULL)); char*address=strDest; while((*strDest++=*strSrc++)! =‘0’) NULL; returnaddress; } (2)strcpy能把strSrc的内容复制到strDest,为什么还要char*类型的返回值? 答: 为了实现链式表达式。 编写函数求传入参数的二进制里有多少个1 intfunc(x) { intcountx=0; while(x) { countx++; x=x&(x-1); } returncountx; } 六、位操作 嵌入式系统总是要用户对变量或寄存器进行位操作。 给定一个整型变量a,写两段代码,第一个设置a的bit3,第二个清除a的bit3。 在以上两个操作中,要保持其它位不变。 对这个问题有三种基本的反应 1).不知道如何下手。 该被面者从没做过任何嵌入式系统的工作。 2).用bitfields。 Bitfields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。 我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序,它用到了bitfields因此完全对我无用,因为我的编译器用其它的方式来实现bitfields的。 从道德讲: 永远不要让一个非嵌入式的家伙粘实际硬件的边。 3).用#defines和bitmasks操作。 这是一个有极高可移植性的方法,是应该被用到的方法。 最佳的解决方案如下: #defineBIT3(0x1<<3) staticinta; voidset_bit3(void) { a|=BIT3; } voi
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 考题
![提示](https://static.bdocx.com/images/bang_tan.gif)