天天看點

偷學C語言第二天

目錄

補碼

 字元類型 VS 整型

字元串

算數運算符

類型轉換

分支結構

關系運算符

邏輯運算符

if語句

switch-case語句 

if和switch語句嵌套

循環結構

while循環

do...while循環

for循環

continue

多學一點

for VS while

goto

條件運算符

彩蛋

第一天忘了跟大家說C環境的配置,點選這裡檢視C語言初學者如何配置開發環境。

補碼

計算機是用補碼的形式存儲整型資料的。正數的原返補都是一樣的,負數的補碼需要三步才能得到:

  1. 先把該數的絕對值轉換成二進制形式,也就是該數的源碼 ------ 源碼就是原來的樣子
  2. 再把數值位按位取反,得到反碼 ------ 反碼就是反着幹,0變1,1變0;
  3. 最後将第2步的值加一就得到了補碼 ------ 補碼就是補1。
    偷學C語言第二天

為什麼會引入補碼?詳情見:計算機中數的表示 & 使用補碼的好處

字元類型 VS 整型

先看個例子:

#include<stdio.h>
int main()
{
	char c = 'A';
	printf("%c = %d\n",c,c);
}           

運作結果:

偷學C語言第二天

字元'A'為什麼以整型輸出時是65?還記得第一天在講轉義字元時提到的ASCII表嗎,對的,這裡就是通過查ASCII表查到字元'A'對應的十進制數是“65”,是以字元類型是特殊的整型。

再來看個例子:

#include<stdio.h>
int main()
{
	char height = 175;
	printf("我的海拔是%dcm\n",height);
}           
偷學C語言第二天

哎呀我去,不得了了,咋還長到地底下去了,我又不是胡蘿蔔(´•༝•`) 這又是咋回事呢?還記得第一天學C時提到char的取值範圍是-128到127或0到255,為什麼還有個或呢?這是因為C标準中沒有規定char預設是signed還是unsigned,而是由編譯系統決定它的預設限定符,signed char的取值範圍是 -128 ~ 127;unsigned char的取值範圍是 0 ~ 255。是以我們應該手動指定char的限定符。

字元串

C沒有專門為存儲字元串設計一個單獨的類型,因為沒必要。字元串事實上就是一串字元。是以隻需要在記憶體中開辟一塊連續空間,然後存放一串字元類型的變量即可。

  • 聲明字元串的文法:char 變量名[字元的數量];
  • 指派:事實上就是對這一塊連續空間裡邊的每一個字元變量進行指派。我們通過索引号來獲得每個字元變量的空間,文法格式:變量名[索引号] = 字元;
    #include<stdio.h>
    int main() {
    	char words[12];
    	words[0] = 'I';
    	words[1] = ' ';
    	words[2] = 'L';
    	words[3] = 'o';
    	words[4] = 'v';
    	words[5] = 'e';
    	words[6] = ' ';
    	words[7] = 'C';
    	words[8] = 'h';
    	words[9] = 'i';
    	words[10] = 'n';
    	words[11] = 'a';
    	/*'\0'是字元串結束的标記,不可以省掉*/
    	words[12] = '\0';
    	/*聲明和指派放在一塊,稱為初始化,[]中的數量可以省掉,編譯期會自動幫我們計算*/
    	char words2[] = {'I',' ','L','o','v','e',' ','C','h','i','n','a','\0'};
    	/*字元串常量可以用雙引号直接括起來,不必在末尾追加'\0',系統會自動追加*/
    	char words3[] = {"I Love China"};
    	/*字元串常量可以把大括号也省掉*/
    	char words4[] = "I Love China";
    	printf("%s\n",words);
    	printf("%s\n",words2);
    	printf("%s\n",words3);
    	printf("%s\n",words4);
    	return 0;
    }           

算數運算符

假設A的值是10,B的值是20

運算符 描述 執行個體
+ 把兩個操作數相加(雙目) A + B 将得到 30
- 從第一個操作數中減去第二個操作數(雙目) A - B 将得到 -10
* 把兩個操作數相乘(雙目) A * B 将得到 200
/ 分子除以分母(雙目) B / A 将得到 2
% 取模運算符,整除後的餘數(雙目) B % A 将得到 0
++ 自增運算符,整數值增加 1(單目) A++ 将得到 11
-- 自減運算符,整數值減少 1(單目) A-- 将得到 9

代碼示例:

#include<stdio.h>
int main() {
	int a = 10,b = 20;

	printf("a + b = %d\n",a + b);
	printf("a - b = %d\n",a - b);
	printf("a * b = %d\n",a * b);
	printf("b / a= %d\n",b / a);
	/*
	 對于整型類型資料的除法運算,是采取直接舍棄小數部分的方式,而不是什麼四舍五入。
	*/
	printf("a / b= %d\n",a / b);
	printf("b %% a = %d\n",b % a);
	/*
	  ++在前,先自增再指派;++在後,先指派再自增;自減同理。
	*/
	printf("a++ = %d\n",a++);//a++的值是10
	printf("++a = %d\n",++a);//經過上一步運算a的值就變成11了,++a在11的基礎上先自增1變成12,再指派,是以此時a的值是12
	printf("a-- = %d\n",a--);
	printf("--a = %d\n",--a);

	return 0;
}           

需要注意的幾點問題:

  • 因為鍵盤上沒有乘号和除号兩個按鍵,是以用星号(*)和斜杠(/)代替,幾乎所有程式設計語言都是如此。
  • 對于整數間的除法是采取直接舍棄小數部分的方式,而不是什麼四舍五入。
  • 對于浮點數間的除法則能獲得一個相對逼近結果的值(如果除不盡或位數特别多的話)。
  • 百分号(%)取模(求餘)運算符要求兩邊的操作數必須都是整數,其結果也是整數。
  • 雙目單目指的是運算符有幾個操作數。

類型轉換

#include<stdio.h>
#include<math.h>
int main() {
	/*
	 自動轉換:兩種不同類型的資料做運算,容量小的類型會自動向上轉型成容量大的類型再做運算,是以3+3.0得到的是浮點型,以整型格式輸出,結果肯定不對
	*/
	printf("整型輸出:%d\n",3+3.0);
	printf("浮點輸出:%f\n",3.0+3.0);
	/*
	 強制轉換:文法格式:(目标類型)原資料
	 強制類型轉換會損失精度
	*/
	printf("強制類型轉換:%d\n",3+(int)3.6);//浮點型的3.6強轉成int後變成3了

}           

分支結構

之前我們寫的代碼都是順序結構,即按代碼編寫順序依次執行。但我們生活中會存在一種“如果怎樣就怎樣否則會怎樣”的情況,是以就有了分支結構,在學習分支結構之前,我們得先了解關系運算符和邏輯運算符。

關系運算符

關系運算符用于比較兩個數或者兩個表達式的大小關系,包含>(大于)、<(小于)、=(等于)、>=(大于或等于)、<=(小于或等于)、==(等于)、!=(不等于),關系運算符的優先級低于算數運算符,結合性都是從左到右。關系運算符是雙目運算符,用關系運算符将兩邊的變量、資料或表達式連接配接起來,稱之為關系表達式。關系表達式的結果是一個邏輯值,或真(1)或假(0)。

#include<stdio.h>
int main() {
	int a = 6,b = 8;

	printf("a > b結果是%d\n",a > b);
	printf("a < b結果是%d\n",a < b);
	printf("a >= b結果是%d\n",a >= b);
	printf("a <= b結果是%d\n",a <= b);
	printf("a != b結果是%d\n",a != b);
}           

邏輯運算符

邏輯運算符包含&&(與)、||(或)、!(非),優先級:!>&&>||。邏輯運算符存在短路求值,詳情參見下面代碼示例:

#include<stdio.h>
int main() {
	int a = 6,b = 8;
	/*
	  我們輸入非0數,C語言就會判斷為真,否則為假;
	  C語言回報給我們的值隻有0和1,0表示假,1表示真。
	*/
	/*&&:兩邊同為真結果才為真,否則皆為假。*/
	printf("a && b = %d\n",a && b);
	/*||:兩邊同為假結果才為假,否則皆為真。*/
	printf("(a = 0) || b = %d\n",(a = 0) || b);
	/*!:單目運算符,原資料為真,結果則為假,否則為真,即取反。*/
	printf("!0 || a < b = %d\n",!0 || a < b);

	/*
	  短路求值:隻有當第一個操作數或表達式結果不能确定時,才會對第二個資料進行求值操作
	*/
	int c = (a = 0) && (b = 2);
	printf("a = %d,b = %d\n",a,b);
	int d = (a = 1) || (b = 6);
	printf("a = %d,b = %d\n",a,b);

	return 0;
}           

if語句

if語句有三種形态:

  1. if(邏輯資料){//業務代碼} ;
    #include<stdio.h>
    int main() {
    	int age;
    	printf("閣下貴庚:");
    	scanf("%d",&age);
    
    	if(age >=18) {
    		printf("歡迎光臨,祝您玩的開心!");
    	}
    
    	return 0;
    }           
  2. if(邏輯資料){//業務代碼}else{//業務代碼};
    #include<stdio.h>
    int main() {
    	int age;
    	printf("閣下貴庚:");
    	scanf("%d",&age);
    
    	if(age >=18) {
    		printf("歡迎光臨,祝您玩的開心!");
    	} else {
    		printf("未成年人禁止入内,謝謝配合!");
    	}
    
    	return 0;
    }           
  3. if(邏輯資料){//業務代碼}else if(邏輯值){//業務代碼}else if(邏輯值){//業務代碼}...else{//業務代碼}。
    #include<stdio.h>
    int main() {
    	float score;
    	char  grade;
    	printf("請輸入學生成績:");
    	scanf("%f",&score);
    	/*輸入的是浮點型,以整型列印,結果為0*/
    	printf("使用者輸入的成績是(以整型格式輸出):%d\n",score);
    	if(score >=90) {
    		grade = 'A';
    	} else if(score >= 80 && score < 90) {
    		grade = 'B';
    	} else if(score >= 70 && score < 80) {
    		grade = 'C';
    	} else if(score >= 60 && score < 70) {
    		grade = 'D';
    	} else if(score >= 0 && score < 60) {
    		grade = 'E';
    	} else {
    		printf("學生分數不能給負數,太打擊學生自信心了!");
    		return 0;
    	}
    	printf("該學生分數等級是%c\n",grade);
    
    	return 0;
    }           

switch-case語句 

可以認為switch-case語句是if語句第三種形态的優化版本,因為它的文法更簡潔。文法如下:

switch (表達式)
{
    case 常量表達式1: 語句或程式塊
    case 常量表達式2: 語句或程式塊
    ……
    case 常量表達式n:語句或程式塊
    default: 語句或程式塊
}           

示例代碼: 

#include<stdio.h>
int main() {
	char  grade;
	printf("請輸入學生成績評級:");
	scanf("%c",&grade);
	switch(grade) {
		case 'A':
			printf("該學生分數是90分以上。\n");
			/*break關鍵字用于跳出switch語句*/ 
			break;
		case 'B':
			printf("該學生分數是80分以上。\n");
			break;
		case 'C':
			printf("該學生分數是70分以上。\n");
			break;
		case 'D':
			printf("該學生分數是60分以上。\n");
			break;
		case 'E':
			printf("該學生分數未及格。\n");
			break;
		/*default語句是可選的,即可以不寫,不過最好寫上*/	
		default:
			printf("輸出成績評級不合法,請輸入('A'/'B'/'C'/'D'/'E')。\n");
			break;
	}

	return 0;
}           

 可以看出我們在switch語句中增加了break關鍵字用于跳出switch語句,如果不加break,switch語句就會從比對到的case開始執行,一直執行到default才會退出,這種現象叫做“case穿透”。switch語句中的 case和 default 事實上都是“标簽”,用來标志一個位置而已。當 switch 跳到某個位置之後,就會一直往下執行,是以我們這裡還需要配合一個 break語句,讓代碼在适當的位置跳出 switch。

來看一個利用case穿透的示例:

#include<stdio.h>
/*
  根據使用者輸入的月份,比對相應的季節
*/
int main() {
	/*月份*/
	int  month;
	/*季節*/
	char* season;
	printf("請輸入月份:");
	scanf("%d",&month);
	switch(month) {
		case 3:
		case 4:
		case 5:
			season = "春季";
			break;
		case 6:
		case 7:
		case 8:
			season = "夏季";
			break;
		case 9:
		case 10:
		case 11:
			season = "秋季";
			break;
		case 12:
		case 1:
		case 2:
			season = "冬季";
			break;
		default:
			printf("輸出月份不合法。\n");
			return 0;
	}

	printf("輸出的月份對應%s。\n",season);

	return 0;
}           

if和switch語句嵌套

#include<stdio.h>
/*
  根據使用者輸入的月份,比對相應的季節
*/
int main() {
	/*月份*/
	int  month;
	/*季節*/
	char* season;
	printf("請輸入月份:");
	scanf("%d",&month);
	if(month >= 1 && month <= 12) {
		switch(month) {
			case 3:
			case 4:
			case 5:
				season = "春季";
				break;
			case 6:
			case 7:
			case 8:
				season = "夏季";
				break;
			case 9:
			case 10:
			case 11:
				season = "秋季";
				break;
			case 12:
			case 1:
			case 2:
				season = "冬季";
				break;
			default:
				break;
		}
		printf("輸出的月份對應%s。\n",season);
	} else {
		printf("輸出的月份不合法!\n");
	}

	return 0;
}           

if語句也可以與if嵌套,大家自行練習。

循環結構

while循環

文法:while(邏輯表達式){循環體} ------ 隻要邏輯表達式的結果為真,就會一直循環執行循環體中的語句。

/*
  統計使用者輸入的字元個數:
*/
#include<stdio.h>
int main() {
	int count = 0;

	printf("請輸入你想說的話:");
	/*getchar()------從标準輸入流中擷取字元*/
	while(getchar() != '\n') {
		count++;
	}
	printf("您總共輸入了%d個字元。\n",count);
}           

do...while循環

文法:do{循環體}while(邏輯表達式); ------ 先會執行一次循環體,然後進入while,隻要邏輯表達式為真,就會一直循環執行。do...while循環語句不是很常用。

for循環

文法格式:

for (表達式1; 表達式2; 表達式3){
        循環體;
}           

表達式1是初始化循環變量;表達式2是循環的條件,滿足條件就會執行循環體;最後執行表達式3,表達式3是改變循環變量的值。下面是一個判斷使用者輸入的自然數是否是素數的案例:

/*
  判斷使用者輸入的數字是否是素數:
  <1>質數又稱素數。一個大于1的自然數,除了1和它自身外,不能被其他自然數整除的數叫做質數;否則稱為合數。
  <2>0和1既不是質數也不是合數,最小的質數是2。
*/
#include<stdio.h>
int main() {
	/*接收使用者輸入的自然數*/
	int num;
	/*标記是否是素數,預設不是素數*/
	_Bool isPrimer = 1;
	printf("請輸入一個自然數:");
	scanf("%d",&num);

	if(num == 0 || num == 1) {
		printf("您輸入的%d既不是素數也不是合數~\n",num);
		return 0;
	}

	/*已知2和3是素數,是以這裡隻對大于3的自然數做處理*/
	if(num > 3) {
		int i;
		/*能整除一個數的數,肯定小于或等于被除數的一半,是以這裡取 num/2 */
		for(i = 2; i <= num / 2; i++) {
			if(num % i == 0) {
				isPrimer = 0;
				/*隻要找到一個可以整除num的除數就跳出循環*/
				break;
			}
		}
	}

	if(isPrimer)
		printf("您輸入的自然數%d是素數\n",num);

	else
		printf("您輸入的自然數%d不是素數\n",num);

	return 0;
}           

另外,for 語句的表達式1,表達式2和表達式3都可以按需省略(但分号不能省):

  • for ( ; 表達式2; 表達式3)
  • for (表達式1; 表達式2; )
  • for (表達式1; ; )
  • for ( ; ; )
  • ……

C99允許在 for語句的表達式1中定義變量,比如如下代碼:

for (int i=0, int j=10; i < j; i++, j--){
    printf("%d\n", i);
}           

增加這個新特性的原因主要是考慮到循環通常需要一個循環變量(計數器),而這個循環變量隻需要在for循環中有效即可。是以在表達式1的位置定義的循環變量,作用域僅限于循環中,出了循環,它就無效了。

ps:循環結構同意可以嵌套,如下中的for循環嵌套案例,先執行内層循環,再執行外層循環。

/*
  列印九九乘法表
*/
#include<stdio.h>
int main() {
	int i,j;
	for(i=1; i<10; i++) {
		for(j=1; j<=i; j++) {
			printf("%d * %d = %d\t",j,i,j*i);
		}
		printf("\n");
	}

	return 0;
}           

continue

continue與break都是用于跳出循環,continue是跳出本輪循環繼續下一輪循環,break是跳出所在的那一層循環(當循環嵌套時如果要跳出整個循環可以借助goto語句,其他場景慎用goto)。

/*
  去掉使用者輸入的空格 
*/
#include<stdio.h>
int main() {
	char ch;
	while((ch = getchar()) != '\n') {
		if(ch == ' ') {
			continue;
		}
		putchar(ch);
	}
	putchar('\n');
	return 0;
}           

多學一點

for VS while

for語句和 while語句執行過程是有差別的,它們的差別在于出現 continue語句時。在 for語句中,continue語句跳過循環的剩餘部分,直接回到調整循環變量部分。在 while語句中,由于調整部分是循環體的一部分,是以 continue語句會把它也跳過,稍不注意就會出現問題。比如如下案例中:使用while改造for循環實作的功能,結果死循環了{T_T}

/*
  for VS while
*/
#include<stdio.h>
int main() {
	int i,j;
	for(i = 0,j = 10; i<10; i++,j--) {
		if(i > j) {
			continue;
		}
	}
	printf("for循環執行完成後i和j的值分别是:%d、%d\n",i,j);
	
	/*
	  使用while改造上面的for循環 
	*/ 
	i = 0;
	j = 10;
	while(i<10) {
		if(i > j) {
			continue;
		}
		i++;
		j--;
	}
	printf("while循環執行完成後i和j的值分别是:%d、%d\n",i,j);
	return 0;
}           

goto

goto用于跳到指定标簽的位置。

/*
  goto語句使用案例
*/
#include <stdio.h>
int main() {
	int i = 6;

	while (i++) {
		if (i > 8) {
			goto Label;
		}
	}

Label:
	printf("第%d次發牢騷:\n從來表白多白表,\n自古情書難書情,\n笑談年少多少年,\n常與生人道人生。 \n", i);

	return 0;
}           

條件運算符

條件運算符是C語言中唯一一個三目運算符,文法格式:exp1 ? exp2 : exp3; exp1是邏輯表達式,如果結果為真,則取exp2的值,否則取exp3的值。其實它就是為了簡化 if...else...這種簡單的語句。

/*
  條件運算符簡化if-else語句,實作求兩個數中的最大值。
*/
#include <stdio.h>
int main() {
	int i = 6,j = 8,max;

	if(i > j)
		max = i;
	else
		max = j;

	/*使用條件表達式簡化上述語句*/
	max = i > j ? i:j;

	printf("最大值是%d。\n",max);
	return 0;
}           

彩蛋

#include <stdio.h>
int main()
{
int a = 3;
printf("%d,%d\n",a++,a++); 
printf("\n%d",a);

return 0;
}