Chinaunix首页 | 论坛 | 博客
  • 博客访问: 590850
  • 博文数量: 68
  • 博客积分: 2621
  • 博客等级: 少校
  • 技术积分: 1498
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-23 21:04
文章分类

全部博文(68)

文章存档

2013年(8)

2012年(52)

2010年(8)

分类: C/C++

2012-09-05 18:49:43

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

1. 求下面程序的结果:

点击(此处)折叠或打开

  1. int
  2. main(void)
  3. {
  4.     unsigned short A = 10;
  5.     printf("~A = %u\n", ~A);

  6.     char c = 128;
  7.     printf("c = %d\n", c);
  8. }

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

2. 求输出结果:

点击(此处)折叠或打开

  1. void
  2. GetMemory(char **p, int num)
  3. {
  4.     *p = (char *)malloc(num);
  5. }
  6. int
  7. main(void)
  8. {
  9.     char *str = NULL;

  10.     GetMemory(&str, 100);
  11.     strcpy(str, "hello");
  12.     free(str);
  13.     if( str != NULL )
  14.         strcpy(str, "world");
  15.     printf("str is %s\n", str);
  16. }
  17. 运行结果: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. 求下面程序的运行结果:

点击(此处)折叠或打开

  1. int
  2. main(void)
  3. {
  4.     char a[10];
  5.    
  6.     printf("%d\n", strlen(a));

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

点击(此处)折叠或打开

  1. int
  2. main(void)
  3. {
  4.     char a[10];
  5.     int i;

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

  9.     return 0;
  10. }
下面是运行结果:

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

4. x = 9999, 求下面函数的返回值

点击(此处)折叠或打开

  1. int
  2. func(int x)
  3. {
  4.     int countx = 0;
  5.     
  6.     while(x)
  7.     {
  8.         countx++;
  9.         x = x & (x-1);
  10.     }

  11.     return countx;
  12. }
  13. 运行结果: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位机)

点击(此处)折叠或打开

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