联发科部分笔试题及答案

这里作者的编译器标准是C++17.

题目一

以下程序的运行结果

int main() {
    
    
    int x = 19;    
    x &= 0x1f;
    printf("%d %d\n", x << 1, x >> 1);
    return 0;
}

19的二进制 0001_0011

0x1f的二进制 0001_1111

所以 x = 0001_0011,左移一位为 0010_0110,右移一位为 0000_1001

所以答案为 38,1

题目二

64bit系统上,有如下结构体定义,下面程序的输出结果为:

#include <stdio.h>

typedef unsigned int GEN_OBJ_T[7];
typedef enum {
    
    
    COLOR_SYS_A,
    COLOR_SYS_B,
    COLOR_SYS_C,
    COLOR_SYS_D,
    COLOR_SYS_E,
    COLOR_SYS_F,
    COLOR_SYS_G,
    COLOR_SYS_H,
    COLOR_SYS_AUTO = 30,
    COLOR_SYS_UNKOWN,
} COLOR_SYS_T;

#pragma pack(4)
typedef struct {
    
    
    union {
    
    
        unsigned char u_char;          
        COLOR_SYS_T u_sys_t;
        unsigned long int u_long_int: 24;
        signed long long _long: 16;
        int u_int[96 / COLOR_SYS_UNKOWN];
    } u;
} GEN_SUB_OBJ_T;
#pragma pack()
typedef struct _my_obj_t {
    
    
    signed char a;         
    GEN_OBJ_T obj;         
    GEN_SUB_OBJ_T sub_obj; 
    struct _my_obj_t *next;
    struct _my_obj_t *prev;
} MY_OBJ_T;

int main(void) {
    
    
    printf("$d %d\n", sizeof(MY_OBJ_T), sizeof(GEN_SUB_OBJ_T));
}

这个题的目的是求两个结构体的长度,首先看 MY_OBJ_T 这个结构体,这里包含了几个知识点

  • COLOR_SYS_UNKOWN的值是31,因为枚举默认从0开始,但是中间被截断了,COLOR_SYS_AUTO=30,所以COLOR_SYS_UNKOWN的值是31。
  • 96 / COLOR_SYS_UNKOWN的值是3,因为这里是放在数组里面,需要一个整数,c语言中的整数是直接取整,不是四舍五入。
  • #pragma pack(4)的作用是指定多少字节对齐,这里指定是4字节对齐,unsigned char是1字节,但是对齐后是4字节,枚举类型是整型,也是4字节,unsigned long int 在c语言标准中可以是4字节也可以是8字节,作者试了一下,在GNU的编译器下就是4字节,signed long long 就是8字节。 int u_int[96 / COLOR_SYS_UNKOWN]的长度就是数组的长度,为12字节,正好是对齐数量的3倍。
  • #pragma pack()取消了设定好的字节对齐数,也就是后面的程序不用在编译的时候强行按照4字节进行对齐了。
  • 所以这个结构体的长度就是12,为嵌合体中最长的,就是 int u_int[96 / COLOR_SYS_UNKOWN]
  • 如果我们将#pragma pack()设定为8,也就是8字节对齐,那么答案就是16,因为联合体需要8字节对齐,数组的长度为12字节,自动就补齐为2个8字节也就是16字节了。

接着看 MY_OBJ_T 这个结构体

  • struct _my_obj_t *next这个指针决定了结构体的对齐长度为8字节
  • signed char1字节,GEN_OBJ_T4*7字节,所以它两个会凑4个8字节
  • GEN_SUB_OBJ_T这个结构体有12字节,对齐后是2个8字节
  • 所以整体是(4+2+1+1)个8字节

总结:这个题对于结构体,联合体,枚举对齐的知识考察的非常丰富!非常有参考价值。

题目三

以下二维数组中申明错误的是

char a[2][3] = {
    
    0};
char a[][3] = {
    
    0};
char a[2][] = {
    
    0};
char a[][] = {
    
    0};

答案是CD,很好奇b为什么是对的,实际上在申明数组的时候可以不指定多少行,根据你的初始化列表自动推断,比如

char b[][3] = {1, 2, 3, 4, 5, 6};

他可以自动推断出是两行三列的二维数组。

也就是说,创建多维数组的时候可以忽略第一维度的大小,注意:不能忽略第二维维度了。

而且忽略第一维度的时候也必须提供参数列表让编译器去自动推断数组的大小,实际上不是你没有了,是编译器帮助你完成了这个工作。

题目四

下列叙述中错误的是

int main(){
	int *p;
	*p = 10;
	return 0;
}

// A 该代码可以编译通过
// B 该代码执行会有segmentation fault
// C 该代码可以正常执行结束
// D 该代码无法通过编译

答案是CD,这个代码乍看上去没问题,实际上有问题,定义了一个指针却没有给指针赋值,也就是这个指针不知道指向哪里的,直接对指针指向的地方写入值,会导致段错误,段错误是因为这个地址程序并没有向操作地址申请,或者访问了不能访问的地址空间。都段错误了怎么可能正常结束。但是这个代码是可以通过编译的。

题目五

下列代码的输出结果:

class Base {
    
    
public:
    Base() {
    
    
        Init();
    }
    virtual void Init() {
    
    
        printf("Base Init\n");
    }
    void func() {
    
    
        printf("Base func\n");
    }
};

class Derived : public Base {
    
    
public:
    virtual void Init() {
    
    
        printf("Derived Init\n");
    }
    void func() {
    
    
        printf("Derived func\n");
    }
};

int main() {
    
    
    Derived d;
    ((Base *) &d)->func();
    return 0;
}

这里的结果是

Base Init
Base func

原因是子类初始化要首先初始化父类,所以直接调用了Base的Init函数,然后为啥没有打印 Drived Init呢,因为子类的构造函数又没有执行 Init 函數

子类强转父类后,执行的是父类的func函数,而不是子类的,只有再转回子类才执行的是子类的

这里介绍了比较难的五个问题,剩下的稍稍简单,不在打字了。。。

猜你喜欢

转载自blog.csdn.net/weixin_43903639/article/details/131834227