1. 程式人生 > 其它 >資料型別和輸入輸出

資料型別和輸入輸出

技術標籤:程式語言pythonjavaprintfsync

1.存放資料的小箱子——變數

在《二進位制思想以及資料的儲存》一節中講到:
  1. 計算機要處理的資料(諸如數字、文字、符號、圖形、音訊、視訊等)是以二進位制的形式存放在記憶體中的;
  2. 我們將8個位元(Bit)稱為一個位元組(Byte),並將位元組作為最小的可操作單元。
程式設計中我們會經常處理各種資料,與記憶體打交道。我們不妨先從最簡單的整數說起,看看它是如何放到記憶體中去的。 現實生活中我們會找一個小箱子來存放物品,一來顯得不那麼凌亂,二來方便以後找到。計算機也是這個道理,我們需要先在記憶體中找一塊區域,規定用它來存放整數,並起一個好記的名字,方便以後查詢。這塊區域就是“小箱子”,我們可以把整數放進去了。 C語言中這樣在記憶體中找一塊區域:

     
  1. int
    a;
int 又是一個新單詞,它是 Integer 的簡寫,意思是整數。a 是我們給這塊區域起的名字;當然也可以叫其他名字,例如 abc、mn123 等。 這個語句的意思是: 在記憶體中找一塊區域,命名為 a,用它來存放整數。 注意 int 和 a 之間是有空格的,它們是兩個詞。也注意最後的分號,int a表達了完整的意思,是一個語句,要用分號來結束。 不過int a;僅僅是在記憶體中找了一塊可以儲存整數的區域,那麼如何將 123、100、999 這樣的數字放進去呢? C語言中這樣向記憶體中放整數:

     
  1. a=123;
= 是一個新符號,它在數學中叫“等於號”,例如 1+2=3,但在C語言中,這個過程叫做 賦值(Assign)
。賦值是指把資料放到記憶體的過程。 把上面的兩個語句連起來:

     
  1. int a;
  2. a=123;
就把 123 放到了一塊叫做 a 的記憶體區域。你也可以寫成一個語句:

     
  1. int a=123;
a 中的整數不是一成不變的,只要我們需要,隨時可以更改。更改的方式就是再次賦值,例如:

     
  1. int a=123;
  2. a=1000;
  3. a=9999;
第二次賦值,會把第一次的資料覆蓋(擦除)掉,也就是說,a 中最後的值是9999,123、1000 已經不存在了,再也找不回來了。 因為 a 的值可以改變,所以我們給它起了一個形象的名字,叫做 變數(Variable)。
int a;創造了一個變數 a,我們把這個過程叫做變數定義。a=123;把 123 交給了變數 a,我們把這個過程叫做給變數賦值;又因為是第一次賦值,也稱變數的初始化,或者2.賦初值。 你可以先定義變數,再初始化,例如:

     
  1. int abc;
  2. abc=999;
也可以在定義的同時進行初始化,例如:

     
  1. int abc=999;
這兩種方式是等價的。

2.在螢幕上顯示整數——輸出

我們定義了一個變數 abc 並給它賦值,例如:

      
  1. int abc=100;
現在我們希望執行一下,看看效果,怎麼才能在螢幕上顯示呢? puts 是 output string 的縮寫,只能用來輸出字串,不能輸出整數,我們需要用另外一種方法,那就是——printf。 printf 比 puts 更加強大,不僅可以輸出字串,還可以輸出整數、小數、單個字元等;輸出格式也可以自己定義,例如:
  1. 以二進位制、八進位制、十六進位制形式輸出;
  2. 要求輸出的數字佔 n 個字元的位置;
  3. 控制小數的位數。
printf 是 print format 的縮寫,意思是“格式化列印”。這裡所謂的“列印”就是在螢幕上顯示內容,與“輸出”的含義相同,所以我們一般稱 printf 是用來 格式化輸出的。 先來看一個簡單的例子:

      
  1. printf("C語言中文網");
這個語句可以在螢幕上顯示“C語言中文網”,與puts("C語言中文網");的效果完全相同。 輸出變數 abc 的值:

      
  1. int abc=999;
  2. printf("%d", abc);
這裡就比較有趣了。 先來看%d,d 是 decimal 的縮寫,意思是十進位制數,%d 表示以十進位制的形式輸出。輸出什麼呢?輸出 abc 的值。%d 與 abc 是對應的,也就是說,會用 abc 的值來替換 %d。 再來看個複雜點的:

      
  1. int abc=999;
  2. printf("The value of abc is %d !", abc);
會在螢幕上顯示: The value of abc is 999 ! 你看, 字串 "The value of abc is %d !" 中的 %d 被替換成了 abc 的值,其他字元沒有改變。這說明 %d 比較特殊,不會原樣輸出,會被替換成對應的變數的值。 再來看:

      
  1. int a=100;
  2. int b=200;
  3. int c=300;
  4. printf("a=%d, b=%d, c=%d", a, b, c);
會在螢幕上顯示: a=100, b=200, c=300 再次證明了 %d 與後面的變數是一一對應的,第一個 %d 對應第一個變數,第二個 %d 對應第二個變數…… 我們把程式碼補充完整,體驗一下:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a=100;
  6. int b=200;
  7. int c=300;
  8. printf("a=%d, b=%d, c=%d\n", a, b, c);
  9. system("pause");
  10. return 0;
  11. }
輸出結果: a=100, b=200, c=300 請按任意鍵繼續. . . 我們也可以不用變數,直接將資料輸出:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. printf("The output: %d, %s\n", 1234, "Think you");
  6. system("pause");
  7. return 0;
  8. }
輸出結果: The output: 1234, Think you s 是 string 的簡寫,%s 表示輸出字串。

3.更多型別的資料——小數和字元

int 可以用來表示整數,但是對小數卻無能為力。請看下面的程式碼:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a=1234.888;
  6. printf("a=%d\n", a);
  7. system("pause");
  8. return 0;
  9. }
輸出結果: a=1234 請按任意鍵繼續. . . 看,小數點後面的數字被丟棄了。

①.小數

在C語言中,我們使用 float 來表示小數,例如:

      
  1. float a=123.888;
  2. float b=0.302;
  3. float c=98.0;
float 是“浮點數”的意思,我們暫時可以認為浮點數就是小數,不必深究,不影響我們的學習。 輸出浮點數用%f,請看下面的例子:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. float a=123.888;
  6. float b=0.302;
  7. float c=98.0;
  8. float d=1.23002398;
  9. float e=123; // 整數
  10. printf("a=%f \nb=%f \nc=%f \nd=%f \ne=%f\n", a, b, c, d, e);
  11. system("pause");
  12. return 0;
  13. }
輸出結果: a=123.888000 b=0.302000 c=98.000000 d=1.230024 e=123.000000 %f 預設保留六位小數,不足六位以 0 補齊,超過六位按四捨五入截斷。也可以將整數賦值給 float 型別變數。

②.字元

我們前面提到過字串,它是多個字元的集合,例如 "abc123";當然也可以只包含一個字元,例如 "a"、"1"。 但是為了使用方便,我們可以用 char 來專門表示一個字元,例如:

      
  1. char a='1';
  2. char b='$';
  3. char c='X';
  4. char d=' '; // 空格也是一個字元
char 型別的字元用單引號 ' ' 來包圍,而字串用雙引號" "包圍,大家不要混淆,否則編譯會出錯。 資料型別 整數、浮點數、字元都是C語言中會使用到的資料,它們有一個專業的稱呼,叫做資料型別(Data Type)。例如:

      
  1. int a=1;
  2. float b=10.09;
  3. char c='@';
我們就可以說 a、b、c 的資料型別不同,a 是整型變數,b 是浮點型變數,c 是字元型變數。 整型、浮點型、字元型是C語言中的基本資料型別,必須要掌握,後續我們還會介紹更多資料型別,甚至可以自己定義一種新的型別。 注意:字串比較特殊,C語言中沒有這個資料型別,不能使用string a="abc123";這種方式來定義字串,後面我們會講解為什麼。

③.字元與整數

先看下面一段程式碼:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. char a=69;
  6. char b=0x46; // 十六進位制數以 0x 或 0X開頭
  7. char c='G';
  8. int d=72;
  9. printf("a1=%c, a2=%d, b=%c, c=%d, d=%c\n", a, a, b, c, d);
  10. system("pause");
  11. return 0;
  12. }
輸出結果: a1=E, a2=69, b=F, c=71, d=H 程式碼第6行中, //及其後面的內容為註釋(Comments)。註釋用來對程式進行說明,編譯器會忽略它,不去執行。 在ASCII碼錶中,E、F、G、H 的值分別是 69、0x46 (十進位制為 70)、71、72。給 char 變數一個整數,然後以 %c 輸出,它會根據 ASCII 碼錶轉換成對應的字元,如果以 %d 輸出,那麼還是整數。同樣,一個 char 變數以 %d 輸出,會轉換成對應的 ASCII 碼值。反過來,int 變數以 %c 輸出,會轉換成對應的字元。 可以看出,ASCII 碼將字元和整數聯絡起來了,你可以給 char 變數一個整數,也可以給 int 變數一個字元,它們可以相互轉換。

4.小箱子有多大——取值範圍

2015年05月20日,在納斯達克上市的蘋果公司市值達到了 7628.40 億美元,是有史以來市值最高的公司,並且最有希望成為世界上第一個市值破萬億的公司。喬布斯辭世後,很多人質疑庫克能不能再創輝煌,資料不會說謊,蘋果公司市值已經翻倍,喬布斯沒有選錯接班人,iPhone 6 也順應了大屏手機的趨勢。 那麼,我們不妨將蘋果公司的市值輸出一下,看看是多長的一串數字:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int value=7628400000000;
  6. // AAPL 是蘋果公司的股票程式碼
  7. printf("The market value of AAPL is %d !\n", value);
  8. system("pause");
  9. return 0;
  10. }
輸出結果: The market value of AAPL is 538082304 ! 讓人驚訝的是,市值瞬間蒸發,只剩下 5.38 億美元,與達內的市值相當,剛剛夠上市的體量。 這是因為,一般情況下 int 型別在記憶體中佔用 4 個位元組的空間,也就是32位, 理論上所能表示的最大數是 2^32 - 1 = 0xFFFFFFFF = 4,294,967,296 ≈ 42.95 億,7628.40 億顯然超出了它的範圍,會發生溢位(Overflow)。就像水一樣,多出的會流到木桶外面,如果數字超過32位,比如35位,那麼就會有3位丟失,當然不能表示原來的值。 我們來驗證一下 int 的最大值:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int value=0xffffffff;
  6. printf("The maximum value of \"int\" is %d .\n", value);
  7. system("pause");
  8. return 0;
  9. }
執行結果: The maximum value of "int" is -1 . 這和我們期望的結果不一樣:我們期望輸出 4294967296,但實際輸出的卻是八竿子打不著的 -1。 這是因為,整數有正負之分,我們需要在32位中選擇一位用來標明到底是正數還是負數,這一位就是最高位。也就是說,0~30位表示數值,31 位表示正負號。如下圖所示 注意: 在程式語言中,計數往往是從0開始,例如字串 "abc123",我們稱第 0 個字元是 a,第 1 個字元是 b,第 5 個字元是 3。這和我們平時從 1 開始計數的習慣不一樣,大家要慢慢適應,培養程式設計思維。 如果我們希望能表示更大些的數值,也可以不使用符號位,而用關鍵字 unsigned來宣告變數,例如:

      
  1. // 下面的兩種寫法都是正確的,使用 unsigned 時可以不寫 int
  2. unsigned int a=100;
  3. unsigned a=999;
這樣,就只能表示正數了。unsigned 型別的整數需要用%u來輸出。 我們來驗證一下上面的說法:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a=1234;
  6. unsigned a1=1234;
  7. int b=0x7fffffff;
  8. int c=0x80000000; // 0x80000000 = 0x7fffffff + 0x1
  9. int d=0xffffffff;
  10. unsigned e=0xffffffff;
  11. printf("a=%d, a(u)=%u\n", a, a);
  12. printf("a1=%d, a1(u)=%u\n", a1, a1);
  13. printf("b=%d, b(u)=%u\n", b, b);
  14. printf("c=%d, c(u)=%u\n", c, c);
  15. printf("d=%d, d(u)=%u\n", d, d);
  16. printf("e=%d, e(u)=%u\n", e, e);
  17. system("pause");
  18. return 0;
  19. }
輸出結果: a=1234, a(u)=1234 a1=1234, a1(u)=1234 b=2147483647, b(u)=2147483647 c=-2147483648, c(u)=2147483648 d=-1, d(u)=4294967295 e=-1, e(u)=4294967295 我們發現, 無論變數宣告為 int 還是 unsigned,只有當以 %u 格式輸出時,才會作為無符號數處理;如果宣告為 unsigned,卻以 %d輸出,那麼也是有符號數。 為了更加靈活的表示資料,合理利用記憶體資源,C語言還使用另外兩個關鍵字來表示整數,那就是 short 和 long。short 表示短整型,long 表示長整型,這裡的“長短”是針對 int 來說的。C語言規定,short 的長度不能大於 int,long 的長度不能小於 int。 unsigned表示有無符號位,short、int、long表示所佔記憶體的大小,不要混淆,請看下面的例子:

      
  1. long int a=1234;
  2. long b=299;
  3. unsigned long int c=999;
  4. unsigned long d=98720;
  5. short int m=222;
  6. short n=2094;
  7. unsigned short int p=3099;
  8. unsigned int q=68893;
值得一提的是:C語言是70年代的產物,那個時候以及後面的十幾年,是8086的天下,計算機的軟硬體都是16位的,按照C語言的規定,short 一般佔2個位元組,int 也佔2個位元組,long 佔4個位元組。 現在的計算機已經甩開8086幾條街,CPU、作業系統、編譯器都已經支援到32位或者64位,所以 short 一般佔2個位元組,int 佔 4 個位元組,long 也佔 4 個位元組。 你看,C語言為了相容硬體,追求效率,對資料型別的規定不是那麼嚴謹,short 不一定真的“短”,long 也不一定真的“長”,這增加了我們學習和開發的成本。 在Java中不存在這樣的情況,資料型別的長度是板上釘釘的,short 一定是 2 個位元組,int 是 4 個位元組,long 是 8 個位元組,多麼簡單。但這是以犧牲效率為代價的。在程式語言中,簡單和效率一直是相互矛盾的,幾個語句搞定的程式碼往往效率不高,追求高效的程式碼往往需要自己實現,可能要幾百行。

①.其他資料型別的長度

現在的計算機軟硬體配置都已經支援到32位或者64位,這裡以及後面提到的資料型別長度,都只考慮現代計算機,不再關心古董機器——16位機。 char 的長度始終都是1個位元組。 float 的長度為 4 個位元組,是單精度浮點數。為了更加精確,表示更大或者更小的浮點數,可以使用 double。double 佔用 8 個位元組,是雙精度浮點數。 最後,我們來總結一下現代計算機中各種資料型別的長度。
說 明字元型 短整型 整型 長整型 單精度浮點型雙精度浮點型
資料型別charshortintlongfloatdouble
所佔位元組124448
注意:unsigned 表示有無正負號,不會影響資料型別長度。 sizeof sizeof 用來計算資料所佔用的記憶體空間,以位元組計。如果你忘記了某個資料型別的長度,可以用 sizeof 求得,請看下面的程式碼:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int m=290;
  6. float n=23;
  7. printf("int = %d\n", sizeof(m));
  8. printf("float = %d\n", sizeof(n));
  9. printf("double = %d\n", sizeof(double));
  10. printf("A = %d\n", sizeof('A'));
  11. printf("23 = %d\n", sizeof(23));
  12. printf("14.67 = %d\n", sizeof(14.67));
  13. system("pause");
  14. return 0;
  15. }
輸出結果: int = 4 float = 4 double = 8 A = 1 23 = 4 14.67 = 8 sizeof 的運算元既可以是變數、資料型別,還可以是某個具體的資料。細心的讀者會發現,sizeof(14.67) 的結果是8,不錯,因為小數預設的型別就是double。

5.加減乘除運算

C語言也可以進行加減乘除運算,但是運算子號與數學中的略有不同,見下表。
加法減法 乘法除法 求餘數
數學+-×÷
C語言+-*/%
加號、減號與數學中的一樣,乘號、除號不同,另外C語言還多了一個求餘數的運算子。 我們先來看一段程式碼:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a=12;
  6. int b=100;
  7. float c=8.9;
  8. int m=a+b;
  9. float n=b*c;
  10. double p=a/c;
  11. int q=b%a;
  12. printf("m=%d, n=%f, p=%lf, q=%d\n", m, n, p, q);
  13. system("pause");
  14. return 0;
  15. }
輸出結果: m=112, n=889.999962, p=1.348315, q=4 b*c 的結果很明顯是890,但是卻輸出一個近似數,這裡涉及到浮點數的運算原理,並且實際開發中較少使用浮點數運算,所以這裡我們不再深究。 你也可以讓數字直接參與運算:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a=12;
  6. int b=100;
  7. float c=8.9;
  8. int m=a-b; // 變數參與運算
  9. int n=a+239; // 有變數也有數字
  10. double p=12.7*34.3; // 數字直接參與運算
  11. printf("m=%d, n=%f, p=%lf\n", m, n, p);
  12. printf("m*2=%d, 6/3=%d, m*n=%ld\n", m*2, 6/3, m*n);
  13. system("pause");
  14. return 0;
  15. }
輸出結果: m=-88, n=-0.000000, p=0.000000 m*2=-176, 6/3=2, m*n=-22088 注意: 除數不能是 0,所以諸如int a=3/0;這樣的語句是錯誤的。 再來看一個例子:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a=12;
  6. int b=10;
  7. printf("a=%d\n", a);
  8. a=a+8;
  9. printf("a=%d\n", a);
  10. a=a*b;
  11. printf("a=%d\n", a);
  12. system("pause");
  13. return 0;
  14. }
輸出結果: a=12 a=20 a=200 第一次輸出 a 原來的值;a=a+8;相當於用a+8的值替換原來 a 的值,所以第二次輸出 20;第三次用a*b的值替換第二次的值,所以是 200。 注意: a=a+8;可以寫成a+=8;,a=a*b;可以寫成a*=b。這僅是一種簡寫,不會影響效率,其他運算子也可以這樣寫。

①.加一和減一

一個整數自身加一可以這樣寫:

      
  1. a+=1;
它等價於 a=a+1;。 但是在C語言中還有一種更簡單的寫法,就是 a++; 或者++a; 。這種寫法叫做自加或自增;意思很明確,就是自身加一。 相應的,也有a--和--a,叫做自減,表示自身減一。 ++和--分別稱為自增和自減運算子。 那麼,++ 在前面和後面有區別嗎? 請看下面的例子:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a=12;
  6. int a1=a++;
  7. int b=10;
  8. int b1=++b;
  9. printf("a=%d, a1=%d\n", a, a1);
  10. printf("b=%d, b1=%d\n", b, b1);
  11. system("pause");
  12. return 0;
  13. }
輸出結果: a=13, a1=12 b=11, b1=11 我們來分析一下: 1) 執行a1=a++;,a 的值並不會立馬加 1,而是先把 a 原來的值交給 a1,然後才能加 1。a 原來的值是 12,所以 a1 的值是 12;而 a 因為加 1,最終的值是 13。 2) 執行b1=++b;,b 的值會立馬加1,變成11,然後才將 b 的值交給 b1。因為此時 b 的值已經是11,所以 b1 的值自然也就是 11 了。 可以看出:a1=a++;會先進行賦值操作,再進行自增操作;而b1=++b;會先進行自增操作,再進行賦值操作。 所以,我們把諸如a++這樣的操作叫做後自增,也就是先進行其他操作,再進行自增操作;而把++a叫做前自增,會先進行自增操作,再進行其他操作。 相應的,a--叫做後自減,--a叫做前自減,意思與++相同。 自增自減非常方便,後續程式設計中會經常用到,大家要注意區分。 為了強化記憶,我們再來看一個自增自減的綜合示例:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a=12;
  6. int b=1;
  7. int c=a-(b--); // ①
  8. int d=(++a)-(--b); // ②
  9. printf("c=%d, d=%d\n", c, d);
  10. system("pause");
  11. return 0;
  12. }
輸出結果: c=11, d=14 我們來分析一下: 1) 執行語句①時,會先進行a-b運算,結果是11,然後 b 再自減,就變成了 0,最後再將a-b的結果(也就是11)交給 c,所以 c 的值是 11。 2) 執行語句②之前,b 的值已經變成 0。對於d=(++a)-(--b),a 會先自增,變成 13,然後 b 再自減,變成 -1,最後再進行13-(-1),結果是14,交給 d,所以 d 最終是 14。

6.更加優美的C語言輸出

雖然我們已經熟悉了 printf,但是還沒有把它發揮到極致,printf 可以有更加“炫酷”的輸出。 假如現在老師要求我們用C語言輸出一個 4×4 的整數矩陣,為了增強閱讀性,數字要對齊,怎麼辦呢?我們顯然可以這樣來做:

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a1=20, a2=345, a3=700, a4=22;
  6. int b1=56720, b2=9999, b3=20098, b4=2;
  7. int c1=233, c2=205, c3=1, c4=6666;
  8. int d1=34, d2=0, d3=23, d4=23006783;
  9. printf("%d %d %d %d\n", a1, a2, a3, a4);
  10. printf("%d %d %d %d\n", b1, b2, b3, b4);
  11. printf("%d %d %d %d\n", c1, c2, c3, c4);
  12. printf("%d %d %d %d\n", d1, d2, d3, d4);
  13. system("pause");
  14. return 0;
  15. }
執行結果: 20 345 700 22 56720 9999 20098 2 233 205 1 6666 34 0 23 23006783 矩陣一般在大學的《高等數學》中會講到,m×n 的數字矩陣可以理解為把 m×n 個數字擺放成 m 行 n 列的樣子。 看,這是多麼地自虐,要敲那麼多空格,還要嚴格控制空格數,否則輸出就會錯位。 類似的需求隨處可見,整齊的格式會更加美觀,讓人覺得生動有趣。我們大可不必像上面一樣,printf 可以更好的控制輸出格式。

      
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a1=20, a2=345, a3=700, a4=22;
  6. int b1=56720, b2=9999, b3=20098, b4=2;
  7. int c1=233, c2=205, c3=1, c4=6666;
  8. int d1=34, d2=0, d3=23, d4=23006783;
  9. printf("%-9d %-9d %-9d %-9d\n", a1, a2, a3, a4);
  10. printf("%-9d %-9d %-9d %-9d\n", b1, b2, b3, b4);
  11. printf("%-9d %-9d %-9d %-9d\n", c1, c2, c3, c4);
  12. printf("%-9d %-9d %-9d %-9d\n", d1, d2, d3, d4);
  13. system("pause");
  14. return 0;
  15. }
輸出結果: 20 345 700 22 56720 9999 20098 2 233 205 1 6666 34 0 23 23006783 這樣寫起來更加方便,即使改變某個數字,也無需修改 printf 語句。 %-9d中,d表示以十進位制輸出,9表示最少佔9個字元的寬度,寬度不足以空格補齊,-表示左對齊。綜合起來,%-9d表示以十進位制輸出,左對齊,寬度最小為9個字元。大家可以親自試試%9d的輸出效果。 printf 格式控制字串的完整形式如下: %[flags][width][.precision]type 1) type 也就是以什麼型別輸出,比如 %d、%f、%c,type 就分別對應 d、f、c;%-9d中 type 對應 d。type 必須有。 2) width 表示最小輸出寬度,也就是佔幾個字元的位置;%-9d中 width 對應 9。 注意:[xxx] 並不是C語言規定的格式,只是一種習慣寫法,表示此處的內容可有可無,後面會經常見到這樣的寫法。 對於整數和小數,預設右對齊,不足的寬度以空格補齊,例如: printf("%10d%12f", 234, 9.8); 輸出結果為: 234 9.800000 234 前面共有7個空格,9.8 前面有4個空格。 3) . precision 表示輸出精度。 對於 %d,.precision 表示的其實是最小輸出寬度,與 width 不同的是,不足的寬度以 0 補齊,例如: printf("%.10d\n", 4309); 輸出結果為: 0000004309 對於 %f,.precision 表示小數的位數,不足以 0 補齊,也就是精度,例如: printf("%.10f %.3f\n", 23.988, 2.9328745); 輸出結果為: 23.9880000000 2.933 4) flags 是標誌字元,%-9d中 flags 對應- 幾種常見的標誌字元
標誌字元含 義
-左對齊
+輸出符號(正號或負號)
空格輸出值為正時冠以空格,為負時冠以負號
# 對c、s、d、u類無影響; 對o類,在輸出時加字首o; 對x類,在輸出時加字首0x; 對e、g、f 類當結果有小數時才給出小數點。

7.從鍵盤輸入資料

puts 可以輸出字串,printf 可以輸出多種型別的資料,另外還有 putchar,可以輸出單個字元,例如:

     
  1. putchar('a');
  2. putchar(7);
  3. putchar('\x46');
輸出結果:

     
  1. aF // 同時會聽到喇叭發出“嘟”的聲音
程式是人機互動的媒介,有輸出必然也有輸入。在C語言中,使用 scanf 從鍵盤獲取使用者輸入的資料。 scanf 是 scan format 的縮寫,意思是格式化掃描,這裡可以理解為從鍵盤獲得使用者輸入。我們先來看一個例子:

     
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a, b;
  6. scanf("%d %d", &a, &b);
  7. printf("a+b=%d\n", (a+b));
  8. system("pause");
  9. return 0;
  10. }
執行結果: 34 29 ↙ a+b=63 從鍵盤輸入 34,空格,再輸入 29,然後回車(↙表示回車),就會看到兩個數相加的結果。 scanf 和 printf 非常相似:

     
  1. scanf("%d %d", &a, &b); // 獲取使用者輸入的兩個整數,分別賦值給變數 a 和 b
  2. printf("%d %d", a, b); // 將變數 a 和 b 的是在顯示器上輸出。
它們都有格式控制字串,都有變數列表。不同的是, scanf 的變數前要帶一個&符號;&稱為取地址符,也就是獲取變數在記憶體中的地址。 在《二進位制思想以及資料的儲存》一節中講到,資料是以二進位制的形式儲存在記憶體中的,位元組(Byte)是最小的可操作單位。為了便於管理,我們給每個位元組分配了一個編號,使用該位元組時,只要知道編號就可以,就像每個學生都有學號,老師會隨機抽取學號來讓學生回答問題。位元組的編號是有順序的,從 0 開始,接下來是 1、2、3…… 下圖是 4G 記憶體中每個位元組的編號(以十六進位制表示): 這個編號,就叫做 地址(Address)。int a; 會在記憶體中分配四個位元組的空間,我們將第一個位元組的地址稱為變數 a 的地址,也就是 &a 的值。對於前面講到的整數、浮點數、字元,都要使用 & 獲取它們的地址,scanf 會根據地址把讀取到的資料寫入記憶體。 我們不妨將它們的地址輸出看一下:

     
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a='F';
  6. int b=12;
  7. int c=452;
  8. printf("&a=%#x, &b=%#x, &c=%#x\n", &a, &b, &c);
  9. system("pause");
  10. return 0;
  11. }
輸出結果: &a=0x18ff48, &b=0x18ff44, &c=0x18ff40 圖:a、b、c 的記憶體地址 注意: 你看到的地址是虛擬地址,並不等於它在實體記憶體中的地址。虛擬記憶體是現代作業系統因記憶體管理的需要才提出的概念,dos 下沒有這個概念,使用者看到的都是真實的地址。CPU 操作的是實體記憶體地址,所以虛擬地址必須經過轉換才能交給 CPU,這是 OS 的工作,對使用者是透明的。 再來看一個 scanf 的例子:

     
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. int a, b, c, age;
  6. float scores;
  7. scanf("a=%d,b=%d,c=%d", &a, &b, &c);
  8. printf("a+b+c=%d\n\n", (a+b+c));
  9. fflush(stdin); // 清空緩衝區
  10. scanf("Tom's age is %d, his scores is %f.", &age, &scores);
  11. printf("age=%d, scores=%f.\n", age, scores);
  12. system("pause");
  13. return 0;
  14. }
執行結果: a=230,b=400,c=5009↙ a+b+c=5639 Tom's age is 16, his scores is 94.5.↙ age=16, scores=94.500000. 先輸入a=230,b=400,c=5009,回車,就可以得到 a+b+c 的值;再輸入Tom's age is 16, his scores is 94.5.,回車,就可以得到年齡和成績。 fflush(stdin);用來清空輸入緩衝區,這是什麼意思呢? 在記憶體中,有一塊區域(比如512位元組)專門用來儲存使用者輸入的資料,遇到 scanf 時,程式會首先檢查該區域是否有資料:
  1. 如果沒有,就等待使用者輸入,使用者從鍵盤輸入的每個字元都會暫時儲存到這裡,直到按下回車鍵,輸入結束,scanf 再從這裡讀取資料,賦值給變數。
  2. 如果有資料,哪怕是一個字元,scanf 也會直接讀取,不會等待使用者輸入。
這塊記憶體區域,就叫做緩衝區(Buffer),或者快取(Cache);又因為它是用來暫存使用者輸入的資料的,所以又叫輸入緩衝區。 緩衝區與普通的記憶體沒有什麼兩樣,都是實體記憶體上的若干位元組,只是作用不同而已。 上面的程式碼如果沒有fflush(stdin);,執行時就會大有不同: a=23,b=900,c=399↙ a+b+c=1322 age=4239360, scores=0.000000. 第一次輸入後,程式並沒有等待我們第二次輸入,age 和 scores 都是無效值。這是因為,第一次輸入的資料為a=23,b=900,c=399↙(包含最後的回車),回車後 scanf 讀取到的資料是a=23,b=900,c=399,還有一個回車符留在緩衝區,遇到第二個 scanf 時,因為緩衝區中有資料,所以會直接讀取,不給我們輸入的機會。 所以要用fflush(stdin);來清空緩衝區,這樣遇到第二個 scanf 時因為緩衝區中沒有資料,就會等待使用者輸入。 這裡重點是告訴大家 scanf 是帶有緩衝區的,fflush(stdin);的用法不再展開討論,請大家先記住,後續我們再講解。

①.getchar

scanf 用於接收使用者輸入的各種資料,如果僅僅是輸入單個字元,也可以使用 getchar。請看下面的例子:

     
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main()
  4. {
  5. char a, b, c;
  6. a=getchar();
  7. printf("a='%c'\n", a);
  8. b=getchar();
  9. printf("b='%c'\n", b);
  10. c=getchar();
  11. printf("c='%c'\n", c);
  12. system("pause");
  13. return 0;
  14. }
執行結果: XYZ↙ a='X' b='Y' c='Z' 你也可以這樣輸入: X↙ a='X' b=' ' Y↙ c='Y' 第二次之所以是這樣的結果,是因為 getchar 也帶有緩衝區。輸入 X 並回車,第一個 getchar 讀取到 'X',接著第二個 getchar 讀取到回車換行符,到第三個 getchar,緩衝區中已經沒有資料,所以會等待使用者輸入。

8.運算子的優先順序和結合性

先來看一個例子:

      
  1. #include <stdio.h>
  2. int main(){
  3. int a=10,b=1,c=2;
  4. a=b=c;
  5. printf( "12+3*5=%d\n", 12+3*5);
  6. printf( "a=%d, c=%d\n", a, c);
  7. return 0;
  8. }
執行結果: 12+3*5=27 a=2, c=2 1) 對於表示式12+3*5,很明顯先進行乘法運算,計算3*5,結果為15,再進行加法運算,計算12+15,結果為27。也就是說,乘法的優先順序比加法高,要先計算,這與數學中的規則是一樣的。 所謂優先順序,就是當有多個運算子在同一個表示式中出現時,先執行哪個運算子。如果不想按照預設的規則執行,可以加( ),例如(12+3)*5的結果為 75,(2+5)*(10-4)的結果為 42。大部分情況下,它們的規則和數學中是相同的。 2) 對於語句賦值語句a=b=c;,先執行b=c,再執行a=b,而不是反過來,這說明賦值操作符=具有右結合性。 所謂結合性,就是當一個運算子多次出現時,先執行哪個運算子。先執行右邊的叫右結合性,先執行左邊的叫左結合性。 表示式(Expression)和語句(Statement)的概念在C語言中並沒有明確的定義: 表示式可以看做一個計算的公式,往往由資料、變數、運算子等組成,例如3*4+5、a=c=d等,它的結果必定是一個值; 語句的範圍更加廣泛,不一定是計算,不一定有值,可以是某個操作、某個函式、選擇結構、迴圈等。 值得注意的是:以分號;結束的往往稱為語句,而不是表示式,例如3*4+5;、a=c=d;等。 3) 像 +、-、*、/ 這樣的運算子,它的兩邊都有資料,例如 3+4、a*3 等,有兩個運算元,我們稱這樣的運算子為雙目運算子。後面還會講解單目運算子和三目運算子。

9.幾個重要的 C 語言概念

①.識別符號

定義變數時,我們使用了諸如“a”“abc”“mn123”這樣的名字,它們都是程式設計師自己起的,一般能夠表達出變數的作用,這叫做 識別符號(Identifier)。 識別符號就是程式設計師自己起的名字,除了變數名,後面還會講到函式名、標號等。不過,名字也不能隨便起,C語言規定,識別符號只能由字母(A~Z, a~z)、數字(0~9)和下劃線(_)組成,並且第一個字元必須是字母或下劃線。 以下識別符號是合法的: a, x, x3, BOOK_1, sum5 以下識別符號是非法的: 3s 不能以數字開頭 s*T 出現非法字元* -3x 不能以減號(-)開頭 bowy-1 出現非法字元減號(-) 在使用識別符號時還必須注意以下幾點:
  1. C語言雖然不限制識別符號的長度,但是它受到不同編譯器的限制,同時也受到具體機器的限制。例如在某個編譯器中規定識別符號前128位有效,當兩個識別符號前128位相同時,則被認為是同一個識別符號。
  2. 在識別符號中,大小寫是有區別的,例如BOOK和book 是兩個不同的識別符號。
  3. 識別符號雖然可由程式設計師隨意定義,但識別符號是用於標識某個量的符號,因此,命名應儘量有相應的意義,以便於閱讀理解,作到“顧名思義”。

②.關鍵字

關鍵字(Keywords)是由C語言規定的具有特定意義的字串,通常也稱為保留字,例如 int、char、long、float、unsigned 等。我們定義的識別符號不能與關鍵字相同,否則會出現錯誤。 你也可以將關鍵字理解為具有特殊含義的識別符號,它們已經被系統使用,我們不能再使用了。 標準C語言中一共規定了32個關鍵字。

③.註釋

註釋(Comments)可以出現在程式碼中的任何位置,用來向用戶提示或解釋程度的意義。程式編譯時,會忽略註釋,不做任何處理,就好像它不存在一樣。 C語言支援單行註釋和多行註釋:
  1. 單行註釋以//開頭,直到本行末尾(不能換行);
  2. 多行註釋以/*開頭,以*/結尾,註釋內容可以有一行或多行。
一個使用註釋的例子:

      
  1. /*
  2. Powered by: c.biancheng.net
  3. Author: xiao p
  4. Date: 2015-6-26
  5. */
  6. #include <stdio.h>
  7. int main()
  8. {
  9. /* puts 會在末尾自動新增換行符 */
  10. puts("http://c.biancheng.net");
  11. printf("C語言中文網\n"); //printf要手動新增換行符
  12. return 0;
  13. }
執行結果: http://c.biancheng.net C語言中文網 在除錯程式的過程中可以將暫時不使用的語句註釋掉,使編譯器跳過不作處理,待除錯結束後再去掉註釋。

④.函式和標頭檔案

在C語言中,有的語句使用時不能帶括號,例如宣告型別的關鍵字 int、char、long 等,有的語句必須帶括號,例如 puts()、printf()、scanf() 等。帶括號的稱為 函式(Function)。 C語言釋出時,它的開發者已經為我們編寫了常用的程式碼(例如輸入輸出程式碼),我們可以拿來直接使用,不必再自己編寫。這些程式碼,以函式(Function)的形式提供給我們,我們直接呼叫函式就可以。 函式的一個明顯特徵就是使用時帶括號 ( ),必要的話,括號中還要包含資料或變數。我們將在《函式》一章中詳細介紹,請大家先記住這個概念。 C語言中的函式和數學中的函式有很大的不同,最好不要拿兩者對比。 C語言包含了幾百個函式,為了便於管理和使用,這些函式被分門別類地儲存到了不同的檔案,使用時要引入對應的檔案。這樣的檔案稱為 標頭檔案(Header File),字尾為.h。 引入標頭檔案使用 #include命令,並將標頭檔案放在 < >之間,例如 #include <stdio.h>、#include <stdlib.h>。 stdio 是 standard input and output 的縮寫,稱為“標準輸入輸出檔案”。 stdio.h中包含了很多常用函式,如 puts、printf、scanf 等。 需要注意的是: 標頭檔案必須在程度開頭引入。

⑤.main 函式

我們說過,編寫的程式碼要位於 main(){ }之中,main 也是一個函式,稱為 主函式。main 比較特殊,程式執行時,會從 main 函式開始,直到 main 函式結束,所以程式碼中只能有一個 main 函式,否則程式將不知道從哪裡開始執行,出現錯誤。 來自為知筆記(Wiz)