错题整理

一些错题。

考试错题

  1. 编程实现一个对8bit数据(unsigned char)的指定位(比如第n位)的置0或者置1操作,其他位保持不变。

    函数原型:void bit_set(unsigned char *p_data, unsigned char pos, int flag)

    这道题考试的时候我是这么写的:将这个p_data左移、右移。刚才在写关于位段的错题时,想起来还可以这么搞,但是还是移位操作比较方便,嘿嘿,我就试试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    #include <stdio.h>
    #define A(uc, x) uc##x

    #pragma pack(1)
    typedef struct sup
    {
    unsigned char uc1 : 1;
    unsigned char uc2 : 1;
    unsigned char uc3 : 1;
    unsigned char uc4 : 1;
    unsigned char uc5 : 1;
    unsigned char uc6 : 1;
    unsigned char uc7 : 1;
    unsigned char uc8 : 1;
    } *pSUP_S;
    #pragma pack()

    void bit_set(unsigned char *p_data, unsigned char pos, int flag)
    {
    pSUP_S ps = (pSUP_S)p_data;
    switch(pos)
    {
    case 1:
    ps->A(uc, 1) = flag;
    break;
    case 2:
    ps->A(uc, 2) = flag;
    break;
    case 3:
    ps->A(uc, 3) = flag;
    break;
    case 4:
    ps->A(uc, 4) = flag;
    break;
    case 5:
    ps->A(uc, 5) = flag;
    break;
    case 6:
    ps->A(uc, 6) = flag;
    break;
    case 7:
    ps->A(uc, 7) = flag;
    break;
    case 8:
    ps->A(uc, 8) = flag;
    break;
    default:
    break;
    }
    }

    void print(unsigned char a)
    {
    while(a)
    {
    printf("%d", a%2);
    a /= 2;
    }
    printf("\n");
    }

    int main()
    {
    unsigned char a = 1;
    bit_set(&a, 5, 1);
    print(a);
    return 0;
    }

  2. 给出以下定义:

    char x[] = "abcdefg";

    char y[] = {'a','b','c','d','e','f','g'};

    则正确的叙述为(C)

    A)数组x和数组y等价 B)数组x和数组y的长度相同

    C)数组x的长度大于数组y的长度 D)数组x的长度小于y的长度

    分析:粗心大意!选项都不看清就选答案?


  3. 设有如下定义:

    unsigned long arr[] = {6,7,8,9,10};

    unsigned long *p;

    则下列程序段的输出结果为(6,12)

    1
    2
    3
    p = arr;
    *(p + 3) += 3;
    printf("%d,%d\n", *p, *(p + 3));

    分析:粗心大意!慌什么慌,稳住啊。给数组解引用又不是给你解引用,题看完再写啊。


  4. 全局变量可以定义在被多个.c文件包含着的头文件中(❌)

    分析:注意区分定义和声明的概念,definition、declaration!


  5. 下面这道题虽然没错,但是还是想写出来,自己以后看到也注意点,别忘了。

    1
    2
    3
    4
    5
    unsigned char a = 200;
    unsigned char b = 100;
    unsigned char c = 0;
    c = a + b;
    printf("%d %d ",a+b,c);

    C的整型算数运算总是至少以缺省整型类型的精度来进行的,为了获得这个精度,表达式中的字符型和短整型操作数在使用之前被转换为普通整型。《C和指针》

    分析:输出结果为300,44,注意整形提升问题,整形提升笔记


  6. 下面这个题也是粗心大意造成的,但是还是有一点就是没有区分清楚指针数组和数组指针等概念。关于指针的笔记

    1
    2
    3
    4
    5
    6
    7
    8
    unsigned short *pcArr[10][10];
    typedef union unRec
    {
    unsigned long a;
    unsigned short b[7];
    unsigned short c;
    }REC_S;
    REC_S stMax, *pstMax;

    当四字节对齐方式时:

    sizeof(pcArr) = 400是一个10•10的指针数组,数组里存放的是指针。

    sizeof(stMax) = 16注意这里是union,慌什么!?

    sizeof(pstMax) = 4指针的大小

    sizeof(*pstMax) = 16钥匙(&)重4kg,拿着钥匙进了你家门。


  7. 结构体定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct B
    {
    long l1;
    char c2;
    char c3;
    long l4;
    long l5;
    } *p;
    p = (struct B*)0x100;

    p + 0x1 = 0x110,结构体大小是16,指针p的一步长为16,向前走一步加16(10H)。

    (unsigned long)p + 0x1 = 101,注意是强转成了一个数。

    (unsigned long *)p + 0x1 = 0x104强转成了指针。


  8. 有如下定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct A
    {
    unsigned char uc1 : 1;
    unsigned char uc2 : 2;
    unsigned char uc2 : 6;
    unsigned char uc3 : 4;
    unsigned char uc4;
    unsigned char uc5 : 4;
    unsigned char uc6;
    } AAA_S;

    问:AAA_S在1字节对齐和4字节对齐的情况下,占用的空间大小是:6字节、12字节

    分析:对于结构体内存对齐这块掌握的还是不好,也没有整理过笔记什么的,考完试整理一篇笔记,复习复习(预习)。


  9. 以下程序的执行结果为:2 29 0 0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #pragma pack(4)
    int main(int argc, char const *argv[])
    {
    unsigned char puc[4];
    struct tagPIM
    {
    unsigned char ucPiml;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
    } *pstPimData;
    pstPimData = (struct tagPIM *)puc;
    memset(puc, 0, 4);
    pstPimData->ucPiml = 2; // 2-> 10b
    pstPimData->ucData0 = 3;// 3-> 11b
    pstPimData->ucData1 = 4;// 4-> 100b
    pstPimData->ucData2 = 5;// 5-> 101b
    printf("%02x %02x %02x %02x\n", puc[0], puc[1], puc[2], puc[3]);
    return 0;
    }
    #pragma pack()

    分析:首先,结构体tagPIM占4个字节,puc[0]-puc[3]分别指向这4个字节,并初始化为了0,但是tagPIM只用了前两个字节,所以puc[2]和puc[3]的输出结果为0,其余的如下图分析:

    要注意的几点:第一、ucData0放3(11b)的时候,从低地址开始放,放不下的扔掉,对于ucData1也是。第二、要注意是小端,输出结果的时候是从右往左看的二进制数,还要注意输出的是以16进制的形式输出。

    如图


  10. 注意函数传递的时候,形参的问题。

  11. 添加下面分割线内的代码的初衷是为了给gui_show_image这句代码的执行加上限制条件,请问这样修改有没有隐患,若有,该如何修改?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // ......
    gui_push_clip();
    /*我是分割线*/
    #ifdef AAA
    if (show_status == MMI_TRUE)
    #endif
    /*我是分割线*/
    #ifdef BBB
    gui_show_image(x, y, image_id);
    #endif
    gui_pop_clip();
    update_dt_display();
    // ......

    分析:这句话我第一眼看没啥啊,仔细看🧐下:如果宏BBB没有被定义,程序的逻辑就变成了下面这样:

    1
    2
    if (show_status == MMI_TRUE)
    gui_pop_clip();

    修改的方法:加大括号,要养成良好的编码习惯。

牛客的错题整理

  1. 双链表中至多只有一个节点的后继指针为空(🙆‍♂️)

    非循环双链表最后一个节点的后继指针为空;循环双链表没有一个节点的后继指针为空。

  2. 一个长度为99的循环链表,指针A和指针B都指向了链表中的同一个节点,A以步长为1向前移动,B以步长为3向前移动,一共需要同时移动多少步A和B才能再次指向同一个节点?(99)

    我选的50,但并不是50,首先是个循环链表,再来就是只有99个元素,设A走x步,那么B走3x步,两个碰到需要$(3x-x) MOD 99 = 0$,x取99才满足。

  3. 在有n个结点的二叉链表中,值为非空的链域的个数为(n-1)

    值为空的链域个数为n+1个,为非空的个数为n-1个。

  4. 向一个有127个元素的顺序表中插入一个新元素并保持原来顺序不变,平均要移动(63.5)个元素。

    插入:$\frac {n}{2}$,删除:$\frac {n-1}{2}$,查找:$\frac {n+1}{2}$

  5. 广义表第一个元素是表头,其余元素是表尾,如果只有一个元素,那么表尾为空即(),在广义表中注意区分空表和有一个为空表的元素的情况。

  6. 广义表有如下三个特性:1.层次性:广义表的元素可以是子表,而子表的元素还可以是子表,由此,广义表是一个多层次的结构。2.共享性:广义表可为其他表所共享。3.递归表:广义表可以是其自身的一个子表。

  7. 十字链表是无向图的一种存储结构(🙅‍♂️)。

    无向图存储:邻接矩阵、邻接表、多重邻接表。有向图存储:邻接矩阵、邻接表、十字链表


荐读:《C++/C高质量编程指南》、《C语言深度剖析》