Q1: 為什麼下面的輸出不按照代碼的順序顯示?
[cpp]
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
while(1)
{
fprintf(stdout, "stdout.");
fprintf(stderr, "stderr.");
sleep(1);
}
return 0;
}
#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: 如果把代碼改為如下,輸出就按照代碼順序顯示了:
[cpp]
#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;
}
#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?
[cpp]
#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;
}
#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;
}
上面的代碼保存的編碼為UTF-8.
A: 在我的另一篇帖子中已經有詳細解釋: http://blog.csdn.net/cxsjabcabc/article/details/7528227
如果把代碼文件的編碼改為其它,答案就出來了: printf返回的是實際輸出的字節數。
Q3: 下面代碼的輸出為什麼改變了b的數值?
[cpp]
#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;
}
#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;
}
A: 原因在於c標准規定:switch語句體的復合語句中,位於第一個case或者default標號前面的語句永遠不會被執行到。所以,b相當於沒有初始化。
另外,我們查看一下匯編,能夠得到結論,其實case標號和default標號相當於一個地址,編譯器內部會使用jmp, je, jne等語句直接跳到對應位置來執行,而不會分析它前面的初始化過程。
[cpp]
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
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
注意main+36和main + 38位置的匯編,兩條跳轉語句會直接找到標號,忽略了前面的代碼。
如果把源代碼改為如下,依然不能輸出3:
[cpp]
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;
}
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沒有自增?
[cpp]
#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;
}
#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;
}
A: 原因在於sizeof在編譯期間自動計算出來它的數值,對於i++, 編譯器僅僅看到了它的類型,卻沒有計算它的數值。看看匯編:
[cpp]
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
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"]這樣的表達式是什麼意思?
[cpp]
#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;
}
#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;
}
A: 2["hello"]等同於"hello"[2], 又等同於 char *str = "hello"; str[2] . 但是,為什麼?
因為編譯器對於數組訪問根本就采用指針加減的計算方法,即2["hello"] == *(2 + "hello"), "hello"是個字符指針, *(2 + "hello") == *("hello" + 2), 所以結論也就出來了。
Q6: 為什麼一個整形數據用%f格式輸出,結果改變了?
[cpp]
#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;
}
#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標准寫的很清楚,如果出現類似的問題,可以使用如下結構來簡單地驗證:
[cpp]
// 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;
// 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;
一個簡單的測試代碼:
[cpp]
#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;
}
#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;
}
輸出:
[plain]
fv.f is -2.000000