大漠落日

while(!dead) study++;
posts - 46, comments - 126, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

C Puzzles详细分析

Posted on 2010-05-27 17:04 乱78糟 阅读(3144) 评论(11)  编辑 收藏 引用 所属分类: C语言派系
极客C Puzzles。
这里有常见的错误,也有易混淆的知识点,还有奇妙的算法,更有那些变态的用法。
原题目都在这里:http://www.gowrikumar.com/c/,下面详细给出我的解答。

1.sizeof操作符返回值为size_t型,在windows/linux下为typedef size_t unsigned int,而int和unsigned int进行比较时,编译器会自动把int转换为unsigned int,又因为int d = -1,所以转换之后溢出为正值4294967295,条件不成立。

2.这道题命很简单,仔细看一下就会发现OS_HP-UX_print()中间那个字符“-”,变量和函数名只能由字母、数字还有下划线组成。

3.continue语句导致最近的循环语句的当前迭代结束,执行权被传递给条件计算部分。SO,只会打印一个1。

4.stdout换行缓冲,或者等缓冲区满了一并输出,而stderr无缓冲,即时输出,故一开始是看不到hello-out的。

5.这涉及到一个宏展开的问题。先来讲一下宏展开的步骤,以#define MACRO_TEST(x) x为例,如果宏带有参数,例如x成为形参,调用的时候例如MACRO_TEST(z),那么z则成为实参:
   第一步,实参替换形参,带入宏文本中;
   第二步,如果实参也是宏,则继续展开该实参宏;
   第三步,继续展开宏文本,如果文本中还有宏,继续展开,否则宏展开顺利结束。
   上面第一步中,有一个特例,即字符串化预处理符#,如果遇到它,停止后面的宏展开,而是把这个参数当成字符串处理。
SO,这一题就好解答了。第一个输出完全展开宏,为12,而第二个不完全展开,为f(1,2)。

6.很简单,int和const char怎么比?//错误
  正确的应该是default拼写错了。。。这个case语法上是对的,我理解成语义了,非常感谢网友们的指正。

7.因为我的PC是32位的,所以无法测试,google一把,发现和CPU架构有关,IA-64是RISC,不能访问未对齐的地址。

8.和第13题一样,解法不同,结果相同。

9.浮点数不能直接比较。

10.int a=1,2;这句被编译器认为是声明定义变量,而不是逗号表达式,2是常量,不能为变量名,非法。

11.printf返回值是实际输出值的个数,SO,值为4321。

12.这个看到register关键字,想来是底层应用,google之果然,duff's device,大大的有名。详细可以看这个帖子的链接

13.每次保留那些不是最后一位的1,循环几次,就是有几个1。这个函数针对unsigned int,其实对int也是适用的。

14、函数空参数列表在C语言中表示不定参数,仅有一个void表示没有参数,SO,program 1是编译不过的。注意:C++中给前面两个函数传参数都是非法的。

15、这个值得好好研究,首先来看一下第一个输出汇编代码(MS V8生成)。
   
    float a = 12.5;
00411576  fld         dword ptr [__real@41480000 (4156C0h)]   //将立即数放入浮点寄存器ST0
0041157C  fstp        dword ptr [a]                           //将ST0中的数据放入变量a中 
    printf(
"%d\n", a);
0041157F  fld         dword ptr [a]                           //再一次将变量a中的数据放入ST0
00411582  mov         esi,esp                                 //保存栈顶
00411584  sub         esp,8                                   //调整栈顶指针ESP,注意这里调整的是8个字节,不是float的4个,说明内部按double转换了
00411587  fstp        qword ptr [esp]                         //将ST0中的数据入栈
0041158A  push        offset 
string "%d\n" (415640h)          //printf函数参数入栈
0041158F  call        dword ptr [__imp__printf (4182B8h)]     //调用printf
00411595  add         esp,0Ch                                 //栈顶恢复
   00411587  fstp        qword ptr [esp]这句代码之后,我们来看看刚刚入栈的内容,这时候ESP=0012FE7C,查看内存,如下图

   发现没有,头4个字节(红框部分),printf("%d")打印头四个字节,故而输出为0。
   第二个输出就简单了,将变量a的数据强制为int输出。

16、类型T的指针和类型T的数组并非同种类型,程序试图写入0X00000004内存,崩溃。详细可以参考CCFAQ-0.9.4 ch6.1和ch6.2。

17、switch跳过第一个case或default之前的代码,输出结果不定。C++编译器(G++、VC)是编译不过的,提示a的初始化被case/default标签跳过了。

18、数组做参数传递时会退化成指针,函数void size(int arr[SIZE])等价于void size(int *arr),输出为4。

19、如果传给Error函数的参数字串包含格式化字符串怎么搞?例如,"test123...%d",那么输出可能为"test123...1245032",大大超出预期。

20、输入a,按回车,输出a,换行,换行,然后程序结束。这说明第二个scanf("%c", &c),这样不包含空格,直接读入了回车字符。当然,有空格是不读的。原因?C99 7.19.6.2第五条说的狠清楚:包含空白符的指令从输入中读取第一个非空白符字符,知道无字符可读。

21、数组溢出。

22、输出10,4,10,因为sizeof操作符实在编译时决定的,里面的i++不会起作用。
    这里补充一下,sizeof('a')=?,C99中将'a'理解为整形字符常量,为int;C++中为字符字面量,为char。

23、指针类型不匹配。

24、不要看到逗号表达式就激动,等号优先级高于逗号,所以输出1,不是3。如果改成i=(1,2,3)则是3。

25、可能死循环、除数为0。

26、不懂,以后有时间在研究

27、参考第5题。
  
28、算法原理,让两个int数字互掐,直到两人相等为止。如果是4个数求结果就是4强淘汰赛,分两队互掐,掐完的胜利者再互掐,呵呵。scanf返回的是输入的字符数目。

29、其实y=y/*p是注释语句。。。

30、同20题,scanf读取非‘-’字符。

31、scanf("%d\n", &n);

32、加号的优先级高于左移运算符。

33、三目运算符?:中不能使用return

34、i--一直小于20,循环到溢出为止。更改: n--。

35、ptr2是一个int型变量,不是指针。声明指针的最后方法是让*紧挨着放在变量名前面,例如:int *ptr1, *ptr2。

36、a没被初始化就运算也就算了,尽然还有除0这种严重错误。

37、条件表达式一旦一个不成立,立即转到下一个独立条件判断,汇编码可以很好的看出。打印8,不是9,也不是7。

38、指针a已经指导最后了,free崩溃。

39、这个题目貌似很变态,别怕,还是拿出汇编来看问题。
   printf(&a["ya!hello! how is this ? %s\n"], &b["junk/super"]);
00413734  mov         eax,dword ptr [b] 
00413737  add         eax,offset string "junk/super" (415994h) //eax指向string "junk/super"的第5个字符
0041373C  mov         esi,esp 
0041373E  push        eax  
0041373F  mov         ecx,dword ptr [a] 
00413742  add         ecx,offset string "ya!hello! how is this ? %s\n" (415A80h) //ecx指向string 的第3个字符
00413748  push        ecx  
00413749  call        dword ptr [__imp__printf (4182B8h)] 
   由汇编码来分析,其实相当于那一句C代码等价于printf("hello! how is this ? %s\n", "super")。
   在来看,说明&b["junk/super"]相当于下面的这两行代码:
char arr[] = "junk/super";
const char *= &arr[b];    
   即数组a[n]等价于n[a]。
   第二行printf中的1["this"]其实等价于"this"[1],即取出字符'h'。下面在来一次测试:
int a[] = {45678};
int b = 3[a];
printf(
"%d\n", b);//输出7
   原理参考C99 6.5.2.1第二条,E1[E2]的操作下表[]被解析为(*((E1)+(E2))),so,上面的代码就彻底清楚了。

40、读取字符知道遇到a为止,参见C99 7.19.6.2第12条。

41、a.c中的为声明,b.c中的为定义,main.c中的为外联声明,需要找定义,所以链接b.c中的变量定义,与a.c中的无关。

42、阅读了这题,才明白为什么C那么牛逼。参考 ccfaq-0.9.4 第2.12问。offsetof(struct s, f)计算s结构中f域的偏移量。

43、SWAP(a++, b++)。

44、同第五题。

45、
int is_overflow(int *p, int a, int b)
{
    
long x, y, z;
    x 
= (long)a;
    y 
= (long)b;

    
*= a + b;

    z 
= (long)((unsigned long)x + y);
    
if ( (z^x) >= 0 || (z^b) >= 0 )
        
return 1;
    
else 
        
return 0;                   //溢出
}

46、看不懂

47、同43.

48、起码为VarArguments(const char *format, ...),可参考printf函数,不定参数不能只有这一个...。因为标准C要求用可变参数的函数至少有一个固定参数项,这样才可以使用va_start()。
补充:标准C++中可以这样定义,表示0个或多个参数,例如void fun(...);函数void fun(void)和函数void fun()意思完全一样,和C不同,见14题。

49、还真没想出来。。。

50、读取format字符串的长度存入变量中,例如printf("hello%n", &a),a的值为5。

51、
mov eax, [a]
add eax, [b]

52、%%

53、const char *p指向const char的非const指针,char* const p指向char的const指针。

54、memcopy可以重叠,而后者不可以。

55、%lf打印double,%f打印float

56、
short int a = 0x00ff;
char *= (char *)&a;
if (*== 0xff)
   printf(
"big endia");
else
   printf(
"small endia");

57、
int main()
{
   
if (printf("hello world"< 0)
   {}
}


Feedback

# re: C Puzzles详细分析  回复  更多评论   

2010-05-28 12:55 by 凡客诚品衬衫
路过~~~~~~~~~~·

# re: C Puzzles详细分析  回复  更多评论   

2010-05-28 14:14 by zhangjs
第六题明显是default拼错,当成label了

# re: C Puzzles详细分析  回复  更多评论   

2010-05-28 14:20 by pengwang
只看第六题你就没理解C语言,这个明显是default拼写错了,编译器会把它当成标号,而不是int不能和const char比。

# re: C Puzzles详细分析  回复  更多评论   

2010-05-31 09:10 by 乱78糟
@pengwang
非常感谢指正错误,已经修正

# re: C Puzzles详细分析  回复  更多评论   

2010-07-18 17:23 by liu zhiquan
第25个,case '-':
push(pop() - pop());
break;
case '/':
push(pop() / pop());
减法、除法和加法、乘法不同,加法和乘法有交换律,不需要考虑函数调用的顺序。减法和除法不一样,必须保证第一个pop()是被减数或被除数。

# re: C Puzzles详细分析  回复  更多评论   

2014-03-05 20:23 by lazybug
第19个作者已经提示了是 Error 函数定义的问题,Error的参数应该是 const char *,否则const char *类型的字符串不能作为实参传递

# re: C Puzzles详细分析  回复  更多评论   

2014-03-05 20:35 by lazybug
第25我觉得是 pop() - pop() 的 - 号两边求值顺序不确定,结果可能刚好反过来,同理,/ 也存在这个问题

# re: C Puzzles详细分析  回复  更多评论   

2014-03-05 20:40 by lazybug
第27题:
0001,0010,0100是八进制的1, 8, 64

# re: C Puzzles详细分析  回复  更多评论   

2014-03-05 21:43 by lazybug
第52题也可以 printf("%s", "I can print %");

# re: C Puzzles详细分析  回复  更多评论   

2014-03-05 21:48 by lazybug
第49题 大概可以用 ?: 运算符

# re: C Puzzles详细分析  回复  更多评论   

2014-10-17 11:24 by shuit
楼主第7题搜的感觉没说对点。http://stackoverflow.com/questions/7545365/why-does-this-code-segfault-on-64-bit-architecture-but-work-fine-on-32-bit?rq=1
stackoverflow上说是以为没引用头文件时,malloc的返回值默认是int,而在IA64上sizeof(int)和sizeof(int*)不相等,相当于没拿到正确的地址

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理