您的位置:首页 > 职场人生

程序员面试宝典-1

2015-09-14 16:44 513 查看
第2部分 C/C++程序设计

1. 二进制位变换

对于整型x:

x&(x-1)的结果是x最右边的1被置为0

这儿有详细介绍:/content/3670973.html

2. 类型转换

这儿提到的类型转换主要是指,浮点型和整型之间的转换。

例如:

float x=2.5f;

则printf("%#x\n",*(int*)&x);//0x40200000

printf("%#x\n",(int&)x);//0x40200000(这个需要在C++下编译)

(int&)x效果等价于*(int*)&x,这是C++中可以编译通过(VC,g++都可以),在C中是不可以的。

下面一段程序:

#include <stdio.h>

int main(){

unsigned int a=0x77777777;

unsigned char i=(unsigned char)a;

char *b=(char*)&a;

char c=*(char*)&a;

printf("%#08x,%#08x,%#08x",i,*b,c);

return 0;

}

若a=0x77777777;

输出为:0x000077,0x000077,0x000077

若a=0x7ffffff7;

输出为:0x0000f7,0xfffffff7,0xfffffff7

若a=0x7fffff77;

输出为:0x000077,0x000077,0x000077

若a=0xffffff77;

输出为:0x000077,0x000077,0x000077

若a=0xfffffff7

输出为:0x0000f7,0xfffffff7,0xfffffff7

所以可以得出结论:i和*b和c其实都是一个字节,只是如果值为负的时候要在前面补1

3.运算符符问题

看下面代码的输出:

#include <cstdio>

using namespace std;

int main(){

unsigned char a=0xA5;

unsigned char b=~a>>4+1;

printf("b=%d\n",b);

return 0;

}

这儿需要注意的是~的优先级高于+,+的优先级高于>>,所以先取反,然后求和最后移位。

正常情况下,对10100101取反得01011010,然后右移5位得到00000010,为2,但是运行后结果是0xfa(250)

解释如下:



4. x&(x-1)又一个应用实例



在分析该表达式的实现思路之前,首先说明该表达式的作用就是求两数的平均值。也就是说,像上面当 x为 729,y为271时函数的返回值是500 。下面说明该表达式的思路。

我们先了解下面几种情况:

1. 当两个数所有为 1 和为 0 的位都相同时,这两个数的平均值就是 (x & y) 。比如当 x 和 y 都等于 1100 时,x & y 的值也是 1100 ,我们也可以说此时求 x 和 y 的平均数可以用 (x & y) 来求得。

2. 当两个数中的所有对应位有且只有一个为 1 时,那么这两个数的平均值由 (x^y)>>1 表达式求得。比如当 x 为 101100 (十进制 44),y 为 010010 (十进制 18) 时,x ^ y 的值为 111110 ,然后再将 111110 向右移动 1 位后得 11111 (十进制为 31),也就是 (44 + 18)/2 = 31 。

由上面的 情况1 和 情况2 我们知道,将它们两者结合起来便可求得随意两个整数的平均值。下面以 x 等于 12, y 等于 24 为例说明:

x 的二进制数位 1100 ,y 的二进制数为 11000 。我们先将 x 和 y 做相与运算:

01100

11000 &

--------------

01000

实际上,像上面的运算,我们也可以直接看成是 情况1 中的运算,即相当于执行:01000 & 01000 ,最后值仍然是 01000 。

好,经过上面的与运算后,看起来是将 01100 和 11000 分别去掉了 01000 这部分,所以剩下来的就是 00100 和 10000 。当我们执行 x^y 时:

01100

11000 ^

-----------

10100

由上可见,异或的运算正好也是去掉了 01000 ,然后将剩下来的 00100 和 10000 这两部分进行相加,所以求这两部分的平均数只要向右移动 1 位即可。

综 上可得,我们求平均数的过程是先用与运算对数值做部分的提取,然后用异或并右移运算获得余下部分的平均值,因此这两部分的平均值相加后就得出了原来两数的 平均值。实际上,这是一个加法分解然后综合的过程。如上面的 12 和 24,先做与运算,也就相当于从 12 和 24 里分别先减去 4,剩下 8 和 16,再将这两数相加得 24 (异或运算),然后再除以2(右移),结果为 12 。最后 12 + 4 等于 16 即得最后所要的结果。

尽管上面的过程看来上去实际用处不是很大,但如果是用在没有乘除法指令的简单单片机系统,移位和逻辑运算操作就显得很重要了。

5. 比较两个数

不使用判断语句得到两个数中较大的一个:

方案一:

int max=(a+b+abs(a-b))/2;

方案二:

int flag=(((a - b) >> (8 * sizeof(int) - 1))) & 0x01; //获得两者差的符号位

int buf[2]={a,b};

int max=buf[flag];//如果flag=0,说明a>b,否则a<b

或者:

int max=(flag * b) + ((1 - flag) * a);

参考:http://blog.csdn.net/mougaidong/article/details/6904099

测试代码如下:

int test1(){

int x,y;

int z;

int i;

double m;

srand((int)time(NULL));

for(i=0;i<10;i++)

{

x=rand()%100;

y=rand()%100;

m=(x+y)/2.0;

z=(x&y)+((x^y)>>1);

printf("%4d%4d%5.1f%4d%4d%4d\n",x,y,m,x&y,x^y,z);

}

}

int test2(){

int i;

srand((int)time(NULL));

for(i=0;i<10;i++){

int x=rand()%100;

int y=rand()%100;

int buf[2]={x,y};

unsigned int z;

z=x-y;

z>>=31;

printf("%3d%3d%3d\n",x,y,buf[z]);

}

return 0;

}

输出如下(左边是test1函数,右边是test2函数):

6. 删除C/C++程序中的注释

7. const用法,在C与C++中的不同



在const成员函数中,使用mutable修饰成员变量后,就可以修改类成员变量了。

8. 内存对齐

不同编译器的内存对齐情况



下面的代码:

int main(){

int a;

char b;

int c;

char *p=(char*)&a;

printf("%#08x\n",&a);

printf("%#08x\n",&b);

printf("%#08x\n",&c);

printf("%#08x\n",p);

printf("%#08x\n",p+1);

printf("%#08x\n",p+2);

printf("%#08x\n",p+3);

return 0;

}

gcc编译运行如下:







最右边的图表示的是内存中的排列方式

这儿要注意内存的增长方向:高->低

/article/4753485.html

数组在内存中的存储:

下面是一段测试代码:

int test3(){

int i=-1,a[]={1,2,3,4,5};

char *p=NULL;

printf("%#08x\n",&i);

printf("%#08x\n",a);

printf("%#08x\n",&p);

for(i=0;i<5;i++){

printf("%d: ",i);

p=(char*)&a[i];

printf("%#08x ",p);

printf("%#08x ",p+1);

printf("%#08x ",p+2);

printf("%#08x\n",p+3);

}

}





这儿有一个关于数组内存布局的讨论:http://www.cnblogs.com/xkfz007/admin/EditPosts.aspx?postid=2607224&update=1

9. C++中类也遵循内存对齐

注意:类中的方法不占用空间。类中的静态变量是分配在全局数据区的,而sizeof计算的是栈中分配的大小。

参 数为结构或类。Sizeof应用在类和结构的处理情况是相同的。但有两点需要注意,第一、结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态 变量的存储位置与结构或者类的实例地址无关。第二、没有成员变量的结构或类的大小为1,因为必须保证结构或类的每一个实例在内存中都有唯一的地址。

10. sizeof空类,多重继承

一个空类所占的空间为1,多重继承的空类所占空间还是1,但是虚继承设计虚表(虚指针),所以至少有一个指针的大小

11. 指针减法运算

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char *argv[])

{

int arr[5] = {1,2,3,4,5};

int *ptr = arr;

printf("%d\n",ptr[4]-ptr[0]);

return 0;

}

运行输出:4

更换为字符数组,测试结果一样。

《C 和指针》P110 分析如下:两个指针相减的结果的类型为ptrdiff_t,它是一种有符号整数类型。减法运算的值为两个指针在内存中的距离(以数组元素的长度为单位,而 非字节),因为减法运算的结果将除以数组元素类型的长度。所以该结果与数组中存储的元素的类型无关。

size_t是unsigned类型,用于指明数组长度或下标,它必须是一个正数,std::size_t

ptrdiff_t是signed类型,用于存放同一数组中两个指针之间的差距,它可以使负数,std::ptrdiff_t.

size_type是unsigned类型,表示容器中元素长度或者下标,vector<int>::size_type i = 0;

difference_type是signed类型,表示迭代器差距,vector<int>:: difference_type = iter1-iter2.

前二者位于标准类库std内,后二者专为STL对象所拥有。

12. 获取成员变量偏移量的两种方式

第一种方式是MFC里使用广泛的宏:#define OFFSET(structure, member) ((int)&((structure*)0)->member); 正如我们平时通过某对象的地址指针访问某个成员变量一样,这里只是强制使用0作为该地址,但区别是并没有通过该地址去访问成员变量,而只是用&操 作符来获取该成员变量的地址,所以不会出现访问违规的情况。所以,完全可以用此类声明一个对象,然后用该对象某成员变量地址减去该对象首地址获取偏移量,
只是纯虚类无法这样实现。

另一种方式是通过域操作符取成员变量的地址。例如一个类Test有int 型成员变量x,则可以通过int Test::* pOffset = &Test::x 获得该偏移量,然后通过int nOffset = reinterpret_cast<int>(*(void**)(&pOffset))将其转化为整型量。

另外,以上这两种方式都对静态成员无效。

下面给出了获取成员变量地址的方式:

class A{

public:

A():m_a(1),m_b(2){

cout<<"A()"<<endl;

}

~A(){

cout<<"~A()"<<endl;

}

void fun(){

cout<<"A:fun "<<m_a<<" "<<m_b<<" "<<endl;

}

public:

int m_a;

int m_b;

static int s_a;

};

int A::s_a=3;

class B{

public:

B():m_c(3){

cout<<"B()"<<endl;

}

~B(){

cout<<"~B()"<<endl;

}

void fun(){

cout<<"B:fun "<<m_c<<endl;

}

public:

int m_c;

};

void test1(){

A a;

B *p=(B*)(&a);

cout<<"&a:"<<&a<<endl;

cout<<"&(a.m_a):"<<&(a.m_a)<<endl;

cout<<"&(a::s_a)"<<&a.s_a<<endl;

cout<<"&(A::s_a)"<<&A::s_a<<endl;

int A::* q=&A::m_a;

cout<<"&A::m_a:"<<reinterpret_cast<int>(*(void**)(&q))<<endl;

q=&A::m_b;

cout<<"&A::m_b:"<<reinterpret_cast<int>(*(void**)(&q))<<endl;

int B::* t=&B::m_c;

cout<<"&B::m_c:"<<reinterpret_cast<int>(*(void**)(&t))<<endl;

p->fun();

}

输出如下:



13. 指针与句柄



第8章 递归问题

14. 输入两个字符串,比如abdbcc和abc,输出第二个字符串在第一个字符串中的连接次序,即输出125,146,145,146.



递归代码如下:


View
Code

输出:



非递归代码如下:

15. 给出如下递归表达式的非递归计算方法:



递归和非递归的代码如下:


View
Code

16. zig-zag扫描问题



实现代码如下:


View
Code

17. 两个数组匹配的问题:

A,B两个数组是等长度,但顺序不同,对两个数组中的数进行匹配。



常规解法的代码如下:


View
Code

输出如下:



一种更好的方法:

18. 螺旋队列问题





具体程序如下:


View
Code

程序输出如下:



扩展问题



代码如下:


View
Code

输出:



19. 概率问题



测试代码如下:


View
Code

输出如下:



20. 虚函数,覆盖问题

这儿牵扯到一个虚函数的使用问题,这儿没有使用基类的指针或基类引用,但是却利用虚函数实现了多态机制。

代码如下:


View
Code

该代码的输出如下:AA AA BB BB

21. 虚函数继承与虚继承



22. 多重继承 的优点与缺陷







23. C++所引入的额外开销







内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: