如何使用printf?前言printf可能是我們在學習C語言的過程中最早接觸的庫函數了其基本使用想必我們都已經非常清楚了但是下面的這些情況你是否已經清楚地知道了呢?,我來為大家科普一下關于如何使用printf?以下内容希望對你有幫助!
前言
printf可能是我們在學習C語言的過程中最早接觸的庫函數了。其基本使用想必我們都已經非常清楚了。但是下面的這些情況你是否已經清楚地知道了呢?
示例程序
我們來看一個示例程序,看看你能否對下面的結果輸出有非常清晰的認識。
#include <stdio.h> int main(void) { int a = 4; int b = 3; int c = a/b; float d = *(float*)(&c); long long e = 0xffffffffffffffff; printf("a/b:%f,a:%d\n",a/b,a,b); //打印0 printf("(float)a/b:%f\n",((float)a)/b); //打印1 printf("(double)a/b:%lf\n",((double)a)/b);//打印2 printf("d:%f\n",d); //打印3 printf("%.*f\n",20,(double)a/b); //打印4 printf("e:%d,a:%d\n",e,a); //打印5 printf("a:%d, a:%d,a :%d\n",a, a,a ); //打印6 return 0; }
編譯為32位程序:
gcc -m32 -o test test.c
在運行之前,你可以自己先猜想一下打印結果會是什麼。實際運行結果:
a/b:0.000000,a:3 //打印0的結果 (float)a/b:1.333333 //打印1的結果 (double)a/b:1.333333 //打印2的結果 d:0.000000 //打印3的結果 1.33333333333333325932 //打印4的結果 e:-1,a:-1 //打印5的結果 a:6, a:6,a :4 //打印6的結果
你的猜想是否都正确呢?如果猜想錯誤,那麼接下來的内容你就不應該錯過了。
你是否會有以下疑問:
在解答這些問題之前,我們需要先了解一些基本内容。
可變參數中的類型提升
printf是接受變長參數的函數,傳入printf中的參數個數可以不定。而我們在變長參數探究中說到:
調用者會對每個參數執行“默認實際參數提升",提升規則如下:
——float将提升到double
——char、short和相應的signed、unsigned類型将提升到int
也就是說printf實際上隻會接受到double,int,long int等類型的參數。而從來不會實際接受到float,char,short等類型參數。
我們可以通過一個示例程序來檢驗:
//bad code #include<stdio.h> int main(void) { char *p = NULL; printf("%d,%f,%c\n",p,p,p); return 0; }
編譯報錯如下:
printf.c: In function ‘main’: printf.c:5:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘char *’ [-Wformat=] printf("%d,%f,%c\n",p,p,p); ^ printf.c:5:12: warning: format ‘%f’ expects argument of type ‘double’, but argument 3 has type ‘char *’ [-Wformat=] printf.c:5:12: warning: format ‘%c’ expects argument of type ‘int’, but argument 4 has type ‘char *’ [-Wformat=]
我們可以從報錯信息中看到:
而編譯之所以有警告是因為,char *類型無法通過默認實際參數提升,将其提升為int或double。
參數入棧順序以及計算順序
在C語言中,參數入棧順序是确定的,從右往左。而參數的計算順序卻是沒有規定的。也就是說,編譯器可以實現從右往左計算,也可以實現從左往右計算。
浮點數的有效位
對于double類型,其有效位為15~~16位(參考:對浮點數的一些理解)。
可變域寬和精度
printf中,*的使用可實現可變域寬和精度,使用時隻需要用*替換域寬修飾符和精度修飾符即可。在這樣的情況下,printf會從參數列表中取用實際值作為域寬或者精度。示例程序如下:
#include<stdio.h> int main(void) { float a = 1.33333333; char *p = "hello"; printf("%.*f\n",6,a); printf("%*s\n",8,p); return 0; }
運行結果:
1.333333 hello
而這裡的6或者8完全可以是一個宏定義或者變量,從而做到了動态地格式控制。
格式控制符是如何處理參數的
printf有很多格式控制符,例如%d,它在處理輸入時,會從堆棧中取其對應大小,即4個字節作為對應的參數值。也就是說,當你傳入參數和格式控制符匹配或者在經過類型提升後和格式控制符匹配的時候,參數處理是沒有任何問題的。但是不匹配時,可能會出現未定義行為(有兩種情況例外,我們後面再說)。例如,%f期望一個double(8字節)類型,但是傳入的參數是int(4字節),那麼在處理這個int參數值,可能會多處理4個字節,并且也會造成處理數據錯誤。
真相大白
有了前面這些内容的鋪墊,我們再來解答開始的疑問:
至此,真相大白。
總結
雖然我們前面解釋了那些難以理解的現象,同時讀者可以參考變長參數探究和對浮點數的一些理解找到更多的信息。但是我們在實際編程中應該注意以下幾點:
問題思考
如果編譯為64位程序運行,結果還是一樣嗎?為什麼?
關注公衆号【編程珠玑】,第一時間獲取更多原創技術文章
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!