程式語言進階

林品儒

2016/1/29

社團介紹

NTNUCIC

台師大資訊研究社

對資訊相關有興趣的人都來~~

自我介紹

林品儒

www.github.com/aigecko

師大貓耳控

喜歡Asm,Ruby和Crystal

指標

在C/C++中深奧難懂的神祕名詞

代表記憶體的位置

可以取出變數的記憶體位置

或是從指定位置取出值

指標大小

雖然各種型別佔用空間可能不一樣

但是他們的指標都是一樣大的

可能是32或64位元視編譯器和系統而定

指標宣告

變數名稱前面加上星號*

這樣"該變數"就會是該型別的指標

取址和取值

對變數使用&當前綴的話可以取得其指標

對指標使用*前綴則可以取得該位址放的值

注意地址和放的東西不一定一樣

空指標

指標的值為0(NULL)則為空指標

不可以對空指標取值不然會發生錯誤

指標初始化時儘量初始化為空指標

不是不爆 時候未到

練習

地址和放的值不會一樣

指標可以指派給變數

#include <stdio.h>
int main(){
	int a=0x5566;
	int *p=&a;
	printf("%X %X %X %X %X",a,p,*p,&a,*(&a));
}

使用指標

取得指標後可以對該位置的值進行存取

可以讀也可以寫入

#include <stdio.h>
int main(){
	int n=4850;
	int *p=&a;
	*p=7850
	printf("%X %X %X %X %X",a,p,*p,&a,*(&a));
}

練習

既然可以使用指標存取值

那就可以將兩個變數的值交換

提示:宣告變數a,b並宣告指標p,q指派a,b的位址

陣列

要使用陣列需要使用指標進行操作

宣告陣列要在變數名稱後加上使用[]

陣列宣告時要指定大小

可以用{}將其元素初始化

陣列範例

可以手動指定大小

也可以把元素指派好後自動知道大小

#include <stdio.h>
int main(){
	bool arr[4],table[]={true,false,false};
	char list[5]={'a','b','c','d','e'};
	printf("%d %d %d",
		sizeof(arr)/sizeof(bool),
		sizeof(table)/sizeof(bool),
		sizeof(list)/sizeof(char));
	return 0;
}

陣列與字串

字串其實是一種char陣列

但是字串最後的元素是NULL字元

把字串裝進變數可使用陣列宣告方式

char str[]="hello, world"

元素與索引

陣列中的放的東西稱為元素

要取出元素必須要索引代表其位置

索引值從0開始

使用[]來取得指定索引的元素

練習

觀察字串索引以及元素的變化

觀察初始化但未指定的陣列

觀察未初始化的陣列

#include <stdio.h>
int main(){
	char str[]="abcdef";
	int arr[4]={22,66};
	short uninit[2];
	printf("%d %d %d\n",str[0],str[5],str[6]);
	printf("%d %d %d\n",arr[0],arr[1],arr[2]);
	printf("%hd %hd",uninit[0],uninit[1]);
}

陣列與指標

陣列的頭可以代表該型態的指標

對於指標可以進行加減法來移動

移動的矩離和該型別的大小相關

指標運算

加法減法可以使用

還可以使用 ++ 遞增

以及使用 -- 遞減

注意!此兩個運算子是地雷請小心使用

指標移動

可以用來迭代整個陣列

記得陣列本身可以轉成該型別指標

#include <stdio.h>
int main(){
	int arr[]={100,200,0};
	int *p=arr; //注意這裡直接把arr給p
	printf("%d ",*p);p++;
	printf("%d ",*p);p++;
	printf("%d ",*p);p++;
}

陣列取值

使用指標取出指定索引的值

#include <stdio.h>
int main(){
	int arr[]={478,771,775};
	int *p=arr;
	printf("%d %d\n",*(arr+0),arr[0]);
	printf("%d %d\n",*(arr+1),arr[1]);
	printf("%d %d\n",*(arr+2),arr[2]);
}

其實中括號就是幫我們精減語法

練習

陣列也可以裝指標

使用printf列印陣列中的字串

const char *arr[3]={
	"coppermine",
	"thunghbird",
	"northwood"
};

常數

使用const關鍵字前綴代表常數

常數不可以被更改

但是有指標時就要小心了

常數的指標vs指標的常數

#include <stdio.h>
int main(){
	int a=4004;
	const int *p;  //指向的值不能改
	int * const q=&a; //指標本身不能改
	const int * const r=&a; //都不能改
}

辨識方法

看看const在 * 的哪一邊

放前面就是常數的指標

放後面就是指標的常數

兩個都有就是常數的指標常數

練習

補上p和q的宣告讓輸出為8008 8008

#include <stdio.h>
int main(){
	int a=8087,b=8008;
	//宣告不同型態的p和q
	p=&b;
	*q=*p;
	printf("%d %d",*p,a);
}

格式化輸入

為什麼輸出很早就講了而沒有講輸入

因為輸入要有指標才能做到

scanf可傳入格式以及多個指標

將輸入的值以指定格式取代指標指的值

格式化字串

其他的格式相同,只有以下不同

"%f"對應單精度浮點數

"%d"對應雙精度浮點數

"%s"對應字串(記得字串空間要夠大)

要知道佔用的記憶體有多大才能放好放滿

練習

語法參考 scanf("%d",&n);

宣告以下型態的變數並在輸入後輸出其值

unsigned int,long long int,float及double

比較運算

在講解分支指令前需要先知道比較運算

對兩個運算元比較之後得出其真假值

數值比較

大於 > 大於等於 >=

小於 < 小於等於 <=

相等 == 不相等 !=

要注意的是相等的等號有2個而非1個

字串比較

由於字串其實是指標

不同字串在記憶體中的位置不會一樣

因此直接比較指標沒有意義

要比較兩方指標所指的值有無相同

字串比較

#include <string.h>

其中有strcmp可以傳入兩個字串

字串相等時回傳0

前字串小於後字串回傳負值

前字串大於後字串回傳正值

練習

列印以下字串的比較結果

"i7-6700","i5-6500"

"p4 631","p4 631"

"MX440","R4850"

分支指令

使用條件讓程式執行流程改變

所以才需要講解比較運算

不同語言的分支指令語法可能略有出入

if

最簡單的分支指令

if(條件){想執行的內容}

此處的右大括號後方是不需要分號的

用在只有某種情況才要執行的時候

if實例

#include <stdio.h>
int main(){
	if(3==8){
		printf("impossible!!");
	}
}

if-else

二分法的分支指令

if(條件){想執行的內容}

else{否則執行這裡}

條件符合就往if去,不合則往else去

用在不同狀況有不同處理

if-else實例

#include <stdio.h>
int main(){
	if('Z'>'B'){
		printf("Just do it!");
	}
	else{
		printf("Don't do that.");
	}
}

if-else if-else

多個條件的判斷

if(條件){想執行的內容}

else if(條件){某些內容}

else{不然就執行這裡}

if-else if-else實例

#include <stdio.h>
int main(){
	unsigned int n;scanf("%ud",&n);
	if(n<50){printf("You get nothing.");}
	else if(n<80){printf("You get dirt.");}
	else if(n<95){printf("You get stone.");}
	else if(n<100){printf("You get gold");}
	else{printf("Out of range");}
}

switch-case

上面講的都是不同的判斷是來做分支

使用switch-case可以只對某變數的值做比較

因為語法很難描述就直接看實例吧

switch-case實例

#include <stdio.h>
int main(){
	char c;scanf(" %c",&c);
	switch(c){
		case '1':{
			printf("Taiwan No.1");
			break;
		}
		case '2':
		case '3':
			printf("2 or 3");
			break;
		case '4':
			break;
		default:
			printf("other");
	}
}

練習

依照隨機的骰子列印不同的字串

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
	srand(time(NULL));
	int dice=rand()%64;
	//骰出0~7輸出"AL"
	//骰出8~15輸出"AX"
	//骰出16~31輸出"EAX"
	//骰出32~63輸出"RAX"
	//其他狀況輸出"QQ"
}

迴圈

改變程式執行流程的除了分支還有迴圈

可以在特定狀況下重複執行程式碼

有各種不同功能的指令

while

最簡單的迴圈

while(條件){重複部份}

當條件符合時會執行大括號內的指令

執行完會再判斷是否符合條件

無限迴圈

會一直執行而不會停止

剛好當成while語法的實例

#include <stdio.h>
int main(){
	while(true){
		printf(".");
	}
}

可以使用ctrl+c停止程式

練習

取得輸入整數取log2的整數部份

提示:使用除法將輸入的數每次除以2

提示:記得宣告另外的變數來遞增

for

進階版的迴圈但是更常用

for(初始化;條件;內容結束時執行){內容}

可以在初始化的地方宣告變數

在條件的部份撰寫所需條件

在內容結束時執行的部份遞增變數

要注意是2個分號而非逗號

for實例

for常常拿來做指定次數的迴圈

#include <stdio.h>
int main(){
	for(int i=0;i<5;i++){
		printf("now i is %d\n",i);
	}
}

練習

輸入數字指定迴圈的執行次數

輸入字串使讓迴圈執行時可以列印

陣列迭代

使用for迴圈操作陣列的每個元素

#include <stdio.h>
int main(){
	int arr[5];
	for(int i=0;i<sizeof(arr)/sizeof(int);i++){
		printf("arr[%d] is %d\n",i,arr[i]);
	}
}

練習

宣告有8個元素的整數陣列

把陣列中的每個元素變成其索引的平方數

邏輯運算子

此運算子可以補足分支指令的不足

可以同時判斷多個條件

AND

使用 && 或and將兩個值取AND

要注意這裡是兩個&

是對兩個值進行操作而非其中的位元

兩個值為true才為true

否則為false

OR

使用 || 或or將兩個值取OR

一樣要注意是2個|

兩個值任一為true就為true

其他就是false

XOR

可以用 ^ 或xor將兩個值取XOR

兩值相同則為false

兩值不同則為true

NOT

其實NOT應該歸類於邏輯運算子

可以用 ! 或not讓結果反向

false變成true

true變成false

最後

這次是台師大資訊研究社第一次舉辦寒訓

當然採用直播也是第一次嘗試

希望可以讓大家對寫程式有初步的認識

此次寒訓課程就到此結束

感謝各位的收看及指教