資料型別和輸入輸出
阿新 • • 發佈:2020-12-27
1.存放資料的小箱子——變數
在《二進位制思想以及資料的儲存》一節中講到:- 計算機要處理的資料(諸如數字、文字、符號、圖形、音訊、視訊等)是以二進位制的形式存放在記憶體中的;
- 我們將8個位元(Bit)稱為一個位元組(Byte),並將位元組作為最小的可操作單元。
int a;
int 又是一個新單詞,它是
Integer 的簡寫,意思是整數。a 是我們給這塊區域起的名字;當然也可以叫其他名字,例如 abc、mn123 等。
這個語句的意思是:
在記憶體中找一塊區域,命名為 a,用它來存放整數。
注意 int 和 a 之間是有空格的,它們是兩個詞。也注意最後的分號,int a表達了完整的意思,是一個語句,要用分號來結束。
不過int a;僅僅是在記憶體中找了一塊可以儲存整數的區域,那麼如何將 123、100、999 這樣的數字放進去呢?
C語言中這樣向記憶體中放整數:
a=123;
= 是一個新符號,它在數學中叫“等於號”,例如 1+2=3,但在C語言中,這個過程叫做
賦值(Assign)
int a;
a=123;
就把 123 放到了一塊叫做 a 的記憶體區域。你也可以寫成一個語句:
int a=123;
a 中的整數不是一成不變的,只要我們需要,隨時可以更改。更改的方式就是再次賦值,例如:
int a=123;
a=1000;
a=9999;
第二次賦值,會把第一次的資料覆蓋(擦除)掉,也就是說,a 中最後的值是9999,123、1000 已經不存在了,再也找不回來了。
因為 a 的值可以改變,所以我們給它起了一個形象的名字,叫做
變數(Variable)。
int abc;
abc=999;
也可以在定義的同時進行初始化,例如:
int abc=999;
這兩種方式是等價的。
2.在螢幕上顯示整數——輸出
我們定義了一個變數 abc 並給它賦值,例如:
int abc=100;
現在我們希望執行一下,看看效果,怎麼才能在螢幕上顯示呢?
puts 是 output string 的縮寫,只能用來輸出字串,不能輸出整數,我們需要用另外一種方法,那就是——printf。
printf 比 puts 更加強大,不僅可以輸出字串,還可以輸出整數、小數、單個字元等;輸出格式也可以自己定義,例如:
- 以二進位制、八進位制、十六進位制形式輸出;
- 要求輸出的數字佔 n 個字元的位置;
- 控制小數的位數。
printf("C語言中文網");
這個語句可以在螢幕上顯示“C語言中文網”,與puts("C語言中文網");的效果完全相同。
輸出變數 abc 的值:
int abc=999;
printf("%d", abc);
這裡就比較有趣了。
先來看%d,d 是 decimal 的縮寫,意思是十進位制數,%d 表示以十進位制的形式輸出。輸出什麼呢?輸出 abc 的值。%d 與 abc 是對應的,也就是說,會用 abc 的值來替換 %d。
再來看個複雜點的:
int abc=999;
printf("The value of abc is %d !", abc);
會在螢幕上顯示:
The value of abc is 999 !
你看,
字串 "The value of abc is %d !" 中的 %d 被替換成了 abc 的值,其他字元沒有改變。這說明 %d 比較特殊,不會原樣輸出,會被替換成對應的變數的值。
再來看:
int a=100;
int b=200;
int c=300;
printf("a=%d, b=%d, c=%d", a, b, c);
會在螢幕上顯示:
a=100, b=200, c=300
再次證明了 %d 與後面的變數是一一對應的,第一個 %d 對應第一個變數,第二個 %d 對應第二個變數……
我們把程式碼補充完整,體驗一下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=100;
int b=200;
int c=300;
printf("a=%d, b=%d, c=%d\n", a, b, c);
system("pause");
return 0;
}
輸出結果:
a=100, b=200, c=300
請按任意鍵繼續. . .
我們也可以不用變數,直接將資料輸出:
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("The output: %d, %s\n", 1234, "Think you");
system("pause");
return 0;
}
輸出結果:
The output: 1234, Think you
s 是 string 的簡寫,%s 表示輸出字串。
3.更多型別的資料——小數和字元
int 可以用來表示整數,但是對小數卻無能為力。請看下面的程式碼:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=1234.888;
printf("a=%d\n", a);
system("pause");
return 0;
}
輸出結果:
a=1234
請按任意鍵繼續. . .
看,小數點後面的數字被丟棄了。
①.小數
在C語言中,我們使用 float 來表示小數,例如:
float a=123.888;
float b=0.302;
float c=98.0;
float 是“浮點數”的意思,我們暫時可以認為浮點數就是小數,不必深究,不影響我們的學習。
輸出浮點數用%f,請看下面的例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
float a=123.888;
float b=0.302;
float c=98.0;
float d=1.23002398;
float e=123; // 整數
printf("a=%f \nb=%f \nc=%f \nd=%f \ne=%f\n", a, b, c, d, e);
system("pause");
return 0;
}
輸出結果:
a=123.888000
b=0.302000
c=98.000000
d=1.230024
e=123.000000
%f 預設保留六位小數,不足六位以 0 補齊,超過六位按四捨五入截斷。也可以將整數賦值給 float 型別變數。
②.字元
我們前面提到過字串,它是多個字元的集合,例如 "abc123";當然也可以只包含一個字元,例如 "a"、"1"。 但是為了使用方便,我們可以用 char 來專門表示一個字元,例如:
char a='1';
char b='$';
char c='X';
char d=' '; // 空格也是一個字元
char 型別的字元用單引號 ' ' 來包圍,而字串用雙引號" "包圍,大家不要混淆,否則編譯會出錯。
資料型別
整數、浮點數、字元都是C語言中會使用到的資料,它們有一個專業的稱呼,叫做資料型別(Data Type)。例如:
int a=1;
float b=10.09;
char c='@';
我們就可以說 a、b、c 的資料型別不同,a 是整型變數,b 是浮點型變數,c 是字元型變數。
整型、浮點型、字元型是C語言中的基本資料型別,必須要掌握,後續我們還會介紹更多資料型別,甚至可以自己定義一種新的型別。
注意:字串比較特殊,C語言中沒有這個資料型別,不能使用string a="abc123";這種方式來定義字串,後面我們會講解為什麼。
③.字元與整數
先看下面一段程式碼:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a=69;
char b=0x46; // 十六進位制數以 0x 或 0X開頭
char c='G';
int d=72;
printf("a1=%c, a2=%d, b=%c, c=%d, d=%c\n", a, a, b, c, d);
system("pause");
return 0;
}
輸出結果:
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 也順應了大屏手機的趨勢。 那麼,我們不妨將蘋果公司的市值輸出一下,看看是多長的一串數字:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int value=7628400000000;
// AAPL 是蘋果公司的股票程式碼
printf("The market value of AAPL is %d !\n", value);
system("pause");
return 0;
}
輸出結果:
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 的最大值:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int value=0xffffffff;
printf("The maximum value of \"int\" is %d .\n", value);
system("pause");
return 0;
}
執行結果:
The maximum value of "int" is -1 .
這和我們期望的結果不一樣:我們期望輸出 4294967296,但實際輸出的卻是八竿子打不著的 -1。
這是因為,整數有正負之分,我們需要在32位中選擇一位用來標明到底是正數還是負數,這一位就是最高位。也就是說,0~30位表示數值,31 位表示正負號。如下圖所示
:
注意:
在程式語言中,計數往往是從0開始,例如字串 "abc123",我們稱第 0 個字元是 a,第 1 個字元是 b,第 5 個字元是 3。這和我們平時從 1 開始計數的習慣不一樣,大家要慢慢適應,培養程式設計思維。
如果我們希望能表示更大些的數值,也可以不使用符號位,而用關鍵字
unsigned來宣告變數,例如:
// 下面的兩種寫法都是正確的,使用 unsigned 時可以不寫 int
unsigned int a=100;
unsigned a=999;
這樣,就只能表示正數了。unsigned 型別的整數需要用%u來輸出。
我們來驗證一下上面的說法:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=1234;
unsigned a1=1234;
int b=0x7fffffff;
int c=0x80000000; // 0x80000000 = 0x7fffffff + 0x1
int d=0xffffffff;
unsigned e=0xffffffff;
printf("a=%d, a(u)=%u\n", a, a);
printf("a1=%d, a1(u)=%u\n", a1, a1);
printf("b=%d, b(u)=%u\n", b, b);
printf("c=%d, c(u)=%u\n", c, c);
printf("d=%d, d(u)=%u\n", d, d);
printf("e=%d, e(u)=%u\n", e, e);
system("pause");
return 0;
}
輸出結果:
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表示所佔記憶體的大小,不要混淆,請看下面的例子:
long int a=1234;
long b=299;
unsigned long int c=999;
unsigned long d=98720;
short int m=222;
short n=2094;
unsigned short int p=3099;
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 個位元組,是雙精度浮點數。 最後,我們來總結一下現代計算機中各種資料型別的長度。說 明 | 字元型 | 短整型 | 整型 | 長整型 | 單精度浮點型 | 雙精度浮點型 |
資料型別 | char | short | int | long | float | double |
所佔位元組 | 1 | 2 | 4 | 4 | 4 | 8 |
#include <stdio.h>
#include <stdlib.h>
int main()
{
int m=290;
float n=23;
printf("int = %d\n", sizeof(m));
printf("float = %d\n", sizeof(n));
printf("double = %d\n", sizeof(double));
printf("A = %d\n", sizeof('A'));
printf("23 = %d\n", sizeof(23));
printf("14.67 = %d\n", sizeof(14.67));
system("pause");
return 0;
}
輸出結果:
int = 4
float = 4
double = 8
A = 1
23 = 4
14.67 = 8
sizeof 的運算元既可以是變數、資料型別,還可以是某個具體的資料。細心的讀者會發現,sizeof(14.67) 的結果是8,不錯,因為小數預設的型別就是double。
5.加減乘除運算
C語言也可以進行加減乘除運算,但是運算子號與數學中的略有不同,見下表。加法 | 減法 | 乘法 | 除法 | 求餘數 | |
數學 | + | - | × | ÷ | 無 |
C語言 | + | - | * | / | % |
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=12;
int b=100;
float c=8.9;
int m=a+b;
float n=b*c;
double p=a/c;
int q=b%a;
printf("m=%d, n=%f, p=%lf, q=%d\n", m, n, p, q);
system("pause");
return 0;
}
輸出結果:
m=112, n=889.999962, p=1.348315, q=4
b*c 的結果很明顯是890,但是卻輸出一個近似數,這裡涉及到浮點數的運算原理,並且實際開發中較少使用浮點數運算,所以這裡我們不再深究。
你也可以讓數字直接參與運算:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=12;
int b=100;
float c=8.9;
int m=a-b; // 變數參與運算
int n=a+239; // 有變數也有數字
double p=12.7*34.3; // 數字直接參與運算
printf("m=%d, n=%f, p=%lf\n", m, n, p);
printf("m*2=%d, 6/3=%d, m*n=%ld\n", m*2, 6/3, m*n);
system("pause");
return 0;
}
輸出結果:
m=-88, n=-0.000000, p=0.000000
m*2=-176, 6/3=2, m*n=-22088
注意:
除數不能是 0,所以諸如int a=3/0;這樣的語句是錯誤的。
再來看一個例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=12;
int b=10;
printf("a=%d\n", a);
a=a+8;
printf("a=%d\n", a);
a=a*b;
printf("a=%d\n", a);
system("pause");
return 0;
}
輸出結果:
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。這僅是一種簡寫,不會影響效率,其他運算子也可以這樣寫。
①.加一和減一
一個整數自身加一可以這樣寫:
a+=1;
它等價於 a=a+1;。
但是在C語言中還有一種更簡單的寫法,就是 a++; 或者++a; 。這種寫法叫做自加或自增;意思很明確,就是自身加一。
相應的,也有a--和--a,叫做自減,表示自身減一。
++和--分別稱為自增和自減運算子。
那麼,++ 在前面和後面有區別嗎?
請看下面的例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=12;
int a1=a++;
int b=10;
int b1=++b;
printf("a=%d, a1=%d\n", a, a1);
printf("b=%d, b1=%d\n", b, b1);
system("pause");
return 0;
}
輸出結果:
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叫做前自減,意思與++相同。
自增自減非常方便,後續程式設計中會經常用到,大家要注意區分。
為了強化記憶,我們再來看一個自增自減的綜合示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=12;
int b=1;
int c=a-(b--); // ①
int d=(++a)-(--b); // ②
printf("c=%d, d=%d\n", c, d);
system("pause");
return 0;
}
輸出結果:
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 的整數矩陣,為了增強閱讀性,數字要對齊,怎麼辦呢?我們顯然可以這樣來做:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a1=20, a2=345, a3=700, a4=22;
int b1=56720, b2=9999, b3=20098, b4=2;
int c1=233, c2=205, c3=1, c4=6666;
int d1=34, d2=0, d3=23, d4=23006783;
printf("%d %d %d %d\n", a1, a2, a3, a4);
printf("%d %d %d %d\n", b1, b2, b3, b4);
printf("%d %d %d %d\n", c1, c2, c3, c4);
printf("%d %d %d %d\n", d1, d2, d3, d4);
system("pause");
return 0;
}
執行結果:
20 345 700 22
56720 9999 20098 2
233 205 1 6666
34 0 23 23006783
矩陣一般在大學的《高等數學》中會講到,m×n 的數字矩陣可以理解為把 m×n 個數字擺放成 m 行 n 列的樣子。
看,這是多麼地自虐,要敲那麼多空格,還要嚴格控制空格數,否則輸出就會錯位。
類似的需求隨處可見,整齊的格式會更加美觀,讓人覺得生動有趣。我們大可不必像上面一樣,printf 可以更好的控制輸出格式。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a1=20, a2=345, a3=700, a4=22;
int b1=56720, b2=9999, b3=20098, b4=2;
int c1=233, c2=205, c3=1, c4=6666;
int d1=34, d2=0, d3=23, d4=23006783;
printf("%-9d %-9d %-9d %-9d\n", a1, a2, a3, a4);
printf("%-9d %-9d %-9d %-9d\n", b1, b2, b3, b4);
printf("%-9d %-9d %-9d %-9d\n", c1, c2, c3, c4);
printf("%-9d %-9d %-9d %-9d\n", d1, d2, d3, d4);
system("pause");
return 0;
}
輸出結果:
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,可以輸出單個字元,例如:
putchar('a');
putchar(7);
putchar('\x46');
輸出結果:
aF // 同時會聽到喇叭發出“嘟”的聲音
程式是人機互動的媒介,有輸出必然也有輸入。在C語言中,使用 scanf 從鍵盤獲取使用者輸入的資料。
scanf 是 scan format 的縮寫,意思是格式化掃描,這裡可以理解為從鍵盤獲得使用者輸入。我們先來看一個例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("a+b=%d\n", (a+b));
system("pause");
return 0;
}
執行結果:
34 29 ↙
a+b=63
從鍵盤輸入 34,空格,再輸入 29,然後回車(↙表示回車),就會看到兩個數相加的結果。
scanf 和 printf 非常相似:
scanf("%d %d", &a, &b); // 獲取使用者輸入的兩個整數,分別賦值給變數 a 和 b
printf("%d %d", a, b); // 將變數 a 和 b 的是在顯示器上輸出。
它們都有格式控制字串,都有變數列表。不同的是,
scanf 的變數前要帶一個&符號;&稱為取地址符,也就是獲取變數在記憶體中的地址。
在《二進位制思想以及資料的儲存》一節中講到,資料是以二進位制的形式儲存在記憶體中的,位元組(Byte)是最小的可操作單位。為了便於管理,我們給每個位元組分配了一個編號,使用該位元組時,只要知道編號就可以,就像每個學生都有學號,老師會隨機抽取學號來讓學生回答問題。位元組的編號是有順序的,從 0 開始,接下來是 1、2、3……
下圖是 4G 記憶體中每個位元組的編號(以十六進位制表示):
這個編號,就叫做
地址(Address)。int a; 會在記憶體中分配四個位元組的空間,我們將第一個位元組的地址稱為變數 a 的地址,也就是 &a 的值。對於前面講到的整數、浮點數、字元,都要使用 & 獲取它們的地址,scanf 會根據地址把讀取到的資料寫入記憶體。
我們不妨將它們的地址輸出看一下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a='F';
int b=12;
int c=452;
printf("&a=%#x, &b=%#x, &c=%#x\n", &a, &b, &c);
system("pause");
return 0;
}
輸出結果:
&a=0x18ff48, &b=0x18ff44, &c=0x18ff40
圖:a、b、c 的記憶體地址
注意:
你看到的地址是虛擬地址,並不等於它在實體記憶體中的地址。虛擬記憶體是現代作業系統因記憶體管理的需要才提出的概念,dos 下沒有這個概念,使用者看到的都是真實的地址。CPU 操作的是實體記憶體地址,所以虛擬地址必須經過轉換才能交給 CPU,這是 OS 的工作,對使用者是透明的。
再來看一個 scanf 的例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a, b, c, age;
float scores;
scanf("a=%d,b=%d,c=%d", &a, &b, &c);
printf("a+b+c=%d\n\n", (a+b+c));
fflush(stdin); // 清空緩衝區
scanf("Tom's age is %d, his scores is %f.", &age, &scores);
printf("age=%d, scores=%f.\n", age, scores);
system("pause");
return 0;
}
執行結果:
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 時,程式會首先檢查該區域是否有資料:
- 如果沒有,就等待使用者輸入,使用者從鍵盤輸入的每個字元都會暫時儲存到這裡,直到按下回車鍵,輸入結束,scanf 再從這裡讀取資料,賦值給變數。
- 如果有資料,哪怕是一個字元,scanf 也會直接讀取,不會等待使用者輸入。
①.getchar
scanf 用於接收使用者輸入的各種資料,如果僅僅是輸入單個字元,也可以使用 getchar。請看下面的例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a, b, c;
a=getchar();
printf("a='%c'\n", a);
b=getchar();
printf("b='%c'\n", b);
c=getchar();
printf("c='%c'\n", c);
system("pause");
return 0;
}
執行結果:
XYZ↙
a='X'
b='Y'
c='Z'
你也可以這樣輸入:
X↙
a='X'
b='
'
Y↙
c='Y'
第二次之所以是這樣的結果,是因為 getchar 也帶有緩衝區。輸入 X 並回車,第一個 getchar 讀取到 'X',接著第二個 getchar 讀取到回車換行符,到第三個 getchar,緩衝區中已經沒有資料,所以會等待使用者輸入。
8.運算子的優先順序和結合性
先來看一個例子:
#include <stdio.h>
int main(){
int a=10,b=1,c=2;
a=b=c;
printf( "12+3*5=%d\n", 12+3*5);
printf( "a=%d, c=%d\n", a, c);
return 0;
}
執行結果:
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 出現非法字元減號(-) 在使用識別符號時還必須注意以下幾點:- C語言雖然不限制識別符號的長度,但是它受到不同編譯器的限制,同時也受到具體機器的限制。例如在某個編譯器中規定識別符號前128位有效,當兩個識別符號前128位相同時,則被認為是同一個識別符號。
- 在識別符號中,大小寫是有區別的,例如BOOK和book 是兩個不同的識別符號。
- 識別符號雖然可由程式設計師隨意定義,但識別符號是用於標識某個量的符號,因此,命名應儘量有相應的意義,以便於閱讀理解,作到“顧名思義”。
②.關鍵字
關鍵字(Keywords)是由C語言規定的具有特定意義的字串,通常也稱為保留字,例如 int、char、long、float、unsigned 等。我們定義的識別符號不能與關鍵字相同,否則會出現錯誤。 你也可以將關鍵字理解為具有特殊含義的識別符號,它們已經被系統使用,我們不能再使用了。 標準C語言中一共規定了32個關鍵字。③.註釋
註釋(Comments)可以出現在程式碼中的任何位置,用來向用戶提示或解釋程度的意義。程式編譯時,會忽略註釋,不做任何處理,就好像它不存在一樣。 C語言支援單行註釋和多行註釋:- 單行註釋以//開頭,直到本行末尾(不能換行);
- 多行註釋以/*開頭,以*/結尾,註釋內容可以有一行或多行。
/*
Powered by: c.biancheng.net
Author: xiao p
Date: 2015-6-26
*/
#include <stdio.h>
int main()
{
/* puts 會在末尾自動新增換行符 */
puts("http://c.biancheng.net");
printf("C語言中文網\n"); //printf要手動新增換行符
return 0;
}
執行結果:
http://c.biancheng.net
C語言中文網
在除錯程式的過程中可以將暫時不使用的語句註釋掉,使編譯器跳過不作處理,待除錯結束後再去掉註釋。