程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> c語言,有時莫名,有時奇妙

c語言,有時莫名,有時奇妙

編輯:關於C

 

 

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 


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved