程序员面试宝典——总结

1 x=x+1,x+=1,x++,哪个效率最高?为什么?

解析:

x=x+1最低,因为它的执行过程如下:

1)读取右x的地址。

2)x+1

3)读取左x的地址

4)将右值传给左边的x(编译器并不左右x的地址相同)。

x+=1其次,其执行过程如下:

1)读取右x的地址

2)x+1

3)将得到的值传给x(因为x的地址已经读出)。

x++,效率最高,其执行过程如下:

1)读取右x的地址

2)x自增1.

2 i++和++i的区别

i=3;

则j=i++*i++; 则j等于9,此时i累积为5

而k=++i*++i; 则k等于49,先累加i为7

3 在条件表达式中'A'==a与a==’A‘哪一种写法好。

第一种写法'A'==a比较好一些。这时如果把"=="误写做"="的话,因为编译器不允许对常量赋值,就可以检查到错误。

4 当表达式中存在有符号类型和无符号类型时。所有的操作数都自动转换为无符号类型。

5 使用extern "C"的理由

函数被C编译器编译后不带参数信息,被C++编译器编译后会带上参数信息。这是因为C++支持函数重载。所以函数被C编译器编译和被C++编译器编译是不同的。例如:void Zero(int lin),被C编译后,可能得到_Zero,被C++编译后,可能得到_Zero_int。那如果要在C++中使用被C编译过的库呢?使用extern "C"就可以!它会告诉C++编译器,被它修饰的函数是以C语言的编译方式编译的。
 
6 如何判断一段程序是由C编译器还是由C++编译程序编译的?
C++编译时定义了_cplusplus
C编译时定义了_STDC_.
 
7 如果想在main主函数执行完毕后,还能再执行一段代码,可以使用atexit函数注册一个函数。
 
8 使用#define需要注意。
宏定义中的参数最好加括号,因为红替换就是简单的文本替换,加括号是为了处理表达式参数(参数可能是算法表达式(,使之更加通用。
 
9 const 关键字的作用?
在C语言中,const关键字的作用有如下:
1)可以定义const常量。
2)const可以修饰函数的参数和返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
在C++中,const还可以修饰成员函数的定义体,定义类中的某个成员函数为const成员函数,即不改变类中的数据成员。
 
10 const与#define相比有什么不同?
 
C++语言可以用const定义常量,也可以用#define定义常量,但是前者比后者有更多的优点:
  • define宏是在预定义阶段,const常量在编译运行阶段使用
  • const定义的只读变量是有数据类型的,编译器可以对其进行数据类型检查,而#define宏定义的常量只是进行简单的字符替换,没有类型检查,且有时还会产生意料不到的错误(边际效应)。
  • 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
  • 当定义局部变量时,const作用域仅限于定义局部变量的函数体内。但用#define时其作用域不仅限于定义局部变量的函数体内,而是从定义点到整个程序的结束点。但也可以用#undef取消其定义从而限定其作用域范围。

另外,

C++中支持#define是因为要兼容C,C++中不提倡用#define来定义常量。

#define也可以定义带参数的宏,C++中使用inline函数来代替。

扩展知识

C中的const的意思是“一个不能被改变的普遍变量”。在C中,它总是占用内存,而且它的名字是全局符。C编译器不能把const看成一个编译期间的常量。在C中,如果写:

const bufsize=100;

char buf[bufsize];

尽管看起来好像做了一件合理的事,但这将得到一个错误的结果。因为bufsize占用内存的某个地方,所以C编译器不知道它在编译时的值。在C语言中可以选择这样书写:

const bufsize;

这样写在C++中是不对的,而C编译器则把它作为一个声明,这个声明指明在别的地方有内存分配。因为C默认const是外部连接的,C++默认const是内部连接的,这样,如果在C++中想完成与C中同样的事情,必须用extern把内部连接改成外部连接:

extern const bufsize;  //declaration only

这种方法也可以在C语言中。在C语言中使用限定符const不是很有用,即使是在常数表达式里(必须在编译期间被求出)想使用一个已命名的值,使用const也不是很有用的。C迫使程序员在预处理器里使用#define。

11 内联函数和宏定义

 宏和内联函数的区别:

  • 宏是简单的字符串替换,没有参数类型检查,内联函数要做参数类型检查
  • 宏在编译之前进行(编译预处理阶段),即先用宏替换宏名,然后再编译。函数是编译之后,在执行调用的。所以宏占用的是编译的时间,函数占用的是执行的时间。
  • 宏的参数不占内存空间,因为只是简单的字符串替换。函数的形参会占内存空间
  • 函数要有调用的开销,宏没有。

指针和引用

1 指针和引用的差别

1)非空区别。在任何情况下都不能使用指向空值的引用。一个引用必须总是指向某些对象。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高。

2)合法性区别。在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。

3)可修改区别。指针和引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。

4)应用区别。总的来说,在以下情况下你应该使用指针:一是你考虑到存在不指向任何对象的可能,二是你需要能够在不同的时刻指向不同的对象。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。

char c[] = “hello”; char *c = “hello”;的区别:

Notes 前者是分配一个局部数组,位于内存的栈中;后者是分配一个全局数组,位于内存的全局数据区。全局数据区的值不可更改,局部区的数据可改变:

         char c[] = "hello";cout<<c<<endl; //hello

         c[0] = 'H';cout<<c<<endl; //Hello

 

         char *c1 = "leo";cout<<c1<<endl;//leo

         c1 = "jia";cout<<c1<<endl;//jia,指针值改变

         //*c1 = 'J'; //false,内存不可写

         //c1[0] = 'J';//false,内存不可写

 

指针和句柄

1)指针和句柄的区别联系

指针标记物理内存地址,而句柄是指向指针的指针,windows用句柄来标记系统资源,隐藏系统信息。它是windows在内存中维护的一个对象的内存物理地址列表的整数索引,是一个uint。对象的物理地址是变化的,程序将想访问的对象的句柄传递给系统,系统根据句柄检索自己维护的对象列表来访问对象。

Windows是一个以虚拟内存为基础的操作系统,其内存管理器经常在内存中移动对象来满足不同程序的内存需要,为了能继续访问被移动后的对象,系统把对象新的地址用句柄保存起来,用句柄可以间接知道对象的具体物理地址。句柄地址(稳定)->对象在内存中的地址(不稳定)->实际对象。

原文地址:https://www.cnblogs.com/wuchanming/p/4308475.html