# C 語言語法筆記
[C 語言規格書](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf)
## Data Types
| 資料型態 | Size (Byte) | 格式 |
|:--------------------:|:--------------------------:|:----:|
| (unsigned) short | 2 | %d |
| (unsigned) int | 4 | %d |
| (unsigned) long | 4 (32-bits) 或 8 (64-bits) | %d |
| (unsigned) long long | 8 | %d |
| float | 4 | %f |
| double | 8 | %f |
| long double | 12 | %f |
| char | 1 | %c |
| (unsigned) char | 1 | %c |
:::warning
unsigned 資料型態格式為%u
:::
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
// printf("%d\n", sizeof(unsigned long)); 會導致警告,
// 因为 sizeof 回傳的是 size_t 類別,而 %d 是用於格式化 int 類別的格式符。
printf("%zu\n", sizeof(unsigned long));
return 0;
}
//output
8
Program ended with exit code: 0
```
>`size_t` is an unsigned data type,對於 32-bit 的系統而言,size_t 的大小為 4 bytes,而 64-bit 的系統則為 8 bytes。(%zu is used for size_t values)
```c
跳脫字元:
\n: 換行
\r: 將游標移至目前所在行的起始位置
\t: 水平TAB
\a: 警告。讓系統發出警告聲
\\: 在字串中顯示反斜線
\': 在字串中顯示單引號
\": 在字串中顯示雙引號
\?: 在字串中顯示問號
```
## 型別轉換
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
// short -> int
short s_1 = 10;
int i_1 = (int)s_1;
printf("s_1 size: %zu Bytes\ni_1 size: %zu Bytes\n", sizeof(s_1), sizeof(i_1));
//int -> short
int i_2 = 10;
short s_2 = (short)i_2;
printf("\ni_2 size: %zu Bytes\ns_2 size: %zu Bytes\n", sizeof(i_2), sizeof(s_2));
//int -> float
int i_3 = 10;
float f_1 = (float)i_3;
printf("\nf_1 = %f\n", f_1);
//float -> int
float f_2 = 10.5;
int i_4 = (int)f_2;
printf("i_4 = %d\n", i_4);
//overflow
const int i_5 = 32768;
short s_3 = (short)i_5;
printf("\n%d overflow occur! short range is -32768 ~ 32767\n", s_3);
int i_6 = -15;
unsigned short us_4 = (unsigned short)i_6;
printf("%u overflow occur! unsigned short range is 0 ~ 65535\n", us_4);
double d_1 = 3.5e38; // d_1 = 3.5*10^38
float f_3 = (float)d_1;
printf("%f overflow occur! float range is -3.4e38 ~ 3.4e38\n\n", f_3); //inf: 無窮大
}
```
```
//output
s_1 size: 2 Bytes
i_1 size: 4 Bytes
i_2 size: 4 Bytes
s_2 size: 2 Bytes
f_1 = 10.000000
i_4 = 10
-32768 overflow occur! short range is -32768 ~ 32767
65521 overflow occur! unsigned short range is 0 ~ 65535
inf overflow occur! float range is -3.4e38 ~ 3.4e38
Program ended with exit code: 0
```
# 陣列
## Array
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
//一維陣列
int i_arr[] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
printf("%d ", i_arr[i]);
}
printf("\n");
int i_arr2[5];
i_arr2[0] = 6;
i_arr2[1] = 7;
i_arr2[2] = 8;
i_arr2[3] = 9;
i_arr2[4] = 10;
for (int i = 0; i < 5; i++) {
printf("%d ", i_arr2[i]);
}
printf("\n");
printf("\n");
//二維陣列,行size一定要寫
float f_arr[][3] = { // f_arr[列][行]
{1.1, 1.2, 1.3},
{2.1, 2.2, 2.3},
{3.1, 3.2, 3.3}
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%f ", f_arr[i][j]);
}
printf("\n");
}
printf("\n");
}
```
```
//output
1 2 3 4 5
6 7 8 9 10
1.100000 1.200000 1.300000
2.100000 2.200000 2.300000
3.100000 3.200000 3.300000
Program ended with exit code: 0
```
## String : 字元陣列
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
char str[] = "Hello World!";
// str 實際上是由13個字元組成的字串 -> H,e,l,l,o, ,W,o,r,l,d,!,\0
printf("%zu\n", sizeof(str));
printf("%s\n", str);
char str2[][4] = {
"cat", // c, a, t, \0
"dog", // d, o, g, \0
"pig" // p, i, g, \0
};
printf("%s %s %s\n", str2[0], str2[1], str2[2]);
}
//output
13
Hello World!
cat dog pig
Program ended with exit code: 0
```
# 輸入輸出
## scanf & printf
### scanf 的運作邏輯: scanf(”%d%f%c”, &input1, &input2, &input3)
會將輸入的內容以string的格式儲存在緩衝記憶體 (Buffer) 中,在依對應的轉換詞(eg. %d, %f, %c, etc.),存入指定的記憶體位置。
### printf 的運作邏輯: printf(”%d”, input1)
會將 %d 替換成 input 的內容,再以 string 的格式存入緩衝記憶體 (Buffer) 中,並打印出來。也就是說打印出來的內容皆是string的型態。
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
int input;
scanf("%d", &input); // 輸入並指派給 input
printf("output = %d\n", input); // 輸出
int input2, input3, input4;
scanf("%d%d%d", &input2, &input3, &input4);
printf("output = %d %d %d\n", input2, input3, input4);
// 輸入/輸出字串
char input5[10];
scanf("%s", input5); // 不需加&,另外,輸入空白鍵會被當作是\0
printf("output = %s\n", input5);
//print integer.
int a = 100;
printf("%d\n", a);
// print long integer.
long b = 100;
printf("%ld\n", b);
// print %md, m -> 含 a 本身共 m 格,a 前補 space。
printf("%20d\n", a);
// print float number.
float c = 3.141592;
printf("%f\n", c);
// print %mf, m -> Round float number to m place.
printf("%.4f\n", c);
}
```
```
//output
10
output = 10
11 12 13
output = 11 12 13
Hello World
output = Hello
100
100
100
3.141592
3.1416
Program ended with exit code: 0
```
## sscanf
### sscanf 的運作邏輯: scanf(自定義的儲存空間<string>, ”%d%f%c”, &input1, &input2, &input3)
自行宣告字串來取代掉 scanf 使用的 buffer,也就不用在程式執行時才輸入input。
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
int input1;
float input2;
char input3[4];
char input[20] = "10 3.14 abc";
sscanf(input, "%d%f%s", &input1, &input2, input3);
printf("%d, %f, %s\n", input1, input2, input3);
}
//output
10, 3.140000, abc
Program ended with exit code: 0
```
## sprintf
### sprintf 的運作邏輯: sprintf(自定義的儲存空間<string>, ”%d%f%c”, &input1, &input2, &input3)
自行宣告字串來取代掉 printf 使用的 buffer,將要列印出的內容存入自行宣告的字串中。且不會列印出來。
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
float f_1 = 3.14;
char output[20];
sprintf(output, "out = %f", f_1);
printf("%s\n", output); //印出output字串的內容
}
//output
out = 3.140000
Program ended with exit code: 0
```
## gets
s → string (已不再使用,因為gets沒有辦法限制輸入的長度,這可能導致緩衝區溢出,從而引發安全漏洞。)
### gets 與 scanf 的功能類似,但是gets是可以輸入空格的!!
## fgets
char *fgets(char *str, int n, FILE *stream)
### fgets 功能與 gets 相似,皆可輸入空格,差別在 fgets 有限制輸入 n 個字數的機制(較安全),且在輸入完按下 Enter 後會多一個 ‘\n’ 字元。
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
printf("輸入字串:");
char str[10];
fgets(str, 10, stdin); //stdin: 從鍵盤輸入
printf("輸出字串:%s\n", str);
printf("%c, %d, %d\n", str[6], str[7], str[8]);
// str[7] 會輸出 10 對應到 ASCII table 為換行鍵,即 Enter 鍵
// 所以 str: a, b, c, , d, e, f, \n, \0
}
//output
輸入字串:abc def
輸出字串:abc def
f, 10, 0
Program ended with exit code: 0
```
## puts
### puts 與 printf 的功能類似,差別在於puts會自動換行,即printf(”\n”)的效果。
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
char str1[] = "I love coding.";
char str2[] = "And you?";
printf("printf的效果:\n");
printf("%s", str1);
printf("%s", str2);
printf("\nputs的效果:\n");
puts(str1);
puts(str2);
}
\\output
printf的效果:
I love coding.And you?
puts的效果:
I love coding.
And you?
Program ended with exit code: 0
```
## fputs
int fputs(const char *str, FILE *stream)
### fputs 功能與 puts 相似,差別在 fputs 不自帶換行。
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
char str1[] = "I love coding.";
char str2[] = "And you?";
fputs(str1, stdout); // stdout: 印到螢幕上
fputs(str2, stdout);
printf("\n----------------------\n");
puts(str1);
puts(str2);
}
```
```
//output
I love coding.And you?
----------------------
I love coding.
And you?
Program ended with exit code: 0
```
## getc / putc
c → char
int getc(FILE *stream) / int putc(int char, FILE *stream)
### getc()只接受一個字元,
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
char c;
c = getc(stdin);
putc(c, stdout); // 印出的是字元
printf("\n字元 %c 對應到ASCII code: %d\n", c, c);
}
```
```
//output
5
5
字元 5 對應到ASCII code: 53
Program ended with exit code: 0
```
## getchar() / putchar()
getchar() = int getc(stdin) / putchar(int char) = int putc(int char, stdout)
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
char c;
// c = getc(stdin);
c = getchar();
// putc(c, stdout);
putchar(c);
printf("\n字元 %c 對應到ASCII code: %d\n", c, c);
}
```
```
//output
5
5
字元 5 對應到ASCII code: 53
Program ended with exit code: 0
```
# 數學/邏輯運算式
+ , - , * , / , % , & , | , ^ , ~ , << , >>
注意:浮點數不能取餘數 (%)
bit操作術語:pull hight: 0 → 1 / pull down: 1 → 0
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
unsigned short hex = 0x55AA;
// 將 hex 的 bit_9 pull hight
unsigned short bit_9 = 0x01 << 9;
hex = hex | bit_9;
printf("result = %x\n", hex); // 57aa
// 再將 hex 還原成 55AA ,及對 hex 的 bit_9 pull down
hex = hex & ~bit_9;
printf("result = %x\n", hex); // 55aa
}
//output
result = 57aa
result = 55aa
Program ended with exit code: 0
```
# 條件運算式
>, >=, <, <=, ==, !=, !, &&, ||
## 布林值
```c
#include <stdbool.h>
```
| state | value | |
| ----- | ----- | --- |
| True | 1 | |
| False | 0 | s |
## C 語言運算子優先權重表

1. a-b+c 中 - 和 + 同為優先權 4, 結合順序為 '左至右' 所以是 (a-b)+c 而不是 a-(b+c)
2. a->b.c 中 -> 和 . 同為優先權 1, 結合順序為 '左至右' 所以是 (a->b).c而不是 a->(b.c)
3. a=b=10 中兩個 = 優先權相同, 結合順序為 '右至左' 所以是 a= (b=10), 而 b=10 的回傳值是 10, 所以 a,b 都會被設成 10 (指定運算的回傳值為等號右邊的運算結果)
[Source](https://magicjackting.pixnet.net/blog/post/70902861)
## 判斷式
### if else
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
int age;
scanf("%d", &age);
if (age <= 0 || age > 100) {
printf("請輸入正確的年齡\n");
}
else if (age < 13) {
printf("兒童票\n");
}
else if (age >= 13 && age < 65) {
printf("成人票\n");
}
else {
printf("敬老票\n");
}
return 0;
}
```
### switch case
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
int age;
scanf("%d", &age);
switch (age) {
case -10000 ... 0:
case 101 ... 9999:
printf("請輸入正確的年齡\n");
break; // 一般情況不可省略
case 1 ... 12: // 1 ~ 12
printf("兒童票\n");
break; // 一般情況不可省略
case 13 ... 64: // 13 ~ 64
printf("成人票\n");
break; // 一般情況不可省略
default: // other
printf("敬老票\n");
break; // 一般情況可省略,因為程式在進入到default時,也是準備要結束switch case
}
return 0;
}
```
# Loop
## while
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
// 印九九乘法表
int i = 1;
int j = 1;
while (i <= 9) {
while (j <= 9) {
printf("%d * %d = %2d ", j, i, j * i);
j++;
}
j = 1;
i++;
printf("\n");
}
return 0;
}
//output
1 * 1 = 1 2 * 1 = 2 3 * 1 = 3 4 * 1 = 4 5 * 1 = 5 6 * 1 = 6 7 * 1 = 7 8 * 1 = 8 9 * 1 = 9
1 * 2 = 2 2 * 2 = 4 3 * 2 = 6 4 * 2 = 8 5 * 2 = 10 6 * 2 = 12 7 * 2 = 14 8 * 2 = 16 9 * 2 = 18
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9 4 * 3 = 12 5 * 3 = 15 6 * 3 = 18 7 * 3 = 21 8 * 3 = 24 9 * 3 = 27
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16 5 * 4 = 20 6 * 4 = 24 7 * 4 = 28 8 * 4 = 32 9 * 4 = 36
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25 6 * 5 = 30 7 * 5 = 35 8 * 5 = 40 9 * 5 = 45
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36 7 * 6 = 42 8 * 6 = 48 9 * 6 = 54
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49 8 * 7 = 56 9 * 7 = 63
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64 9 * 8 = 72
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
Program ended with exit code: 0
```
```c
// 無窮迴圈寫法:
while(1) {
printf("in loop\n");
}
```
## for
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
// 印九九乘法表
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= 9; j++) {
printf("%d * %d = %2d ", j, i, j*i);
}
printf("\n");
}
return 0;
}
//output
1 * 1 = 1 2 * 1 = 2 3 * 1 = 3 4 * 1 = 4 5 * 1 = 5 6 * 1 = 6 7 * 1 = 7 8 * 1 = 8 9 * 1 = 9
1 * 2 = 2 2 * 2 = 4 3 * 2 = 6 4 * 2 = 8 5 * 2 = 10 6 * 2 = 12 7 * 2 = 14 8 * 2 = 16 9 * 2 = 18
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9 4 * 3 = 12 5 * 3 = 15 6 * 3 = 18 7 * 3 = 21 8 * 3 = 24 9 * 3 = 27
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16 5 * 4 = 20 6 * 4 = 24 7 * 4 = 28 8 * 4 = 32 9 * 4 = 36
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25 6 * 5 = 30 7 * 5 = 35 8 * 5 = 40 9 * 5 = 45
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36 7 * 6 = 42 8 * 6 = 48 9 * 6 = 54
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49 8 * 7 = 56 9 * 7 = 63
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64 9 * 8 = 72
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
Program ended with exit code: 0
```
```c
// 無窮迴圈寫法:
for(;;) {
printf("in loop\n");
}
```
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
// for迴圈特殊寫法:
int i, j, k;
for (i = 0, j = 0, k = 0; k <= 5; i++, j++, k++) {
printf("%2d %2d %2d\n", i, j, k);
}
return 0;
}
//output
0 0 0
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
Program ended with exit code: 0
```
## pooling array
### 1D Array
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
int int_array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i = 0; i < sizeof(int_array) / sizeof(int); i++) {
printf("%d, ", int_array[i]);
}
printf("\n");
return 0;
}
// output
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
Program ended with exit code: 0
```
```c
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
char str[] = "Hello World!";
printf("str的長度(含 \\0):%zu\n", sizeof(str) / sizeof(char));
printf("str的長度(不含 \\0):%zu\n", strlen(str));
// 法一
for (int i = 0; i < strlen(str); i++) {
printf("%c, ", str[i]);
}
printf("\n");
// 法二
for (int i = 0; str[i] != 0; i++) { // 因為字串最後一個字元都是0
printf("%c, ", str[i]);
}
printf("\n");
return 0;
}
//output
str的長度(含 \0):13
str的長度(不含 \0):12
H, e, l, l, o, , W, o, r, l, d, !,
H, e, l, l, o, , W, o, r, l, d, !,
Program ended with exit code: 0
```
### 2D Array
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
int int_2Darray[][10] = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
{21, 22, 23, 24, 25, 26, 27, 28, 29, 30},
};
int col_size = sizeof(int_2Darray[0]) / sizeof(int);
int row_size = (sizeof(int_2Darray) / sizeof(int)) / col_size;
printf("col_size = %d, row_size = %d\n", col_size, row_size);
for (int i = 0; i < row_size; i++) {
for (int j = 0; j < col_size; j++) {
printf("%2d, ", int_2Darray[i][j]);
}
printf("\n");
}
return 0;
}
//output
col_size = 10, row_size = 3
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
Program ended with exit code: 0
```
```c
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
char str2D[][11] = {
"JIMMY HSU",
"JEREMY LIN",
"WEBBER SU",
};
int col_size = sizeof(str2D[0]) / sizeof(char);
int row_size = sizeof(str2D) / sizeof(char) / col_size;
printf("col_size = %d, row_size = %d\n", col_size, row_size);
// 法一:多印了空格
for (int i = 0; i < row_size; i++) {
for (int j = 0; j < col_size; j++) {
printf("%c, ", str2D[i][j]);
}
printf("\n");
}
printf("--------------------------------\n");
// 法二
for (int i = 0; i < row_size; i++) {
for (int j = 0; j < strlen(str2D[i]); j++) {
printf("%c, ", str2D[i][j]);
}
printf("\n");
}
return 0;
}
//output
col_size = 11, row_size = 3
J, I, M, M, Y, , H, S, U, , ,
J, E, R, E, M, Y, , L, I, N, ,
W, E, B, B, E, R, , S, U, , ,
--------------------------------
J, I, M, M, Y, , H, S, U,
J, E, R, E, M, Y, , L, I, N,
W, E, B, B, E, R, , S, U,
Program ended with exit code: 0
```
## do while
## goto
```c
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
int a = 1, b = 2;
if (a > b) goto point1;
else goto point2;
point1:
printf("point1:a > b\n");
return 0;
point2:
printf("point2:a <= b\n");
return 0;
}
//output:
point2:a <= b
Program ended with exit code: 0
```
### goto 迴圈應用
```c
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
int i = 0;
LOOP:
printf("%d\n", i);
if (i < 5) {
i++;
goto LOOP;
}
}
//output
0
1
2
3
4
5
Program ended with exit code: 0
```
# Function
```c
#include <stdio.h>
#include <string.h>
void sayHi(char object[][50], int size) {
for (int i = 0; i < size; i++) {
printf("Hi, %s.I'm your dad!\n", object[i]);
}
}
int main(int argc, const char * argv[]) {
char friends[][50] = {
"Jimmy Xu",
"Jeremy Lin",
"Webber Su",
"Jack Liu"
};
int arr_size = sizeof(friends) / sizeof(char) / (sizeof(friends[0]) / sizeof(char));
sayHi(friends, arr_size);
return 0;
}
//output
Hi, Jimmy Xu.I'm your dad!
Hi, Jeremy Lin.I'm your dad!
Hi, Webber Su.I'm your dad!
Hi, Jack Liu.I'm your dad!
Program ended with exit code: 0
```
```c
#include <stdio.h>
#include <stdbool.h>
bool isBigger(float a, float b){
return a > b;
}
int main(int argc, const char * argv[]) {
printf("%d\n", isBigger(10.5, 20));
return 0;
}
//output
0
Program ended with exit code: 0
```
# Library (header檔)
>#include <...> 用於新增系統目錄下的header檔,而#include "..."用於新增檔案目錄底下的header檔(自創的library)。
## 創建標頭檔和函式庫

main.c (使用library中的function)
```c
#include <stdio.h>
#include "myLibrary.h"
int main(int argc, const char * argv[]) {
char friends[][50] = {
"Jimmy Xu",
"Jeremy Lin",
"Webber Su",
"Jack Liu"
};
int arr_size = sizeof(friends) / sizeof(char) / (sizeof(friends[0]) / sizeof(char));
sayHi(friends, arr_size);
printf("%d\n", isBigger(10.5, 20));
}
//output
Hi, Jimmy Xu.I'm your dad!
Hi, Jeremy Lin.I'm your dad!
Hi, Webber Su.I'm your dad!
Hi, Jack Liu.I'm your dad!
0
Program ended with exit code: 0
```
myLibrary.h (負責宣告有哪些function)
```c
#ifndef myLibrary_h
#define myLibrary_h
#include <stdbool.h>
void sayHi(char object[][50], int size);
bool isBigger(float a, float b);
#endif /* myLibrary_h */
```
myLibraby.c (負責實作myLibrary.h中的function)
```c
#include <stdio.h>
#include "myLibrary.h"
void sayHi(char object[][50], int size) {
for (int i = 0; i < size; i++) {
printf("Hi, %s.I'm your dad!\n", object[i]);
}
}
bool isBigger(float a, float b){
return a > b;
}
```
## 靜態/動態函式庫
靜態函式庫(static library)
: 把Library包成一個檔案,檔案容量大。
動態函式庫(dynamic library)
: 把Library包成額外的檔案,執行時與執行檔一起執行,較省空間。

# 巨集(Macro) #define
:::warning
注意:巨集不是變數,在執行期間不會被改變。當我們用 gcc 或 cl (Microsoft 開發工具裡頭的 C 編譯器) 編譯給定的 C 程式時,會呼叫 cpp (伴隨在 gcc 專案的 C preprocessor) 一類的程式,先行展開巨集 (macro) 或施加條件編譯等操作,再來才會出動真正的 C 語言編譯器 (在 gcc 中叫做 cc1)。
> 參考:[你所不知道的 C 語言:前置處理器應用篇](https://hackmd.io/@sysprog/c-preprocessor)
:::
```c
#include <stdio.h>
#define MAX_SIZE 10
#define ADD(x) (x + 1)
#define SUB_without_brackets(a, b) a - b // 括號陷阱
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main(int argc, const char * argv[]) {
char students[MAX_SIZE];
printf("%zu\n", sizeof(students)); // 10
printf("%d\n", ADD(8)); // 9
printf("%d\n", SUB_without_brackets(21, 9)); // 12
printf("%d\n", MAX(21, 9)); // 21
// 括號陷阱
int res = 2 * SUB_without_brackets(20, 10) / 4; // 2 * 20 - 10 / 4 = 38
printf("%d\n", res); // 38
}
//output
10
9
12
38
21
Program ended with exit code: 0
```
>\## 相連符號
```c
#include <stdio.h>
#define A(x) Value_##x
#define B(x, y) x##y
int main(int argc, const char * argv[]) {
int A(1) = 9;
int A(a) = 21;
printf("%d, %d\n", Value_1, Value_a);
int B(a, 1) = 21;
int B(b, 1) = 9;
printf("%d, %d\n", a1, b1);
}
//output
9, 21
21, 9
Program ended with exit code: 0
```
> \# 引入字串
```c
#include <stdio.h>
#define A(x) #x
int main(int argc, const char * argv[]) {
char str[] = A(Hello World!);
printf("%s\n", str);
}
//output
Hello World!
Program ended with exit code: 0
```
> \ 換行要加的
```c
#include <stdio.h>
#define COMPARE(a, b, ans) \
if (a > b) ans = 1; \
else if (a < b) ans = 0; \
else ans = -1;
int main(int argc, const char * argv[]) {
int res;
int a = 21;
int b = 9;
COMPARE(a, b, res);
printf("%d\n", res);
}
//output
1
Program ended with exit code: 0
```
## 巨集判斷式
`#if` `#elif` `#else` `#endif`
```c
#include <stdio.h>
#define SWITCH 0
int main(int argc, const char * argv[]) {
// 反灰的部分等同於註解
#if SWITCH == 0
printf("offical_mode\n");
#elif SWITCH == 1
printf("develop_mode\n");
#else
printf("test_mode\n");
#endif
}
//output
offical_mode
Program ended with exit code: 0
```
`#ifdef` `#ifndef`
```c
#include <stdio.h>
#define MAJOR "CSIE"
int main(int argc, const char * argv[]) {
// 反灰的部分等同於註解
#ifdef MAJOR
printf("You have defined MAJOR!\n");
#else
printf("You haven't defined MAJOR!\n");
#endif
#ifndef SUBJECT
printf("You haven't defined SUBJECT!\n");
#else
printf("You have defined SUBJECT!\n");
#endif
}
//output
You have defined MAJOR!
You haven't defined SUBJECT!
Program ended with exit code: 0
```
# 別名 typedef
>就是把資料型別重新取名字
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned char uint8; //char -> 1 byte -> 8 bits
typedef unsigned short uint16; // short -> 2 bytes -> 16 bits
typedef unsigned int uint32; // int -> 4 btyes -> 32 bits
typedef unsigned long long uint64; // long long -> 8 bytes -> 64 bits
typedef char* String; // 字元陣列
#define STRING_COPY(s, x) strcpy(s, #x)
int main(int argc, const char * argv[]) {
uint8 a = 255;
printf("uint8_MAX = %u\n", a);
uint16 b = 65535;
printf("uint16_MAX = %u\n", b);
uint32 c = 4294967295;
printf("uint32_MAX = %u\n", c);
uint64_t d = 9223372036854775807;
printf("uint64_MAX = %llu\n", d);
String s = (String)malloc(50);
STRING_COPY(s, Hello World!); // strcpy(s, "Hello World!");
printf("%s\n", s);
}
//output
uint8_MAX = 255
uint16_MAX = 65535
uint32_MAX = 4294967295
uint64_MAX = 9223372036854775807
Hello World!
Program ended with exit code: 0
```
# 結構體 struct
### struct 宣告、初始化 (assignment)
```C
#include <stdio.h>
struct Student {
char name[50];
char major[30];
int age;
};
void show_student(struct Student student) {
printf("Name: %s\n", student.name);
printf("Major: %s\n", student.major);
printf("Age: %d\n", student.age);
}
int main(int argc, const char * argv[]) {
struct Student Jimmy = {"Jimmy", "Computer Science", 22}; // struct init
show_student(Jimmy);
printf("------------------------------\n");
// 對 struct 單一元素初始化
struct Student Jeremy = {.name = "Jeremy"};
show_student(Jeremy);
}
//output
Name: Jimmy
Major: Computer Science
Age: 22
------------------------------
Name: Jeremy
Major:
Age: 0
Program ended with exit code: 0
```
## struct 巢狀結構:struct 裡再放 struct
```c
#include <stdio.h>
#include <string.h>
struct Student {
char name[50];
char major[30];
int age;
};
struct School {
char name[10];
float PR;
struct Student student;
};
void show_school(struct School school) {
printf("School: %s\n", school.name);
printf("PR: %f\n", school.PR);
printf("Student: %s\n", school.student.name);
printf("Major: %s\n", school.student.major);
printf("Age: %d\n", school.student.age);
}
int main(int argc, const char * argv[]) {
struct School NCKU = {"NCKU", 91.5, {"Jimmy", "Computer Science", 22}}; // struct init
show_school(NCKU);
/*NCKU.student.name = "Jeremy";*/ // error
strcpy(NCKU.student.name, "Jeremy");
printf("-------------------------\n");
show_school(NCKU);
}
//output
School: NCKU
PR: 91.500000
Student: Jimmy
Major: Computer Science
Age: 22
-------------------------
School: NCKU
PR: 91.500000
Student: Jeremy
Major: Computer Science
Age: 22
Program ended with exit code: 0
```
:::warning
注意:字串只能在初始化時用 assign 的方式賦值,否則皆須用 strcpy() 來更改。
:::
## struct 簡寫
```c
#include <stdio.h>
#include <string.h>
typedef struct {
char name[50];
char major[30];
int age;
} Student;
typedef struct {
char name[10];
float PR;
Student student;
} School;
int main(int argc, const char * argv[]) {
School NCKU = {"NCKU", 91.5, {"Jimmy GAY", "Computer Science", 22}}; // struct init
}
```
## struct size 計算
:::info
Compiler為了效能考量,會自動做最佳化,也就是資料對齊。
嚴格遵守以下步驟:
1. 從 struct 中選出最大的型別之 size 為單位。
2. struct members 由上而下的擺放進記憶體中。
3. 剩餘空間用 padding byte 補滿。
:::
```c
#include <stdio.h>
typedef struct {
int a;
char b;
char c;
} Size1;
typedef struct {
char b;
int a;
char c;
} Size2;
int main(int argc, const char * argv[]) {
printf("Size1: %zu bytes\nSize2: %zu bytes\n", sizeof(Size1), sizeof(Size2));
}
//output
Size1: 8 bytes
Size2: 12 bytes
Program ended with exit code: 0
```

:::info
橘色方塊的部分就是 padding bytes.
:::
```c
#include <stdio.h>
typedef struct {
char b;
char c;
double a;
} Size3;
typedef struct {
char b;
double a;
char c;
} Size4;
int main(int argc, const char * argv[]) {
printf("Size3: %zu bytes\nSize4: %zu bytes\n", sizeof(Size3), sizeof(Size4));
}
//output
Size3: 16 bytes
Size4: 24 bytes
Program ended with exit code: 0
```

# BitFields
```c
#include <stdio.h>
typedef struct{
unsigned char bit0: 1; // 控制 1 個 bit
unsigned char bit1: 1;
unsigned char bit2: 1;
unsigned char bit3: 1;
unsigned char bit4: 1;
unsigned char bit5: 1;
unsigned char bit6: 1;
unsigned char bit7: 1;
} BitFieldsChar;
int main(int argc, const char * argv[]) {
// 對 char_8 的每一個 bit 初始化
BitFieldsChar char_8 = {0, 0, 0, 0, 0, 0, 0, 0};
// 將第 4 個 bit 設成 1 (即 char_8 -> 00001000 = 8)
char_8.bit3 = 1;
printf("%d\n", *((unsigned char*)&char_8));
}
//output
8
Program ended with exit code: 0
```
# 共用體 union
:::info
語法與 struct 相同,但運作邏輯不同。union 的記憶體是共用的。如下圖所示:
因此,不論初始化 union 中的哪一個元素,都**只會有一個元素被儲存**,使 union 能夠**支援多種不同的資料型別(多型)**。
:::
```c
#include <stdio.h>
typedef union{
int a;
float b;
char c;
} Sample;
int main(int argc, const char * argv[]) {
Sample s = {65, 15.3, 'a'};
printf("%d %f %c\n", s.a, s.b, s.c);
}
//output
65 0.000000 A
Program ended with exit code: 0
```
## union size 計算
:::info
取 union 中 size 最大的元素作為 union 的 size。
:::
```c
#include <stdio.h>
typedef union{
int a; // 4 bytes
float b; // 4 bytes
char c; // 1 bytes
} Size1; // -> 4 bytes
typedef union{
int a; // 4 bytes
double b; // 8 bytes
char c; // 1 bytes
} Size2; // -> 8 bytes
int main(int argc, const char * argv[]) {
printf("Size1 = %zu\nSize2 = %zu\n", sizeof(Size1), sizeof(Size2));
}
//output
Size1 = 4
Size2 = 8
Program ended with exit code: 0
```
## Union 實現多型
```c
#include <stdio.h>
#include <string.h>
typedef union{
int a;
float b;
char c[20];
} Polymorphism; // -> Size = 20 bytes
int main(int argc, const char * argv[]) {
Polymorphism p;
p.a = 10;
printf("%d\n", p.a);
p.b = 15.3;
printf("%f\n", p.b);
strcpy(p.c, "Hello World!");
printf("%s\n", p.c);
}
//output
10
15.300000
Hello World!
Program ended with exit code: 0
```
## Union 應用
### 控制 Byte
```c
#include <stdio.h>
#include <string.h>
typedef union{
unsigned int total;
char detail[4]; // id, majorNum, score, clubNum
} Student; // Size -> 4 Bytes
int main(int argc, const char * argv[]) {
Student Jimmy;
Jimmy.detail[0] = 9;
Jimmy.detail[1] = 21;
Jimmy.detail[2] = 90;
Jimmy.detail[3] = 7;
printf("%d\n", Jimmy.total);
}
//output
123344137
Program ended with exit code: 0
```

### union + struct 控制 Byte
```c
#include <stdio.h>
#include <string.h>
typedef union{
unsigned int total;
struct {
char id;
char majorNum;
char score;
char clubNum;
};
} Student; // Size -> 4 Bytes
int main(int argc, const char * argv[]) {
Student Jimmy;
Jimmy.id = 9;
Jimmy.majorNum = 21;
Jimmy.score = 90;
Jimmy.clubNum = 7;
printf("%d\n", Jimmy.total);
}
//output
123344137
Program ended with exit code: 0
```
### union + struct 控制 Bits
```c
#include <stdio.h>
#include <string.h>
typedef union {
char header;
struct {
unsigned char bit0: 1;
unsigned char bit1: 1;
unsigned char bit2: 1;
unsigned char bit3: 1;
unsigned char bit4: 1;
unsigned char bit5: 1;
unsigned char bit6: 1;
unsigned char bit7: 1;
};
} ControlBits;
int main(int argc, const char * argv[]) {
ControlBits cb;
cb.header = 0; // init
// cb.header |= 0x01 << 3;
cb.bit3 = 1;
printf("pull high bit3 -> %d\n", *((unsigned char*)&cb.header));
// cb.header &= ~(0x01 << 3);
cb.bit3 = 0;
printf("then, pull down bit3 -> %d\n", *((unsigned char*)&cb.header));
}
//output
pull high bit3 -> 8
then, pull down bit3 -> 0
Program ended with exit code: 0
```
# 枚舉 enum
:::info
程式碼要盡量避免使用缺乏解釋或命名的數值,又稱Magic Number,而 enum 通常就是用來取代 Magic Number,目的是為了**增加程式碼的可讀性**。
:::
```c
#include <stdio.h>
typedef enum {
Monday, // 0
Tuesday, // 1
Wednesday, // 2
Thursday = 10, // 以下的枚舉會從 10 開始累加
Friday, // 11
Saturday, // 12
Sunday, // 13
} DAY;
int main(int argc, const char * argv[]) {
DAY today = Sunday;
printf("Today is Sunday: %d\n", today);
printf("%d\n", Wednesday);
printf("%d\n", Thursday);
printf("%d\n", Friday);
}
//output
Today is Sunday: 13
2
10
11
Program ended with exit code: 0
```
```c
#include <stdio.h>
typedef enum {
Monday, // 0
Tuesday, // 1
Wednesday, // 2
Thursday, // 3
Friday, // 4
Saturday, // 5
Sunday, // 6
MAX, // 7 統計共有多少枚舉
} DAY;
int main(int argc, const char * argv[]) {
char TODO[][30] = {
"go swimming",
"play basketball",
"play volleyball",
"play piano",
"go hiking",
"go on a trip",
"go camping"
};
DAY today = Sunday;
printf("Today: %s\n", TODO[today]);
for (int i = 0; i < MAX; i++) {
printf("%s\n", TODO[i]);
}
}
//output
Today: go camping
go swimming
play basketball
play volleyball
play piano
go hiking
go on a trip
go camping
Program ended with exit code: 0
```
# 指標 pointer
:::info
指標大小: 4 bytes(電腦為 32-bits) 或 8 bytes(電腦為 64-bits),不論什麼型態的指標 size 皆相同。
:::
指標是一個變數,用來**儲存某個記憶體位址**,而這個位址通常是某個變數在記憶體的位置。
## & : 取址運算子 、 * : 宣告指標/取值運算子
```c
#include <stdio.h>
int a = 1; // global variable
void foo(void) {
static int b = 9; // static variable
int c = 21; // local variable
int* a_ptr = &a; // 宣告指標 a_ptr 指向 a 的位址
int* b_ptr = &b;
int* c_ptr = &c;
printf("%p %p %p\n", a_ptr, b_ptr, c_ptr); // 印出指標指向的位址
printf("%p %p %p\n", &a, &b, &c); // 印出 a b c 變數對應的位址
// 印出指標所指位址中的值
printf("%d %d %d\n", *a_ptr, *b_ptr, *c_ptr); // 此時 * 為取值運算子
(*a_ptr) ++; // a++;
(*b_ptr) ++; // b++;
(*c_ptr) ++; // c++;
printf("%d %d %d\n", *a_ptr, *b_ptr, *c_ptr);
}
int main(int argc, const char * argv[]) {
foo();
}
// output
0x100008000 0x100008004 0x16fdff2bc
0x100008000 0x100008004 0x16fdff2bc
1 9 21
2 10 22
Program ended with exit code: 0
```
## Swap 函式
```c
#include <stdio.h>
void swap(int* a, int* b) {
int tmp = *a; // 將位址 a 中的值取出存入 tmp 中
*a = *b; // 再將位址 b 中的值存入位址 a 中
*b = tmp; // 最後,將 tmp 的值存入位址 b 中
}
int main(int argc, const char * argv[]) {
int a = 9;
int b = 21;
printf("Before: a = %d, b = %d\n", a, b);
swap(&a, &b); // 傳入a, b的位址
printf("After: a = %d, b = %d\n", a, b);
}
//output
Before: a = 9, b = 21
After: a = 21, b = 9
Program ended with exit code: 0
```
## 間接指標 ( Pointer to Pointer )
:::info
顧名思義就是用來指著指標的指標
:::
### 透過 Linus Torvalds 在 TED 訪談中於 [14:20](https://youtu.be/o8NPllzkFhE?t=859) 時,提到程式設計 "good taste" 的例子,來了解間接指標。
原本的程式碼
```clike
void remove_list_node(List *list, Node *target)
{
Node *prev = NULL;
Node *current = list->head;
// Walk the list
while (current != target) {
prev = current;
current = current->next;
}
// Remove the target by updating the head or the previous node.
if (!prev)
list->head = target->next;
else
prev->next = target->next;
}
```
有「品味」的版本
```clike
void remove_list_node(List *list, Node *target)
{
// The "indirect" pointer points to the *address*
// of the thing we'll update.
Node **indirect = &list->head;
// Walk the list, looking for the thing that
// points to the node we want to remove.
while (*indirect != target)
indirect = &(*indirect)->next;
*indirect = target->next;
}
```
目的:透過「間接指標」來避免「多」判斷 edge case。

## Pointer size 計算
:::info
無論指標的型別,其 size 皆為 8 bytes (64-bit OS) or 4 bytes (32-bit OS)
:::
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
printf("%zu %zu %zu\n", sizeof(int*), sizeof(char*), sizeof(double*));
}
//output
8 8 8
Program ended with exit code: 0
```
## 記憶體配置

## 動態記憶體配置
:::info
malloc() 創造出的空間會存放在 Heap,直到執行 free() 後該空間才會被釋放。另外,free() 只能對儲存在 Heap 區的資料使用。
:::
### malloc()
```c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
int* pi; // 宣告整數指標 pi
pi = (int*)malloc(sizeof(int)); // pi 指向新配置的動態記憶體位址
*pi = 5; // 於動態記憶體位址中存入5
printf("pi指標存放的記憶體位址:%p\npi動態配置的記憶體位址(即pi的內容):%p\npi指向之記憶體位址中的內容為:%d\n", &pi, pi, *pi);
}
//output
pi指標存放的位址:0x16fdff2c8
pi動態配置的位址(即pi的內容):0x6000021940e0
pi指向之記憶體位址中的內容為:5
Program ended with exit code: 0
```
:::danger
記憶體內容如下所示:
| 記憶體位址 | 存放的內容 | 記憶體區塊 | 變數 |
|:--------------:|:--------------:|:----------:|:----:|
| 0x6000021940e0 | 5 | Heap | *pi |
| 0x16fdff2c8 | 0x6000021940e0 | Stack | pi |
:::
### free()
```c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
int* pi; // 宣告整數指標 pi
pi = (int*)malloc(sizeof(int)); // pi 指向新配置的動態記憶體位址
*pi = 5; // 於動態記憶體位址中存入5
printf("pi指標存放的記憶體位址:%p\tpi動態配置的記憶體位址(即pi的內容):%p\tpi指向之記憶體位址中的內容為:%d\t", &pi, pi, *pi);
// 釋放 pi 所指向的記憶體空間
free(pi);
printf("pi指標存放的記憶體位址:%p\tpi動態配置的記憶體位址(即pi的內容):%p\tpi指向之記憶體位址中的內容為:%d\t", &pi, pi, *pi);
}
// output
pi指標存放的記憶體位址:0x16fdff2c8 pi動態配置的記憶體位址(即pi的內容):0x600000668060 pi指向之記憶體位址中的內容為:5
pi指標存放的記憶體位址:0x16fdff2c8 pi動態配置的記憶體位址(即pi的內容):0x600000668060 pi指向之記憶體位址中的內容為:710836320
Program ended with exit code: 0
```
:::danger
可以發現,雖然 pi 仍指向原本存放 5 的記憶體位址,但其內容已經被存入其他數值了!(被作業系統視為已釋放的記憶體區塊)
| 記憶體位址 | 存放的內容 | 記憶體區塊 | 變數 |
|:--------------:|:--------------:|:----------:|:----:|
| 0x600000668060 | 710836320 | Heap | *pi |
| 0x16fdff2c8 | 0x600000668060 | Stack | pi |
:::
### 常見問題 -- 記憶體洩漏 (Memory leak)
:::info
發生原因:因為程式在動態分配記憶體後沒有正確地釋放它。
在使用 malloc、calloc 或 realloc 等函數分配記憶體時,會得到一個指向某記憶體區域的指標。如果這個指針在使用過程中被覆蓋掉,且原本的記憶體區域沒有被釋放,那麼這部分記憶體就會變成「失去指標的記憶體」,即稱記憶體洩漏。
:::
```c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
// 第一次分配
int* pi = (int*)malloc(sizeof(int));
// 第二次分配,第一次分配的記憶體現在已經沒有指標指向它了
pi = (int*)malloc(sizeof(int));
free(pi); // 只會釋放第二次分配的記憶體,第一次分配的記憶體依然還存在,這就是記憶體洩漏。
}
```
### 常見問題 -- 非法記憶體存取 (segment fault)
#### 存取已被釋放的記憶體空間
```c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
int* pi = (int*)malloc(sizeof(int));
*pi = 5;
free(pi);
*pi = 10; // pi 指向的記憶體空間已被釋放,但又在存取該記憶體空間。
}
```
#### 避免存取已被釋放的記憶體空間之方法
```c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
int* pi = NULL;
if (pi == NULL) { // 若 pi 尚未指向任何記憶體空間
pi = (int*)malloc(sizeof(int)); // 才 malloc 記憶體空間給 pi
}
*pi = 5;
if (pi != NULL) { // 若尚未釋放 pi 所指向之記憶體空間
free(pi); // 則釋放 pi 所指向之記憶體空間
pi = NULL;
}
}
```
### 常見問題 -- 全域指標與靜態指標初始化問題
:::info
全域指標與靜態指標是無法直接初始化的,要先將指標指派為NULL,再初始化。另外,全域指標只能在函數中指派。
:::
#### 全域指標、靜態指標的初始化與釋放 (雙重指標)
```c
#include <stdio.h>
#include <stdlib.h>
int* global = NULL;
void pointer_init(int** p) {
if (*p == NULL) {
*p = (int*)malloc(sizeof(int));
}
printf("pointer_address: %p\n", *p);
}
void pointer_free(int** p) {
if (*p != NULL && p != NULL) {
free(*p);
*p = NULL;
}
printf("pointer_address: %p\n", *p);
}
int main(int argc, const char * argv[]) {
pointer_init(&global);
pointer_init(&global); // 與第一次init的位址一樣
pointer_free(&global);
static int* s_pointer = NULL;
pointer_init(&s_pointer);
pointer_init(&s_pointer); // 與第一次init的位址一樣
pointer_free(&s_pointer);
}
//output
pointer_address: 0x600000e94080
pointer_address: 0x600000e94080
pointer_address: 0x0
pointer_address: 0x600000e94080
pointer_address: 0x600000e94080
pointer_address: 0x0
Program ended with exit code: 0
```
## 指標與陣列
:::info
陣列名稱即為指向該陣列首位元素之指標。
:::
### 一維陣列
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
int arr[] = {1, 2, 3, 4, 5};
// arr[1] 即 *(arr + 1)
printf("%d\n", arr[1]);
printf("%d\n", *(arr + 1));
// &arr[1] 即 arr + 1
printf("%p\n", &arr[1]);
printf("%p\n", arr + 1);
int* arr_ptr = arr;
for (int i = 0; i < 5; i++) {
printf("%d, ", *(arr_ptr + i));
}
printf("\n");
}
// output
2
2
0x16fdff274
0x16fdff274
1, 2, 3, 4, 5,
Program ended with exit code: 0
```
### 二為陣列
```c
#include <stdio.h>
int main(int argc, const char * argv[]) {
int matrix[][3] = {
{1, 2, 3},
{4, 5, 6}
};
// matrix[1][1] 即 *(*(matrix + 1) + 1)
printf("%d\n", matrix[1][1]);
printf("%d\n", *(*(matrix + 1) + 1));
// &matrix[1][1] 即 *(matrix + 1) + 1 或 matrix[1] + 1
printf("%p\n", &matrix[1][1]);
printf("%p\n", *(matrix + 1) + 1);
printf("%p\n", matrix[1] + 1);
int (*matrix_ptr)[3] = matrix; // 宣告二維陣列的指標
int* ptr1D = matrix_ptr[0]; // matrix_ptr[0] 即 matrix[0]
for (int i = 0; i < 6; i++) {
printf("%d, ", *(ptr1D + i));
}
printf("\n");
}
//output
5
5
0x16fdff280
0x16fdff280
0x16fdff280
1, 2, 3, 4, 5, 6,
Program ended with exit code: 0
```
### 利用函式修改陣列內容
```c
#include <stdio.h>
void set_1Darr_val(int* arr, int size, int val) {
for(int i = 0; i < size; i++) {
arr[i] = val;
}
}
void set_2Darr_val(int (*arr)[3], int row, int col, int val) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
arr[i][j] = val;
}
}
}
int main(int argc, const char * argv[]) {
int arr1D[] = {1, 2, 3, 4, 5};
printf("Brfore: %d, %d, %d, %d, %d\n", arr1D[0], arr1D[1], arr1D[2], arr1D[3], arr1D[4]);
set_1Darr_val(arr1D, 5, 21);
printf("After: %d, %d, %d, %d, %d\n", arr1D[0], arr1D[1], arr1D[2], arr1D[3], arr1D[4]);
int arr2D[][3] = {
{1, 2, 3},
{4, 5, 6}
};
printf("Brfore: %d, %d, %d, %d, %d, %d\n", arr2D[0][0], arr2D[0][1], arr2D[0][2], arr2D[1][0], arr2D[1][1], arr2D[1][2]);
set_2Darr_val(arr2D, 2, 3, 21);
printf("After: %d, %d, %d, %d, %d, %d\n", arr2D[0][0], arr2D[0][1], arr2D[0][2], arr2D[1][0], arr2D[1][1], arr2D[1][2]);
}
//output
Brfore: 1, 2, 3, 4, 5
After: 21, 21, 21, 21, 21
Brfore: 1, 2, 3, 4, 5, 6
After: 21, 21, 21, 21, 21, 21
Program ended with exit code: 0
```
### 動態記憶體配置一維陣列
```c
#include <stdio.h>
#include <stdlib.h>
void set_1Darr_val(int* arr, int size, int val) {
for(int i = 0; i < size; i++) {
arr[i] = val;
}
}
int main(int argc, const char * argv[]) {
int* vector1D = (int*)malloc(sizeof(int) * 5);
set_1Darr_val(vector1D, 5, 9);
printf("%d, %d, %d, %d, %d\n", vector1D[0], vector1D[1], vector1D[2], vector1D[3], vector1D[4]);
}
//output
9, 9, 9, 9, 9
Program ended with exit code: 0
```
### 動態記憶體配置二維陣列(不連續位址)
```c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
int row = 2;
int col = 3;
int** vector2D = (int**)malloc(sizeof(int*) * row); // vector2D[row][col]
// 配置各 col
vector2D[0] = (int*)malloc(sizeof(int) * col);
// *(vector2D + 0) = (int*)malloc(sizeof(int) * col);
vector2D[1] = (int*)malloc(sizeof(int) * col);
// *(vector2D + 1) = (int*)malloc(sizeof(int) * col);
// print
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%p, ", &vector2D[i][j]);
}
printf("\n");
}
}
//output (第一列到第二列的位址沒有連續)
0x6000034cc090, 0x6000034cc094, 0x6000034cc098,
0x6000034cc0a0, 0x6000034cc0a4, 0x6000034cc0a8,
Program ended with exit code: 0
```
### 動態記憶體配置二維陣列(連續位址)
```c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
int row = 3;
int col = 3;
int** vector2D = (int**)malloc(sizeof(int*) * row); // vector2D[row][col]
*(vector2D) = (int*)malloc(sizeof(int) * row * col);
for (int i = 0; i < row; i++) {
// *(vector2D + i) = *(vector2D) + col * i;
vector2D[i] = vector2D[0] + col * i;
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%p, ", &vector2D[i][j]);
}
printf("\n");
}
}
// output
0x600000f14450, 0x600000f14454, 0x600000f14458,
0x600000f1445c, 0x600000f14460, 0x600000f14464,
0x600000f14468, 0x600000f1446c, 0x600000f14470,
Program ended with exit code: 0
```
### 利用函式動態記憶體配置一維陣列
#### <方法一>:
```c
#include <stdio.h>
#include <stdlib.h>
void setArrayValue(int *arr, int size, int value) {
for (int i = 0; i < size; i++) {
arr[i] = value;
}
}
int *mallocArray(int size) {
int *arr = (int *)malloc(sizeof(int) * size);
return arr;
}
int main() {
int *arr = mallocArray(5);
setArrayValue(arr, 5, 9);
for (int i = 0; i < 5; i++) {
printf("%d\n", arr[i]);
}
}
```
#### <方法二>:
```c
#include <stdio.h>
#include <stdlib.h>
void setArrayValue(int *arr, int size, int value) {
for (int i = 0; i < size; i++) {
arr[i] = value;
}
}
void mallocArray(int **arr, int size) {
*arr = (int *)malloc(sizeof(int) * size);
}
int main() {
int *array1D = NULL;
mallocArray(&array1D, 5);
setArrayValue(array1D, 5, 21);
for (int i = 0; i < 5; i++) {
printf("%d\n", array1D[i]);
}
}
```
:::info
在上述程式碼中,arr 存的內容會是 &array1D (array1D的位址),所以 *arr 就會是 array1D,再透過 *arr 來修改 array1D 的內容。而傳入 `mallocArray` 的參數為一個陣列,又陣列名 array1D 同為指標,故在 `mallocArray` 函數中需使用 ==指標的指標(**)== 來傳遞參數。
:::danger
| 變數 | 記憶體位址 | 存放的內容 | 註 |
|:-----------------:|:----------:|:----------:|:------------:|
| arr(指標的指標) | 0xb0b8 | 0xb0f8 | 指向 array1D 的指標 |
| *arr | 0xb0f8 | 0x0 | array1D 的替身 |
| array1D | 0xb0f8 | 0x0 |array1D 本身就是整數指標|
:::
### 利用函式動態記憶體配置二維陣列 (不連續位址)
#### <方法一>:
```c
#include <stdio.h>
#include <stdlib.h>
void set2DarrayValue(int **arr, int row, int col, int value) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
arr[i][j] = value;
}
}
}
int** malloc2Darray(int row, int col) {
int** arr = (int **)malloc(sizeof(int) * row);
for (int i = 0; i < row; i++) {
arr[i] = (int *)malloc(sizeof(int) * col);
}
return arr;
}
int main() {
int row = 2;
int col = 3;
int** arr = malloc2Darray(row, col);
set2DarrayValue(arr, row, col, 21);
// print
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%d ", *(*(arr + i) + j));
}
printf("\n");
}
}
```
#### <方法二>:
```c
#include <stdio.h>
#include <stdlib.h>
void set2DarrayValue(int **arr, int row, int col, int value) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
arr[i][j] = value;
}
}
}
void malloc2Darray(int*** arr, int row, int col) {
*arr = (int **)malloc(sizeof(int) * row);
for (int i = 0; i < row; i++) {
*(*arr + i) = (int *)malloc(sizeof(int) * col);
}
}
int main() {
int row = 2;
int col = 3;
int** arr = NULL;
malloc2Darray(&arr, row, col);
set2DarrayValue(arr, row, col, 21);
// print
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%d ", *(*(arr + i) + j));
}
printf("\n");
}
}
```
## 指標與結構體
### 結構體的指標
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int id;
char name[7];
} Zoo;
int main() {
Zoo z1 = {1, "tiger"};
Zoo* zPtr = &z1;
printf("animal id: %d\nanimal name: %s\n", z1.id, z1.name);
printf("animal id: %d\nanimal name: %s\n", (*zPtr).id, (*zPtr).name);
printf("animal id: %d\nanimal name: %s\n", zPtr->id, zPtr->name);
}
// output
animal id: 1
animal name: tiger
animal id: 1
animal name: tiger
animal id: 1
animal name: tiger
```
### 結構體陣列指標
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int id;
char name[7];
} Zoo;
int main() {
Zoo z[3] = {{1, "Monkey"}, {2, "Donkey"}, {3, "Horse"}};
Zoo* ptr = z;
for (int i = 0; i < sizeof(z) / sizeof(Zoo); i++) {
printf("animal_id : %d, animal_name : %s\n", (*(ptr + i)).id, (*(ptr + i)).name);
printf("animal_id : %d, animal_name : %s\n", (ptr + i)->id, (ptr + i)->name);
printf("animal_id : %d, animal_name : %s\n", ptr[i].id, ptr[i].name);
}
}
// output
animal_id : 1, animal_name : Monkey
animal_id : 1, animal_name : Monkey
animal_id : 1, animal_name : Monkey
animal_id : 2, animal_name : Donkey
animal_id : 2, animal_name : Donkey
animal_id : 2, animal_name : Donkey
animal_id : 3, animal_name : Horse
animal_id : 3, animal_name : Horse
animal_id : 3, animal_name : Horse
```
### 結構體的指標應用
```c
#include <stdio.h>
#include <stdlib.h>
#pragma pack(1)
typedef struct {
int id;
char name[7];
} Zoo;
#pragma pack()
int main() {
unsigned char data[] = {1, 0, 0, 0, 68, 111, 103, 0, 1, 2, 3, 2, 0, 0, 0, 82, 97, 98, 98, 105, 116, 0};
Zoo* ptr = data;
printf("%d, %s\n", ptr->id, ptr->name);
printf("ptr_point : %d\n", ptr);
printf("%d, %s\n", (ptr + 1)->id, (ptr + 1)->name);
printf("ptr_point : %d\n", ptr + 1);
}
// output
1, Dog
ptr_point : 1864872080
2, Rabbit
ptr_point : 1864872091
```
:::info
首先,到底 `#pragma pack(對齊的單位)` 是做什麼的?其實 `#pragma pack(對齊的單位)` 就是要 compiler 照我們的意思去做記憶體 aligment(對齊)的工作,在 32 位元的系統下為了提高記憶體存取效率,所以記憶體配置(對齊)的預設值是以 DWORD(4BYTES 也就是 32 bits )為單位,因此在配置 struct 的記憶體空間的時候,往往會造成 struct 內部的資料在記憶體位址上並不連續,在我們的程式之中,如果不是用指標去存取這些資料,可能還不會出錯,可是如果我們真的用指標去存取的話,由於資料排列不連續,我們可能就會讀不到我們想要的值。
:::


### 指標與 Bitfields 的應用
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
unsigned int bit_1: 2; // 控制最右邊的兩個 Bit
unsigned int bit_2: 1;
unsigned int bit_3: 3;
} Bitfields;
int main() {
int test = 59; // 111011 = 59
Bitfields *bit = &test;
/**
* ---------- test ---------
* | 111 | 0 | 11 |
* ------------------------
* | bit_3 | bit_2 | bit_1 |
* -------------------------
*/
bit->bit_2 = 1; // 111 1 11 = 63
printf("%d\n", test);
bit->bit_3 = 2; // 010 1 11 = 23
printf("%d\n", test);
bit->bit_1 = 2; // 010 1 10 = 22
printf("%d\n", test);
}
```
### 動態記憶體配置結構體
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int d1;
float d2;
} Data;
int main() {
// Create an Array, which datatype is Data.
Data* dataArray = (Data *)malloc(sizeof(Data) * 3);
// Initialize Array
dataArray[0].d1 = 9;
dataArray[0].d2 = 21;
(*(dataArray + 1)).d1 = 8;
(*(dataArray + 1)).d2 = 20;
(dataArray + 2)->d1 = 7;
(dataArray + 2)->d2 = 19;
// print
for (int i = 0; i < 3; i++) {
printf("%d, %f\n", dataArray[i].d1, dataArray[i].d2);
}
}
// output
9, 21.000000
8, 20.000000
7, 19.000000
```
```c
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int d1;
float d2;
} Data;
void mallocData(Data** data) {
*data = (Data*)malloc(sizeof(Data));
}
void setDataValue(Data* data, int a, float b) {
data->d1 = a;
data->d2 = b;
}
int main() {
Data* ptr = NULL;
mallocData(&ptr);
setDataValue(ptr, 9, 21);
printf("%d, %f\n", ptr->d1, ptr->d2);
}
```
```
// output
9, 21.000000
```
### Linked List
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node {
char* name;
int price;
struct Node* next;
} Node_t;
Node_t* createNode(char* name, int price) {
Node_t* i = (Node_t*)malloc(sizeof(Node_t));
i->name = name;
i->price = price;
i->next = NULL;
return i;
}
void deleteNode(Node_t** head, char* name) {
Node_t* cur = *head;
Node_t* pre = NULL;
while (cur) {
if (strcmp(cur->name, name)) { // Not equal
pre = cur;
cur = cur->next;
}
else { // Equal
if (pre) { // cur not i1
pre->next = cur->next;
}
else { // cur is i1
*head = cur->next;
}
printf("Island %s has been delete.\n", cur->name);
free(cur);
return;
}
}
printf("Island %s is not exist!\n", name);
}
void insertNodeAtTail(Node_t **head, char* name, int price) {
Node_t *newData = createNode(name, price);
Node_t *cur = *head;
if (!(*head)) {
*head = newData;
}
while(cur->next) {
cur = cur->next;
}
cur->next = newData;
}
void insertNodeAtHead(Node_t **head, char* name, int price) {
Node_t *newData = createNode(name, price);
newData->next = *head;
*head = newData;
}
void print(Node_t *head) {
Node_t *cur = head;
while (cur) {
printf("%s %d ", cur->name, cur->price);
cur = cur->next;
}
printf("\n");
}
int main() {
Node_t* head = NULL;
Node_t* head2 = NULL;
Node_t* i1 = createNode("i1", 600);
Node_t* i2 = createNode("i2", 900);
Node_t* i3 = createNode("i3", 300);
head = i1;
i1->next = i2;
i2->next = i3;
deleteNode(&head, "i1");
insertNodeAtHead(&head, "i5", 200);
insertNodeAtTail(&head, "i4", 100);
print(head);
insertNodeAtHead(&head2, "i1", 10);
insertNodeAtTail(&head2, "i2", 20);
print(head2);
}
```
```
// output
Island i1 has been delete.
i5 200 i2 900 i3 300 i4 100
i1 10 i2 20
```
# volatile