程序员面试宝典:点滴记录二

1、用一个FIND宏定义求一个结构体struc里某个变量相对于struct的偏移量

#define FIND(struc,e) (size_t)&(((struc *)0)->e)

(size_t)是一种数据类型,为了便于不同系统之间移植,最好定义为一种无符号类型数据,一般为unsigned int。(P49)

 

2、const和#define相比有什么不同?

C++语言可以用const定义常量,也可以用#define定义常量,但是前者比后者更多优点:

(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意想不到的错误(边际效应)。

side effects可以理解成副作用,
当如下调用该宏时:max(a++,b++);宏被替换成 ((a++)>(b++)?(a++): (b++));所以肯定有个变量被计算2次。

(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。在C++中使用const常量而不使用宏常量,即const常量完全取代宏常量。

(3)C 编译器不能把const看成一个编译期间的常量。在C中,如果写:

#include <iostream> using namespace std; int main(void) { const a = 20; int b[a]; return 0; }

尽管看起来像是做了一件合理的事,但这将得到一个错误的结果。因为bufsize占用内存的某个地方,所以C编译器不知道它在编译时的值(P51)

 

3、在VC中,我们可以用 pack 预处理指令来禁止对齐调整。

pack:

http://www.sf.org.cn/Article/base/200509/260.html

请注意:除非你觉得必须这样,不要轻易做这样的调整,因为这将降低程序性能。目前比较常见的用法是:一,这个结构需要被指接写入文件;二,这个结构需呀哦通过网络传给其他程序。

#include <iostream> using namespace std; #pragma pack(8) struct s1{ short a; long b; }; struct s2{ char c; s1 d; long long e; }; #pragma pack() int main(void) { cout<<sizeof(s2)<<endl; return 0; }

答案为24。(P55)

 

4、数据对齐,是指数据所在的内存地址必须是该数据长度的整数倍。

struct

{

       long a1;

       short a2;

};

CPU的优化规则大致原则是:对于n字节的元素(n=2,4,8,...),它的首地址能被n整除,才能获得最好的性能。设计编译器的时候可以遵循这个原则:对于每一个变量,可以从当前位置向后找到第一个盲足这个条件的地址作为首地址。例子比较特殊,因为即便采用这个原则,得到的结果也应该为6字节(long的首地址偏移量为0000,short首地址偏移量0004,都符合要求)。但是结构体一般会面临数组分配的问题。编译器为了优化这种情况,干脆把它的大小设为8字节,这样就没有麻烦了,否则得话,会出现单个结构体的大小为6字节,而大小为n的结构体数组大小却为8*(n-1)+6的尴尬局面。(P53)

 

5、静态变量是存放在全局数据区的,sizeof计算栈中分配的大小。(P57)

 

6、sizeof 和 strlen的深入理解

(1)sizeof 操作符的结果类型是size_t,它在头文件中的 typedef 为 unsigned int 类型,该类型保证能容纳实现所建立的最大对象的字节大小。

(2)sizeof 是算符,strlen是函数。

(3)sizeof 可以用类型作参数,strlen 只能用char*作参数,且必须是以"/0"结尾的。sizeof 还可以用函数作参数:

#include <iostream> using namespace std; short f(void); int main(void) { cout<<sizeof(f())<<endl; return 0; }

输出的结果是sizeof(short),即2。

(4)数组作 sizeof 的参数不退化,传递给strlen就退化为指针。

数组名退化(即数组名退化为指针):

http://hi.baidu.com/%CC%EC%B5%D8%D3%D0%C7%E9%CC%EC%D2%E0%C0%CF/blog/item/bd228d3c0994920bbba1675e.html

(5)大部分编译程序在编译的时候就把 sizeof 计算过了,是类型或是变量的长度。这就是 sizeof(x) 可以用来定义数组维数的原因。

(6) strlen 的结果要在运行的时候才能计算出来,用来计算字符串的长度,而不是类型占内存的大小。

(7) sizeof 后如果是类型必须加括号,如果是变量名可以不加括号。这是因为 sizeof 是个操作符而不是个函数

#include <iostream> using namespace std; int main(void) { int a; cout<<sizeof a<<endl; return 0; }

(8)当使用了一个结构类型或变量时,sizeof返回实际的大小。当使用一静态的空间数组时,sizeof 返回全部数组的尺寸。sizeof 操作符不能返回被动态分配的数组或外部的数组的尺寸。

(9)数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址。在C++里传递数组永远都是传递指向数组首元素的指针,便一起不知道数组的大小。如果想在函数内知道数组的大小,需要这样做:进入函数后用 memcpy 将数组复制出来,长度由另一个形参传进去。

memcpy: void * memcpy ( void *destination,const void * source, size_t num);

num: Number of bytes to copy
http://www.cplusplus.com/reference/clibrary/cstring/memcpy/
(10) sizeof 操作符不能用于函数类型、不完全类型或位字段。不完全类型指具有未知存储大小数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等等。(P58)
(11) sizeof() 括号中的内同在编译过程中是不被编译的,而是被替代类型,如 int a=8; sizeof(a); 在编译过程中,不管a的值是什么,只是被替换成类型 sizeof(int) 。如果 sizeof(a=6),也一样换成a的类型,但是要注意,a=6 是不被编译的,执行完 sizeof(a=6) 后,a的值仍然还是8。
(12) unsigned 影响的只是最高位 bit 的意义(正/负),数据长度是不会被改变的,sizeof(unsigned int) == sizeof(int)。(P60)
7、 #include <iostream> #include <string> using namespace std; int main(void) { string str = "abcde"; string strArr1[] = {"Trend","Micro","Soft"}; string *pStrArr1 = new string[2]; pStrArr1[0] = "US"; pStrArr1[1] = "CN"; for(int i=0;i<sizeof(strArr1)/sizeof(string);i++) cout<<strArr1[i]; for(int j=0;j<sizeof(*pStrArr1)*2/sizeof(string);j++) cout<<pStrArr1[j]; return 0; }   
string是类,sizeof(string)=4,sizeof(*pStrArr1)*2是string数组的大小。(P60)
8、 #include <iostream> #include <complex> using namespace std; class Base { public: Base() { cout<<"Base-ctor"<<endl; } ~Base() { cout<<"Base-dtor"<<endl; } virtual void f(double) { cout<<"Base::f(double)"<<endl; } virtual void g(int i=10) { cout<<"Base::g()"<<i<<endl; } void g2(int i=10) { cout<<"Base::g2()"<<i<<endl; } }; class Derived:public Base { public: Derived() { cout<<"Derived-ctor"<<endl; } ~Derived() { cout<<"Derived-dtor"<<endl; } void f(complex<double>) { cout<<"Derived::f(complex)"<<endl; } virtual void g(int i=20) { cout<<"Derived::g()"<<i<<endl; } }; int main(void) { Base b; Derived d; Base * pb = new Derived; cout<<sizeof(Base)<<"tt"<<endl; //Base大小为4 cout<<sizeof(Derived)<<"bb"<<endl;//Derived大小为4 return 0; }
Base 类和 Derived 有一个隐含的 this 指针。(P61)
9、再注意一下!数组名在作为函数参数是,将退化为指针。(P61)

 

10、#include <iostream> #include <memory.h> #include <assert.h> using namespace std; class A { }; class A2 { }; class B : public A { }; class C : public virtual B { }; class D : public A,public A2 { }; int main() { cout<<"sizeof(A):"<<sizeof(A)<<endl; cout<<"sizeof(B):"<<sizeof(B)<<endl; cout<<"siziof(C):"<<sizeof(C)<<endl; cout<<"sizeof(D):"<<sizeof(D)<<endl; return 0; }

空类所占的空间为1,单一继承的空类空间也为1,多重继承的空类空间还是1。但是虚继承涉及到虚表(虚指针),所以sizeof(C)的大小为4。

11、内联函数和宏定义
内联函数要做参数类型检查,这是内联函数跟宏相比的优势。
inline一般只用于如下情况:
(1)一个函数不断被重复调用;
(2)函数只有简单几行,且函数不包含 for、while、switch。
宏是在代码处不加任何验证简单替代,而内联函数将代码直接插入调用处,而减少了普通函数调用是的资源消耗。
宏不是函数,只是在编译前(编译预处理阶段)将程序中有关字符串替换成宏体。
inline 函数是函数,但在编译中不单独产生代码,而是将有关代码嵌入到调用处。
inline函数:
http://www.fenglog.com/blog/article.asp?id=127   (P64)

你可能感兴趣的:(c,面试,String,struct,Class,编译器)