1、指向数组的指针
int (*ptr)[5]=(int (*)[5])100;
int k=(int)(ptr+1);
printf("%d\n",(int)(&(*(ptr+1))[2])); //100+5*4+2*4=128
2、三维数组赋值与取值
int a[3][4][5];
int *p=(int *)a;
for (int i=0;i<60;i++)
p[i]=i;
int (*s)[5]=&a[1][0];
int m=a[1][7][1]; //56
int n=*(s+1)[3]; //44
printf("%d\n",a[1][7][1]*(*(s+1)[3]));
3、static和const同时修饰成员函数时,const默认是不起作用的。
下面代码中,在static和const同时修饰的函数里面,只能访问static变量,并且可以改变该变量的值,说明这种函数是没有this指针的,并且const关键字也是无效的。
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream.h>
- #include <string.h>
- class Test
- {
- public:
- const static int func()
- {
- i+=100;
- return i;
- }
- static int i;
- };
- int Test::i=0;
- int main()
- {
- Test t;
- for (int i=0;i<10;i++)
- {
- cout<<t.func()<<endl;
- }
- return 0;
- }
4、运行下面的代码,会输出什么?
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream.h>
- #include <string.h>
- class A
- {
- private:
- int m_value;
- public:
- A(int value)
- {
- m_value = value;
- }
- void Print1()
- {
- printf("hello world");
- }
- void Print2()
- {
- printf("%d", m_value);
- }
- };
- int main(int argc, char* argv[])
- {
- A* pA = NULL; //this指针为NULL,当访问类的非static公有变量时,程序出错。
- pA->Print1();
- pA->Print2();
- return 0;
- }
Print1函数调用正常,输出hello world。因为调用 Print1 时,并不需要 pA 的地址,因为Print1 的函数地址是固定的。编译器会给 Print1 传入一个 this 指针,该指针为NULL,但在 Print1 中该 this 指针并没有用到。
但是调用Print2函数时由于需要访问this指针以获得非static成员变量m_value的值,而由于pA等于NULL,所有this指针等于NULL,所以访问m_value时出错,导致程序崩溃。
5、假如将上题中的Print2函数定义为vitual类型,结果会是怎样呢?
Print1 调用正常,打印出 hello world。Print1 的调用情况和上面的题目一样,不在赘述。
但运行至 Print2 时,程序崩溃。由于 Print2 是虚函数。C++调用虚函数的时候,要根据实例(即this 指针指向的实例)中虚函数表指针得到虚函数表,再从虚函数表中找到函数的地址。由于这一步需要访问实例的地址(即 this 指针),而此时 this 指针为空指针,因此导致内存访问出错。
6、静态成员函数能不能同时也是虚函数?
不能。调用静态成员函数不要实例。但调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数需要一个实例。两者相互矛盾。
7、获取结构体中变量的相对偏移地址
&(pA->m_value)的语意是求pA中变量m_value的地址(pA的地址为0加m_value的偏移量8),并不需要访问 pPoint 指向的内存。只要不访问非法的内存,程序就不会出错。
看下面一个列子:
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream.h>
- #include <string.h>
- struct A
- {
- int m_value;
- int m_value1;
- int m_value2;
- int m_value3;
- };
- int main(int argc, char* argv[])
- {
- A* pA = NULL;
- cout<<(int)(&(pA->m_value))<<endl; //0
- cout<<(int)(&(pA->m_value1))<<endl; //4
- cout<<(int)(&(pA->m_value2))<<endl; //8
- cout<<(int)(&(pA->m_value3))<<endl; //12
- //cout<<pA->m_value<<endl; //访问非法内存
- return 0;
- }
8、下面代码输出什么?
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream.h>
- #include <string.h>
- class A
- {
- public:
- A()
- {
- Print(); }
- virtual void Print()
- {
- printf("A is constructed.\n");
- }
- };
- class B: public A
- {
- public:
- B()
- {
- Print();
- }
- virtual void Print()
- {
- printf("B is constructed.\n");
- }
- };
- int main()
- {
- A* pA = new B();
- delete pA;
- return 0;
- }
先后打印出两行:A is constructed. B is constructed。
调用 B 的构造函数时,先会调用 B 的基类及A 的构造函数。然后在A 的构造函数里调用 Print。由于此时实例的类型 B 的部分还没有构造好,本质上它只是 A 的一个实例,他的虚函数表指针指向的是类型 A 的虚函数表。因此此时调用的Print 是 A::Print,而不是 B::Print。接着调用类型 B 的构造函数,并调用 Print。此时已经开始构造 B,因此此时调用的 Print是 B::Print。
9、成员变量的初始化顺序
在 C++中,成员变量的初始化顺序与变量在类型中的申明顺序相同,而与它们在构造函数的初始化列表中的顺序无关。
10、C++类中复制构造函数的形参只能是引用
假如复制构造函数中传入的参数是传值,把形参拷贝到实参会调用复制构造函数。因此如果允许复制构造函数传值,那么会形成永无休止的递归并造成栈溢出。因此 C++的标准不允许复制构造函数传值参数,而必须是传引用或者常量引用。
11、将某一位清零
int a=31;
int n=4;
a&=~(1<<n);
cout<<a<<endl; //15
12、整数按照字符串格式输出,会得到什么结果呢?
int i=0;
printf("i=%s",i); //i=<null>
int i=10;
printf("i=%s",i); //程序崩溃
13、运算符优先级(~、+、<<)
int i=1+2<<3; //i= (1+2)<<3;
printf("i=%d",i); //24
14、下面输出
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream.h>
- #include <string.h>
- typedef struct Test
- {
- char a;
- short b;
- }test;
- int main()
- {
- test a={0,0};
- test b={0};
- test c;
- memset(&c,0x00,sizeof(a));
- cout<<a.a<<" "<<a.b<<endl;
- cout<<a.a<<" "<<b.b<<endl;
- cout<<c.a<<" "<<c.b<<endl;
- return 0;
- }
15、在64位系统下,int类型与指针占几个字节
int类型依然是4个字节;
指针类型占8个字节。
16、关于TTL(生存时间)
指定数据包被路由器丢弃之前允许通过的网段(路由器、服务器)数量,最大值255。
当数据包传送到一个路由器之后,TTL就自动减1,如果减到0了还是没有传送到目的主机,那么就自动丢失。
17、内存对齐宏
#pragma pack(push,n)
18、hash算法
哈希算法将任意长度的二进制值映射为固定长度的较小二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值。要找到散列为同一个值的两个不同的输入,在计算上是不可能的,所以数据的哈希值可以检验数据的完整性。
越是简单的程序设计,越要多一个心眼,从时间复杂度、空间复杂度去优化程序设计,同时保证程序良好的阅读和可扩展性。
19、请使用虚析构函数
下面的程序由于未使用虚析构函数,因此在析构对象时,仅析构了对象的基类部分,而没有析构派生类部分。
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream>
- #include <string>
- class A
- {
- public:
- A()
- {
- std::cout << "A is created." << std::endl;
- }
- ~A()
- {
- std::cout << "A is deleted." << std::endl;
- }
- };
- class B : public A {
- public:
- B()
- {
- std::cout << "B is created." << std::endl;
- }
- ~B()
- {
- std::cout << "B is deleted." << std::endl;
- }
- };
- int main(int argc, char* argv[])
- {
- A* pA = new B();
- delete pA;
- return 0;
- }
20、求两个整数的和,不得使用+、-、*、/
先看一个例子,5 的二进制是 101,17 的二进制 10001。试着把计算分成三步:
第一步各位相加但不计进位,得到的结果是 10100(最后一位两个数都是 1,相加的结果是二进制的 10。这一步不计进位,因此结果仍然是 0);
第二步记下进位。在这个例子中只在最后一位相加时产生一个进位,结果是二进制的 10;
第三步把前两步的结果相加,得到的结果是 10110,正好是 22。由此可见三步走的策略对二进制也是管用的。
接下来我们试着把二进制上的加法用位运算来替代。
第一步不考虑进位,对每一位相加。0 加 0与 1 加 1 的结果都 0,0 加 1 与 1 加 0 的结果都是 1。我们可以注意到,这和异或的结果是一样的。对异或而言,0 和 0、1 和 1 异或的结果是 0,而 0 和 1、1 和 0 的异或结果是 1。
接着考虑第二步进位,对 0加 0、0 加 1、1 加 0 而言,都不会产生进位,只有 1 加 1 时,会向前产生一个进位。此时我们可以想象成是两个数先做位与运算,然后再向左移动一位。只有两个数都是 1 的时候,位与得到的结果是 1,其余都是 0。
第三步把前两个步骤的结果相加。如果我们定义一个函数 AddWithoutArithmetic,第三步就相当于输入前两步骤的结果来递归调用自己。
根据上面,不难写出下面的代码:
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream>
- #include <string>
- using namespace std;
- int AddWithoutArithmetic(int num1, int num2)
- {
- if(num2 == 0)
- return num1;
- int sum = num1 ^ num2; //相加
- int carry = (num1 & num2) << 1; //进位
- return AddWithoutArithmetic(sum, carry); //递归运算
- }
- int main(int argc, char* argv[])
- {
- int x=10,y=20;
- cout<<AddWithoutArithmetic(x,y)<<endl;
- return 0;
- }
21、某公司有几万名员工,请完成一个时间复杂度为 O(n)的算法对该公司员工的年龄作排序,可使用O(1)的辅助空间。
思路: 题目特别强调是对一个公司的员工的年龄作排序。员工的数目虽然有几万人,但这几万员工的
年龄却只有几十种可能。上班早的人一般也要等到将近二十岁才上班,一般人再晚到了六七十岁也不得不 由于年龄总共只有几十种可能,我们可以很方便地统计出每一个年龄里有多少名员工。举个简单的例子,假设总共有 5 个员工,他们的年龄分别是 25、24、26、24、25。我们统计出他们的年龄,24岁的有两个,25 岁的也有两个,26 岁的一个。那么我们根据年龄排序的结果就是:24、24、25、25、26,即在表示年龄的数组里写出两个 24、两个 25 和一个 26。其实这就是技术排序的原理
故将计数排序稍加改进,得到下面程序:
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream>
- #include <string>
- using namespace std;
- const int oldeAge=150;
- void SortAges(int *ages,int n)
- {
- int timesOfAges[oldeAge];
- //辅助数组初始化
- for (int i=0;i<oldeAge;i++)
- timesOfAges[i]=0;
- //使用辅助数组计数
- for (i=0;i<n;i++)
- {
- if(ages[i]>150)
- ages[i]=149;
- timesOfAges[ages[i]]++;
- }
- //遍历辅助数组,将元素按排序顺序写回原数组
- int index=0;
- for (i=0;i<oldeAge;i++)
- {
- while(timesOfAges[i]>0)
- {
- ages[index]=i;
- index++;
- timesOfAges[i]--;
- }
- }
- }
- int main(int argc, char* argv[])
- {
- int ages[]={13,43,35,7,58,6,32,9,65,22,47,45,7,47,78,5,32,68,67,54,34,43,56,45};
- SortAges(ages,sizeof(ages)/4);
- for (int i=0;i<sizeof(ages)/4;i++)
- {
- cout<<ages[i]<<" ";
- }
- cout<<endl;
- return 0;
- }
22、判断二叉树是不是平衡二叉树
首先我们需要求二叉树的深度。求出二叉树的深度之后,我们很容易就能想到一个思路:在遍历树的每个结点的时候,调用函数TreeDepth得到它的左右子树的深度。如果每个结点的左右子树的深度相差都不超过 1,按照定义它就是一棵平衡的二叉树。
这种思路对应的代码如下:
- bool IsBalanced(BinaryTreeNode* pRoot)
- {
- if(pRoot == NULL)
- return true;
- int left = TreeDepth(pRoot->m_pLeft);
- int right = TreeDepth(pRoot->m_pRight);
- int diff = left - right;
- if(diff > 1 || diff < -1)
- return false;
- return IsBalanced(pRoot->m_pLeft) && IsBalanced(pRoot->m_pRight);
- }
23、输入一个字符串,输出该字符串中字符的所有组合。
举个例子,如果输入 abc,它的组合有 a、b、c、ab、ac、bc、abc。
假设我们想在长度为 n 的字符串中求 m 个字符的组合。我们先从头扫描字符串的第一个字符。针对第一个字符,我们有两种选择:一是把这个字符放到组合中去,接下来我们需要在剩下的 n-1 个字符中选取 m-1 个字符;二是不把这个字符放到组合中去,接下来我们需要在剩下的 n-1 个字符中选择 m 个字符。这两种选择都很容易用递归实现。下面是这种思路的参考代码:
- #include "stdafx.h"
- #include "stdlib.h"
- #include <iostream>
- #include <string>
- #include <vector>
- using namespace std;
- void Combination(char* string, int number, vector<char>& result)
- {
- if(number == 0)
- {
- vector<char>::iterator iter = result.begin();
- for(; iter < result.end(); ++ iter)
- printf("%c", *iter);
- printf("\n");
- return;
- }
- if(*string == '\0')
- return;
- result.push_back(*string);
- Combination(string + 1, number - 1, result);
- result.pop_back();
- Combination(string + 1, number, result);
- }
- void Combination(char* string)
- {
- if(string == NULL)
- return;
- int length = strlen(string);
- vector<char> result;
- for(int i = 1; i <= length; ++ i)
- {
- Combination(string, i, result);
- }
- }
- int main(int argc, char* argv[])
- {
- Combination("abcd");
- return 0;
- }