今天有上课了,王老师与我们分享了一些笔试容易出错的问题,我把它总结出来与大家一起学习。
1. 求下面程序的结果:

点击(此处)折叠或打开

    int
    main(void)
    {
    unsigned short A = 10;
    printf("~A = %u\n", ~A);

    char c = 128;
    printf("c = %d\n", c);
    }
    运算结果:4294967285(我的机子上,short是16位的) -128
分析:第一个printf()语句考察了对数据进行按位求反,最后的结果以%u无符号的形式打印出来。按位求反以后,最高位为有效位,而不是符号位,我们将其转换为十进制打印出来。 第二个printf()语句考察了数据类型char默认是有符号(signed)还是无符号的(unsigned)。这个题很容易让人答错,运行结果后是-128。从结果可以发现,若是无符号数的话,以十进制打印出来,应该为128。所以,可以确定char默认为有符号数(signed)。128对于有符号的char来说,发生了溢出,所以结果为-128。
2. 求输出结果:

点击(此处)折叠或打开

    void
    GetMemory(char **p, int num)
    {
    *p = (char *)malloc(num);
    }
    int
    main(void)
    {
    char *str = NULL;

    GetMemory(str, 100);
    strcpy(str, "hello");
    free(str);
    if( str != NULL )
    strcpy(str, "world");
    printf("str is %s\n", str);}运行结果:str is world
分析:这个题其实是有bug的。main()调用GetMemory(),GetMemory()调用了malloc(),之后将hello拷贝到str中,此时,str为hello。接下来,free把malloc()申请的空间释放掉了。 注意,我们free掉申请的空间后,并没有将str这个指针的值改变。我们在free(str)前、后分别加上一条语句:printf("%x\n", str);,会发现两个地址是一样的,下面是我的电脑运行出来的结果:
我们紧接着又给str拷贝了world,可能很多人会和我有一样的感觉,就是这是错误的,明明将空间释放掉了,怎么能拷贝到里面呢? 在拷贝world之前,有一条if()语句,判断str是否为NULL,从上面的打印结果可以看到,str并不为NULL,所以会进行下面的拷贝语句。不过,特别要注意,打印出str is world,这完全是一个巧合,因为我们在free与后来的拷贝之间并没有进行新的空间申请,所以,虽然释放了空间,但是我们可以找到地址,在这块内存上进行复制操作。如果我们在free后,又申请了新的空间,刚刚好把这块空间申请了,可能结果就不是我们想要的了。 我们自己平时使用时,一定要避免这种错误。方法是在free()后,将指针置为NULL,这样,由于指针为空,所以对它进行复制操作必然会发生错误。
3. 求下面程序的运行结果:

点击(此处)折叠或打开

    int
    main(void)
    {
    char a[10];

    printf("%d\n", strlen(a));

    return 0;
    }
分析:这个程序重点考察了数组的初始化和strlen()的用法。我的电脑上运行这个程序的结果为2,这是一个随机值,下面我将详细说明:这是我将上面的程序进行扩充后的代码:

点击(此处)折叠或打开

    int
    main(void)
    {
    char a[10];
    int i;

    for(i = 0; i < 10; i)
    printf("a[%d]:%d\n", a[i]);
    printf("length:%d\n", strlen(a));

    return 0;
    }
下面是运行结果:
从运行结果可以看到,a数组中存入的值为垃圾值。length()求的是字符串的长度,到'\0'为止的长度。由此可知,strlen(a)的结果为2。2是一个随机值,每个人的机子上运行出来的结果可能都不一样,因为每个人机子上,数组初始化几个元素是不定的。有的人的机子上可能会打印出大于10的值,这是因为strlen()在求值的时候,从数组第一个元素开始查找,找到连续内存中的第一个'\0'停止,即为strlen()的结果。 特别强调一下,如果上述strlen()换为sizeof()的话,结果为10。strlen()与数组的初始化有关,而sizeof()与初始化无关。
4. x = 9999, 求下面函数的返回值

点击(此处)折叠或打开

    int
    func(int x)
    {
    int countx = 0;

    while(x)
    {
    countx ;
    x = x (x-1);
    }

    return countx;
    }运行结果:8
分析:这道题的重点在于我们要明白函数的功能是什么。 注意到,在while()循环里有一个位运算符,这就意味着我们要将x化为二进制表示形式。循环里,x不断的减1,运算后,x的值也就随之改变。我们知道任何数和1,值都不变;而和0,会在0的对应位将那一位的值置0。会发现,其实countx是在统计二进制数中的1的个数。 那么,9999中有多少个1呢?这里有一个方便的方法来计算,而不用我们通常除二的方法来求十进制数的二进制值。我们可以将9999拆为若干个二进制的幂,然后分别计算每一部分的1的个数:9999 = 9 *1024 1*512 1 *256 1*8 1*4 1*2 1*1 = 9 *(2^10) 1*(2^9) 1*(2^8) 1*(2^3) 1*(2^2) 1*(2^1) 1*(2^0)我们知道2的n次幂为:1个1,后面跟n个0。那么,9*(2^10)中含有2个一,因为9为1001,有两个1,乘以2的10次方,1的个数没变。以此类推,可得9999一共有8个1。
5. 求sizeof(A) = ?(32位机)

点击(此处)折叠或打开

    struct A
    {
    char t:4;
    char k:4;
    unsigned short i:8;
    unsigned long m;
    };运行结果:8(gcc编译器,版本为4.6.3)
分析:这道题考察了字节对齐问题,同时涉及到了位段。特别说明,不同的编译器版本,运行出来的结果可能不一样,本人的机子是gcc 4.6.3。 t占用了4位,k占用了4位,i占用了8位,这是一共占用了2个字节。因为gcc下是以4字节对齐的,这时,再放入m的四个字节已经放不下,从另一个存储单元(视不同的编译器而定)开始存放,实际存放如下图(上面的数字是我假设的地址,方便大家看):