最近不务正业啊,下周就要考试了,书还没看,瞎做了几道C语言的面试题,发现C语言真是博大精深啊!
一、结构体
1、(2013年大华笔试)Intel X86 PC 以下程序运行输出的结果是:
#include <stdio.h> int main() { union{ struct{ unsigned short s1:3; unsigned short s2:3; unsigned short s3:3; }x; char c; }v; v.c = 103; printf("%d\n", v.x.s1); return 0; }
我的理解:共用体能够存储不同的数据类型,而只能同时存储其中的一种类型,故共用体所占的内存空间为x或者c中较长的一个。
结构体中可以用特定的位数存储某个数据类型,故结构体x的内存空间为9。
共用体v占据了9个比特,故v.c(103)(0x67)存储为001100111,因而输出v.x.s1只占用最低的3个比特,故输出111
所以结果为:7
验证:系统:Ubuntu 11.10 32位 编译器:gcc
2、(微软笔试)以下程序在X86 PC下的运行结果:
#include <stdio.h> struct _test { unsigned short a:4; unsigned short b:5; unsigned short c:7; }test; void main() { int i; test.a = 2; test.b = 3; test.c = 0; i = *((short*)&test); printf("%d\n", i); }
我的理解:跟上题的思路一致,结构体test在内存中的长度为2个字节,数据存储为0000000000110010,i为结构体低两个字节的数据即00110010(0x32)所以答案为
50
验证:软硬件环境与上题相同。
二、指针
指针问题很复杂,一直以来都搞不清楚,不过指针当属C语言中最有魅力的一部份的东西了,下面看一道简单的例子:
1、(2011年谷歌笔试)运行以下程序后,输出结果是什么?
#include <stdio.h> void foobar(int a, int *b, int **c) { int *p = &a; *p = 101; *c = b; b = p; } int main() { int a = 1; int b = 2; int c = 3; int *p = &c; foobar(a, &b, &p); printf("%d %d %d %d\n", a, b, c, *p); return 0; }
我的想法:指针的问题最好方法借助于画图,直观、易懂!做法如下:main函数中:
100 104 108 10c
1 2 3 108(104)
a b c p
传递给子函数的是a = 1, &b = 104,&p = 108;
foobar函数中:(注意:*c=b更改了内存空间为10c的值)
200 204 208 20c
101 200 200
a b c p
所以可见在main函数中a,b,c,*p分别为1,2,3,2
验证:软硬件环境如上
2、(2013年阜博通面试题)
题目:你熟悉memcpy函数吗?该怎么实现这个函数?这个函数有什么问题?
思考过程:-听到这道题其实比较意外?memcpy用的很少,更别说有什么问题了!其实memcpy实现跟strcpy基本一样,memcpy适用范围更广。以下是代码:
#include <stdio.h> #define SIZE 10 void *memcpy(void *dst, const void *src, int length); //void *memmove(void *dst, const void *src, int length); int main() { // static int Dst[SIZE]; int Src[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; static int Dst[SIZE]; memcpy(Dst, Src, 4 * SIZE); for(int index = 0; index < SIZE; index++) printf("%d ", Dst[index]); printf("\n"); /* memmove(Dst, Src, 4 * SIZE); for(int index = 0; index < SIZE; index++) printf("%d ", Dst[index]);*/ return 0; } void *memcpy(void *dst, const void *src, int length) { char *d = (char *)dst; const char *s = (char *)src; while(length--) *d++ = *s++; return dst; }
当然这个代码似乎是没问题的,而且一般情况下用基本不成问题的。但是,当源地址与目的地址有重合的时候,特别是当源地址在低,目的地址在高的情形下,很有可能会出现问题,故应该采用memmove这个函数实现,代码如下:#include <stdio.h> #define SIZE 10 void *memcpy(void *dst, const void *src, int length); void *memmove(void *dst, const void *src, int length); int main() { // static int Dst[SIZE]; int Src[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; static int Dst[SIZE]; memcpy(Dst, Src, 4 * SIZE); for(int index = 0; index < SIZE; index++) printf("%d ", Dst[index]); printf("\n"); memmove(Dst, Src, 4 * SIZE); for(int index = 0; index < SIZE; index++) printf("%d ", Dst[index]); return 0; } void *memcpy(void *dst, const void *src, int length) { char *d = (char *)dst; const char *s = (char *)src; while(length--) *d++ = *s++; return dst; } void *memmove(void *dst, const void *src, int length) { char *d = (char *)dst; char *s = (char *)src; if(s > d){ while(length--) *d++ = *s++; } else if(s < d){ d = d + length -1; s = s + length -1; while(length --){ *d -- = *s --; } } return dst; }
三、派生类
涉及到派生类构造函数与析构函数的顺序问题,如下例:
1、(2012年大华笔试)写出下列程序的运行结果。
#include <cstdio> using namespace std; class Base { public: int m_a; Base(int a = 2) : m_a(a){} virtual ~Base(){ printf("A %d ", m_a); } }; class Derived:public Base { public: Derived(int a = 4) : Base(a){} virtual ~Derived(){ printf("B %d ", m_a); } }; int main(int argc, char *argv[]) { Base *aa, bb; Derived cc(3); aa = new Derived; delete aa; return 0; }
我的理解:对于局部变量是存储在栈中的,所以该函数中应该按照aa、cc、bb的顺序析构的。(对于指针变量要先初始化后才能使用)而对于派生类的析构是按照先子类、后基类的顺序析构的。
所以结果为:B 4 A 4 B 3 A 3 A 2
验证:
四、运算符
1、(2013年虹软笔试)以下程序输出的结果是?
#include <stdio.h> int main() { int x = 99999; int count = 0; while(x){ x = x & (x-1); count++; } printf("%d\n", count); return 0; }
我的理解:当时想得比较复杂,浪费了很多时间。其实很简单,主要是要能区别位与 与 逻辑与,这道题应该是位与,并且,x的众多位1中总有一个并且唯独一个x-1的位0与之对应,因此求出99999包含多少位1即可得出正确答案。99999的二进制表示为:11000011010011111,1的个数为10,因而为10.
验证:
未完待续!!!!