1、1.设一棵完全二叉树有 700 个结点,则在该二叉树中有多少个叶子结点如果一棵具有 n 个结点的深度为 k 的二叉树,它的每一个结点都与深度为 k 的满二叉树中编号为 1n 的结点一一对应,这棵二叉树称为完全二叉树。可以根据公式进行推导,假设 n0 是度为 0 的结点总数(即叶子结点数) ,n1 是度为 1 的结点总数,n2 是度为 2 的结点总数,由二叉树的性质可知: n0n2 1,则 n= n0n1n2 (其中 n 为完全二叉树的结点总数) ,由上述公式把 n2 消去得:n= 2n0+n11 ,由于完全二叉树中度为 1 的结点数只有两种可能 0 或 1,由此得到n0(n1)/2 或 n0n
2、/2,合并成一个公式: n0(n1) /2 ,就可根据完全二叉树的结点总数计算出叶子结点数。700/2=350 个叶子节点2.static 数据成员必须在类定义的外部定义。不象普通数据成员,static 成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。静态数据成员的用途之一是统计有多少个对象实际存在。静态数据成员不能在类中初始化,实际上类定义只是在描述对象的蓝图,在其中指定初值是不允许的。也不能在构造函数中初始化该成员,因为静态数据成员为类的各个对象共享,那么每次创建一个类的对象则静态数据成员都要被重新初始化#include class Apublic:/ A() i=3; /
3、不注释掉会出现链接错误void foo() printf(“%dn“,i);private:static int i; /如果换成 static int i=10;出错;int A:i=10; /static 变量在类外定义void main()A a;a.foo();3.求函数返回值,输入 x=9999;int func ( x )int countx = 0;while ( x )countx +;x = xreturn countx;结果呢?知道了这是统计 9999 的二进制数值中有多少个 1 的函数,且有9999910245122561591024 中含有 1 的个数为 2;512 中
4、含有 1 的个数为 1;256 中含有 1 的个数为 1;15 中含有 1 的个数为 4;故共有 1 的个数为 8,结果为 8。1000 - 1 = 0111,正好是原数取反。这就是原理。用这种方法来求 1 的个数是很效率很高的。不必去一个一个地移位。循环次数最少。4.分析下面的程序struct s1int i: 8;int j: 4;int a: 3;double b;struct s2int i: 8;int j: 4;double b;int a:3;printf(“sizeof(s1)= %dn“, sizeof(s1);printf(“sizeof(s2)= %dn“, sizeof
5、(s2);result: 16, 24第一个 struct s1int i: 8;int j: 4;int a: 3;double b;理论上是这样的,首先是 i 在相对 0 的位置,占 8 位一个字节,然后,j 就在相对一个字节的位置,由于一个位置的字节数是 4 位的倍数,因此不用对齐,就放在那里了,然后是a,要在 3 位的倍数关系的位置上,因此要移一位,在 15 位的位置上放下,目前总共是18 位,折算过来是 2 字节 2 位的样子,由于 double 是 8 字节的,因此要在相对 0 要是 8个字节的位置上放下,因此从 18 位开始到 8 个字节之间的位置被忽略,直接放在 8 字节的位置
6、了,因此,总共是 16 字节。1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: struct bs unsigned a:4 unsigned :0 /*空域*/ unsigned b:4 /*从下一单元开始存放 */ unsigned c:4 在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过 8 位二进位3. 位域可以无位
7、域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:struct k int a:1 int :2 /*该 2 位不能使用*/ int b:3 int c:2 ; 从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。5.在对齐为 4 的情况下 分析下面程序的结果struct BBBlong num;char *name;short int data;char ha;short ba5;*p;p=0x1000000;p+0x200=_;(Ulong)p+0x200=_;(char*)p+0x200=_;解答:假设在 32 位 CPU 上,sizeof(l
8、ong) = 4 bytessizeof(char *) = 4 bytessizeof(short int) = sizeof(short) = 2 bytessizeof(char) = 1 bytes由于是 4 字节对齐,sizeof(struct BBB) = sizeof(*p)= 4 + 4 + 2 + 1 + 1/*补齐*/ + 2*5 + 2/*补齐*/ = 24 bytes (经 Dev-C+验证)p=0x1000000;p+0x200=_;= 0x1000000 + 0x200*24 指针加法,加出来的是指针类型的字节长度的整倍数。就是 p偏移 sizeof(p) *0x2
9、00.(Ulong)p+0x200=_;= 0x1000000 + 0x200 经过 ulong 后,这已经不再是指针加法,而变成一个数值加法了(char*)p+0x200=_;= 0x1000000 + 0x200*1 结果类型是 char*,这儿的 1 是 char 的数据类型是 1 字节6.分析一下下面程序的输出结果i ncludei nclude i nclude i nclude i nclude i nclude typedef struct AAint b1:5;int b2:2;AA;void main()AA aa;char cc100;strcpy(cc,“01234567
10、89abcdefghijklmnopqrstuvwxyz“);memcpy(cout int main(void) int *p;int arr100;p = return 0;答案:int *p; /二级指针/得到的是指向第一维为 100 的数组的指针应该这样写i nclude int main(void) int *p, *q;int arr100;q = arr;p = return 0;8.写一个内存拷贝函数,不用任何库函数 .void* memcpy(void* pvTo, const void* pvFrom, size_t size)assert(pvTo != NULL) by
11、te* pbTo = pvTo;byte* pbFrom = pbFrom;while (size- 0)*pbTo+ = *pbFrom+;return pvTo;9.将一个数字字符串转换为数字.“1234“ 1234int convert(char* str)int k = 0;while (*str != 0)k = k * 10 + (*str+) - 0;return k;10.写出运行结果#include #include #define STRCPY(a, b) strcpy(a#_p, #b)#define STRCPY1(a, b) strcpy(a#_p, b#_p)int
12、 main(void) char var1_p20;char var2_p30;strcpy(var1_p, “aaaa“);strcpy(var2_p, “bbbb“);STRCPY1(var1, var2);STRCPY(var2, var1);printf(“var1 = %sn“, var1_p);printf(“var2 = %sn“, var2_p);return 0;答案:var1 = bbbbvar2 = var1宏中“#“和“#“的用法一、一般用法我们使用#把宏参数变为一个字符串,用#把两个宏参数贴合在一起.用法:#include#includeusing namespace
13、 std;#define STR(s) #s#define CONS(a,b) int(a#e#b)int main()printf(STR(vck); / 输出字符串“vck“printf(“%dn“, CONS(2,3); / 2e3 输出:2000return 0;二、当宏参数是另一个宏的时候需要注意的是凡宏定义里有用#或#的地方宏参数是不会再展开.1, 非#和#的情况#define TOW (2)#define MUL(a,b) (a*b)printf(“%d*%d=%dn“, TOW, TOW, MUL(TOW,TOW);这行的宏会被展开为:printf(“%d*%d=%dn“, (
14、2), (2), (2)*(2);MUL 里的参数 TOW 会被展开为(2).2, 当有#或#的时候#define A (2)#define STR(s) #s#define CONS(a,b) int(a#e#b)printf(“int max: %sn“, STR(INT_MAX); / INT_MAX #include这行会被展开为:printf(“int max: %sn“, “INT_MAX“);printf(“%sn“, CONS(A, A); / compile error这一行则是:printf(“%sn“, int(AeA);A 不会再被展开, 然而解决这个问题的方法很简单.
15、 加多一层中间转换宏.加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏 (_STR)就能得到正确的宏参数.#define A (2)#define _STR(s) #s#define STR(s) _STR(s) / 转换宏#define _CONS(a,b) int(a#e#b)#define CONS(a,b) _CONS(a,b) / 转换宏printf(“int max: %sn“, STR(INT_MAX); / INT_MAX,int 型的最大值,为一个变量#include输出为: int max: 0x7fffffffSTR(INT_MAX) _STR(
16、0x7fffffff) 然后再转换成字符串;printf(“%dn“, CONS(A, A);输出为:200CONS(A, A) _CONS(2), (2) int(2)e(2)11:此题考查的是 C 的变长参数, 就像标准函数库里 printf()那样.#includeint ripple ( int , .);main()int num;num = ripple ( 3, 5,7);printf( “ %d“ , num);int ripple (int n, .)int i , j;int k;va_list p;k= 0; j = 1;va_start( p , n);for (; j
17、n; +j)i = va_arg( p , int);for (; i; i return k;这段程序的输出是:(a) 7(b) 6(c) 5(d) 3答案: (c)在 C 编译器通常提供了一系列处理可变参数的宏,以屏蔽不同的硬件平台造成的差异,增加程序的可移性。这些宏包括 va_start、 va_arg 和 va_end 等。采用 ANSI 标准形式时,参数个数可变的函数的原型声明是:type funcname(type para1, type para2, .)这种形式至少需要一个普通的形式参数,后面的省略号不表示省略,而是函数原型的一部分。type 是函数返回值和形式参数的类型。不同
18、的编译器,对这个可变长参数的实现不一样 ,gcc4.x 中是内置函数.关于可变长参数,可参阅http:/ p; /*定义一个变量 ,保存函数参数列表 的指针*/va_start( p , n); /*用 va_start 宏初始化变量 p, va_start 宏的第 2 个参数 n, 是一个固定的参数,必须是我们自己定义的变长函数的最后一个入栈的参数也就是调用的时候参数列表里的第1 个参数*/for (; jn; +j) /* j 从 1 开始, 遍历所有可变参数 */ i = va_arg( p , int); /*va_arg 取出当前的参数, 并认为取出的参数是一个整数(int)*/ f
19、or (; i; i /* 如果 i 不为 0, k 自加, i 与 i-1 进行与逻辑运算, 直到 i 为 0 这是一个技巧,下面会谈到它的功能*/当我们调用 ripple 函数时,传递给 ripple 函数的 参数列表的第一个参数 n 的值是 3 .va_start 初始化 p 指向第一个未命名的参数(n 是有名字的参数) ,也就是 is 5 (第一个).每次对 va_arg 的调用,都将返回一个参数,并且把 p 指向下一个参数.va_arg 用一个类型名来决定返回的参数是何种类型, 以及在 var_arg 的内部实现中决定移动多大的距离才到达下一个 参数(; i; i那么要是想让程序跳转
20、到绝对地址是 0x100000 去执行,应该怎么做?答案:*(void (*)( )0x100000 ) ( );首先要将 0x100000 强制转换成函数指针,即:(void (*)()0x100000然后再调用它:*(void (*)()0x100000)();用 typedef 可以看得更直观些:typedef void(*)() voidFuncPtr;*(voidFuncPtr)0x100000)();13.7.C+中为什么用模板类。答:(1)可用来创建动态增长和减小的数据结构(2)它是类型无关的,因此具有很高的可复用性。(3)它在编译时而不是运行时检查数据类型,保证了类型安全(4)它是平台无关的,可移植性(5)可用于基本数据类型