当时笔试做题的时候,面试官说半个小时以后来拿卷子,我趁机拍了一点,现在回过头再做一遍,也算是一个复习和提高,上图,第三大题没有拍全,等会后面补上。
第一题较简单,需要注意的只有float变量与零值的比较可能会比较坑,其他的都是常规,float变量与零值的if比较应该是:
const EPSINON=0.00001;
if((x>=-EPSINON)&&(x<=EPSINON)); //为什么是0.00001,可以具体查看float变量的精度和有效位数的介绍
第二题除了前三个会,其他都是蒙的,查了资料以后总结如下,发现自己蒙错了最后一题,但最后一题不应该错的。
sizeof是一个操作符(operator),其作用是返回一个对象或类型所占的内存字节数。
1.char str[]="Hello"; sizeof(str)=6;
数组的sizeof值等于数组所占用的内存字节数。
注意:1)当字符数组表示字符串时,其sizeof值将’/0’计算进去。
2)当数组为形参时,其sizeof值相当于指针的sizeof值。
2.char *p = str; sizeof(p)=4;
指针是用来记录另一个对象的地址,所以指针的内存大小当然就等于计算机内部地址总线的宽度。
在32位计算机中,一个指针变量的返回值必定是4。
指针变量的sizeof值与指针所指的对象没有任何关系。
3.int n = 10; sizeof(n)=4; //int类型占用的字节数依系统而定,题目说是Windows NT 32,那应该是4
4.void Func(char str[100]); sizeof(str)=4; //当数组为形参时,其sizeof值相当于指针的sizeof值。
5.void *p=malloc(100); sizeof(p)=4; //此时p为指针类型,在32位系统下占4个字节
第三题,列出如下:
1.说明#ifndef #define #endif的作用。
这个会c的人应该都懂,作用是避免重复包含头文件,因为只有在第一次没有包含该头文件时会执行#define和#endif之间的代码(有效代码,函数声明,类声明等),之后有文件想再次包含该头文件时,#ifndef为假,直接endif,不再次包含。
2.#include
用#include
用#include“filename.h”的表示预编译器首先在当前被编译文件所在目录中进行搜索filename.h,若找不到,再去C++系统目录中查找。就是查找的路径不同了。
3.说明const的作用。
说实话,考这题之前我还知道这个const是干嘛的,可这一问,我却都想不起来了。
const的作用:(1)可以避免由于无意间修改数据而导致编程出现错误;(2)可以节省空间,避免不必要的内存分配,原因是例如:
#define PI 3.14159 //常量宏const double Pi=3.14159; //此时并未将Pi放入RAM中 ......double i=Pi; //此时为Pi分配内存,以后不再分配!double I=PI; //编译期间进行宏替换,分配内存double j=Pi; //没有内存分配double J=PI; //再进行宏替换,又一次分配内存!const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干份拷贝。
(3)提高了效率。编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
4.当C++程序要用C语言方式编译的代码时,为什么要加extern C?
可参见https://www.cnblogs.com/daocaorenblog/p/5310492.html,具体我也没怎么回答上来,有空看看
5.说明下面两个for循环的优缺点:
for (int i = 0;i < n;i++)
{
if (condition)
dosomething();
else
dootherthing();
}
{
if (condition)
{
for (int i = 0;i < n;i++)
dosomething();
}
else
{
for (int i = 0;i < n;i++)
dootherthing();
}
}
前者:
优点:程序简洁
条件判断出现在For里面,意味着,即使我在dosomething()或dootherthing()这2个函数中改变了condition的值,For循环也能正确执行我的意图,因为它在每次循环中都会重新检测conditon的值并针对condition的值做不同动作,所谓以不变应万变,这是难能可贵的.
缺点:多执行了N-1次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。
如果condition一直未曾改变,我们可怜的if必须每次循环都判断一下condition的真假.牺牲了运行时效率.
后者:
优点:循环的效率高。只进行一次判断,运行时效率高.适合那种condition的值不会改变的情况.
缺点:由于只在一开始进行一次判断,所以失去的改变condition的值的机会,也就是说,即使我在dosomething()中改变了condition的值为false,这个程序也不会改变它的判断,它依然执行着dosomething()的循环.我们不能随时更换我们需要进行的动作。这是牺牲了弹性。
n较大时,建议采用后面这种写法,由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。
6.重写函数char *strcpy(char *dest, char *src)。
c库函数内的strcpy函数为:
char *strcpy(char *strDestination, const char *strSource);
{
assert((strDestination!=NULL) && (strSource !=NULL)); //断言,当其中任有一个为NULL时,程序终止,打印错误信息
char *address = strDestination;
while( (*strDestination++ = * strSource++) != '/0') NULL ;
return address ;
}
自己写的:
char *strcpy(char *dest, char *src)
{
if (dest == NULL || src == NULL)
return NULL;
char *newcopy = dest;
while ((*dest++ = *src++) != '\0'); //只要源字符串还没有到\0,就继续复制
return newcopy;
}
面试官问的问题是while中*和++运算符的优先级,不会,乱回答的。还有为什么返回的是在函数内定义的指针变量
(1)*和++的优先级和结合性:*作为 指针取值 运算符,级别同 ++(自增)一样。
优先级是用来标识运算符在表达式中的运算顺序的,在求解表达式的值的时候,总是先按运算符的优先次序由高到低进行操作。但是,当一个运算对象两侧的运算符的优先级别相同时,则按运算符的结合性来确定表达式的运算顺序。
结合性是指同一优先级的运算符在表达式中操作的组织方向,即:当一个运算对象两侧的运算符的优先级别相同时,运算对象与运算符的结合顺序。
最容易搞混的莫过于运算符优先级处于第二级别的了,尤其是当 * 和 ++ 用在一起时:
这里应该是优先级相同,按照从右到左的顺序结合,先进行P++,又因为是后加加,故p++执行后得到的还是p,然后取*p,*p++整句代码执行完后,执行p=p+1。从这些分析可以得出上面的while内的运算顺序。
(2)为什么返回函数内的指针变量:newcopy和dest指向的是同一内存。
返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。
链式表达式的形式如:
int iLength=strlen(strcpy(strA,strB));
又如:
char * strA=strcpy(new char[10],strB);