作者:陈曦
日期:2012-8-17 12:53:12
环境:[Mac 10.7.1 Lion Intel i3 支持64位指令 gcc4.2.1 xcode4.2]
转载请注明出处
Q1: 为什么下面的输出不按照代码的顺序显示?
#include <stdio.h> #include <unistd.h> int main(int argc, char **argv) { while(1) { fprintf(stdout, "stdout."); fprintf(stderr, "stderr."); sleep(1); } return 0; }
A: 如果把代码改为如下,输出就按照代码顺序显示了:
#include <stdio.h> #include <unistd.h> int main(int argc, char **argv) { setbuf(stdout, NULL); // set stdout to no buffer while(1) { fprintf(stdout, "stdout."); fprintf(stderr, "stderr."); sleep(1); } return 0; }
Q2: 下面的代码中printf函数返回为什么不是1?
#include <stdio.h> #define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue)); int main(int argc, char **argv) { char *str = "我"; PRINT_D(printf(str)); return 0; }
A: 在我的另一篇帖子中已经有详细解释: http://blog.csdn.net/cxsjabcabc/article/details/7528227
如果把代码文件的编码改为其它,答案就出来了: printf返回的是实际输出的字节数。比如http://coolshell.cn/articles/945.html地址的帖子写的是字符数,还有很多地方都不是很清楚得说明是字符数,其实也是一种没有深入调研的模糊解答。
Q3: 下面代码的输出为什么改变了b的数值?
#include <stdio.h> #define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue)); int main(int argc, char **argv) { int a = 1; switch(a) { int b = 2; case 1: PRINT_D(b) break; default: PRINT_D(b) break; } return 0; }
另外,我们查看一下汇编,能够得到结论,其实case标号和default标号相当于一个地址,编译器内部会使用jmp, je, jne等语句直接跳到对应位置来执行,而不会分析它前面的初始化过程。
0x00001f10 <main+0>: push %ebp 0x00001f11 <main+1>: mov %esp,%ebp 0x00001f13 <main+3>: sub $0x28,%esp 0x00001f16 <main+6>: mov 0xc(%ebp),%eax 0x00001f19 <main+9>: mov 0x8(%ebp),%ecx 0x00001f1c <main+12>: movl $0x0,-0x4(%ebp) 0x00001f23 <main+19>: mov %ecx,-0x8(%ebp) 0x00001f26 <main+22>: mov %eax,-0xc(%ebp) 0x00001f29 <main+25>: movl $0x1,-0x10(%ebp) 0x00001f30 <main+32>: xor %dl,%dl 0x00001f32 <main+34>: test %dl,%dl 0x00001f34 <main+36>: jne 0x1f52 <main+66> 0x00001f36 <main+38>: jmp 0x1f38 <main+40> 0x00001f38 <main+40>: lea 0x1fa0,%eax 0x00001f3e <main+46>: mov -0x14(%ebp),%ecx 0x00001f41 <main+49>: mov %eax,(%esp) 0x00001f44 <main+52>: mov %ecx,0x4(%esp) 0x00001f48 <main+56>: call 0x1f7a <dyld_stub_printf> 0x00001f4d <main+61>: mov %eax,-0x18(%ebp) 0x00001f50 <main+64>: jmp 0x1f6a <main+90> 0x00001f52 <main+66>: lea 0x1fa0,%eax 0x00001f58 <main+72>: mov -0x14(%ebp),%ecx 0x00001f5b <main+75>: mov %eax,(%esp) 0x00001f5e <main+78>: mov %ecx,0x4(%esp) 0x00001f62 <main+82>: call 0x1f7a <dyld_stub_printf> 0x00001f67 <main+87>: mov %eax,-0x1c(%ebp) 0x00001f6a <main+90>: mov $0x0,%eax 0x00001f6f <main+95>: add $0x28,%esp 0x00001f72 <main+98>: pop %ebp 0x00001f73 <main+99>: ret
如果把源代码改为如下,依然不能输出3:
int main(int argc, char **argv) { int a = 2; switch(a) { int b = 2; case 1: PRINT_D(b) break; b = 3; case 2: PRINT_D(b) break; default: PRINT_D(b) break; } return 0; }
Q4: sizeof运算符里面跟着i++,为什么i没有自增?
#include <stdio.h> #define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue)); int main(int argc, char **argv) { int i = 1; PRINT_D(sizeof(i++)) PRINT_D(i) return 0; }
0x00001ef0 <main+0>: push %ebp 0x00001ef1 <main+1>: mov %esp,%ebp 0x00001ef3 <main+3>: push %ebx 0x00001ef4 <main+4>: push %edi 0x00001ef5 <main+5>: push %esi 0x00001ef6 <main+6>: sub $0x2c,%esp 0x00001ef9 <main+9>: mov 0xc(%ebp),%eax 0x00001efc <main+12>: mov 0x8(%ebp),%ecx 0x00001eff <main+15>: mov $0x0,%edx 0x00001f04 <main+20>: lea 0x1fa7,%esi 0x00001f0a <main+26>: lea 0x1f94,%edi 0x00001f10 <main+32>: mov $0x4,%ebx 0x00001f15 <main+37>: movl $0x0,-0x10(%ebp) 0x00001f1c <main+44>: mov %ecx,-0x14(%ebp) 0x00001f1f <main+47>: mov %eax,-0x18(%ebp) 0x00001f22 <main+50>: movl $0x1,-0x1c(%ebp) 0x00001f29 <main+57>: mov %edi,(%esp) 0x00001f2c <main+60>: movl $0x4,0x4(%esp) 0x00001f34 <main+68>: mov %edx,-0x20(%ebp) 0x00001f37 <main+71>: mov %ebx,-0x24(%ebp) 0x00001f3a <main+74>: mov %esi,-0x28(%ebp) 0x00001f3d <main+77>: call 0x1f6e <dyld_stub_printf> 0x00001f42 <main+82>: mov -0x1c(%ebp),%ecx 0x00001f45 <main+85>: mov -0x28(%ebp),%edx 0x00001f48 <main+88>: mov %edx,(%esp) 0x00001f4b <main+91>: mov %ecx,0x4(%esp) 0x00001f4f <main+95>: mov %eax,-0x2c(%ebp) 0x00001f52 <main+98>: call 0x1f6e <dyld_stub_printf> 0x00001f57 <main+103>: mov -0x20(%ebp),%ecx 0x00001f5a <main+106>: mov %eax,-0x30(%ebp) 0x00001f5d <main+109>: mov %ecx,%eax 0x00001f5f <main+111>: add $0x2c,%esp 0x00001f62 <main+114>: pop %esi 0x00001f63 <main+115>: pop %edi 0x00001f64 <main+116>: pop %ebx 0x00001f65 <main+117>: pop %ebp 0x00001f66 <main+118>: ret注意main+60位置,它直接将sizeof(i++)得到的数值4放入堆栈,并没有对i自增。
Q5: 为什么形如2["hello"]这样的表达式是什么意思?
#include <stdio.h> #define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue)); #define PRINT_CH(charValue) printf(#charValue" is %c\n", (charValue)); int main(int argc, char **argv) { char ch = 2["hello"]; PRINT_CH(ch) return 0; }
因为编译器对于数组访问根本就采用指针加减的计算方法,即2["hello"] == *(2 + "hello"), "hello"是个字符指针, *(2 + "hello") == *("hello" + 2), 所以结论也就出来了。
Q6: 为什么一个整形数据用%f格式输出,结果改变了?
#include <stdio.h> #define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue)); #define PRINT_F(floatValue) printf(#floatValue" is %f\n", (floatValue)); int main() { int d = 2; PRINT_F(d) return 0; }
A: 原因在于printf分析%f格式时是从对应地址取出sizeof(float)个字节数据然后当成float格式输出,所以,一般情况下,输出都是错误的。
对于float内部的格式,IEEE 754标准写的很清楚,如果出现类似的问题,可以使用如下结构来简单地验证:
// IEEE 754 single floating number's format(intel little-endian mode) typedef union { // float value float f; // intel bits mode that stands for float value struct { unsigned int dot : 23; // low bits unsigned int exp : 8; // middle bits unsigned int sign : 1; // highest bit }float_bit; }float_value;
#include <stdio.h> #define PRINT_D(intValue) printf(#intValue" is %d\n", (intValue)); #define PRINT_F(floatValue) printf(#floatValue" is %f\n", (floatValue)); // IEEE 754 single floating number's format(intel little-endian mode) typedef union { // float value float f; // intel bits mode that stands for float value struct { unsigned int dot : 23; // low bits unsigned int exp : 8; // middle bits unsigned int sign : 1; // highest bit }float_bit; }float_value; int main(int argc, char **argv) { float_value fv; fv.float_bit.sign = 1; fv.float_bit.exp = 128; fv.float_bit.dot = 0; PRINT_F(fv.f); return 0; }
fv.f is -2.000000
作者:陈曦
日期:2012-8-17 12:53:12
环境:[Mac 10.7.1 Lion Intel i3 支持64位指令 gcc4.2.1 xcode4.2]
转载请注明出处