今天有上课了,王老师与我们分享了一些笔试容易出错的问题,我把它总结出来与大家一起学习。
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的四个字节已经放不下,从另一个存储单元(视不同的编译器而定)开始存放,实际存放如下图(上面的数字是我假设的地址,方便大家看):
阅读(1523) | 评论(0) | 转发(0) |