- Pointer & Array -
陳杰翰 JIElite
error: invalid operands to binary expression ('int *' and 'int *')
printf("p + p2: %p\n", p + p2);
~ ^ ~~
warning: format specifies type 'void *' but the argument has type 'long' [-Wformat]
printf("p - p2: %p\n", p - p2);
~~ ^~~~~~
%ld
eg: *ptr++, *++ptr;
Operator precedence in C:
int a[10];
int *p = &a[0]; ( p 指向 int 變數,所以給予 int 變數的位址 )
圖片取自:C Programming: A Modern Approach, 2nd Edition
int a[10];
int *p = &a[0]; ( p 指向 int 變數,所以給予 int 變數的位址 )
*p = 5;
圖片取自:C Programming: A Modern Approach, 2nd Edition
int a[10];
int *p = &a[2];
int *q = &a[5];
圖片取自:C Programming: A Modern Approach, 2nd Edition
method 1:
p = q + 3;
method 2:
p = p + 6;
我們都知道在宣告的時候,變數前面的 * 用來標記該變數是一個指標變數,像是: int *ptr;
那為什麼要有前面的型別?用來解讀記憶體內容
int *p = &a[0];
p = p + 3;
就會將指標移動到下三個的 int 變數位址,依照指標指向的型別作移動,而不是單純將位址的數值作加減。
指標變數指向 Array 中的任一元素都可以,意思是當前指標對誰做操作。
int *p = &a[8];
p 是什麼? p + 1是什麼?
*p 是什麼? *p + 1 是什麼?
*(p+1) 是什麼?*(p -1) 是什麼?
圖片取自:C Programming: A Modern Approach, 2nd Edition
如果你也很懶的話: git clone https://gist.github.com/634f18ba9e2648b48585.git q6
$ git clone https://gist.github.com/634f18ba9e2648b48585.git q6
如果你也很懶的話: git clone https://gist.github.com/634f18ba9e2648b48585.git q6
即是讓指標移動 ( 非 const pointer )
移動的距離依照指標指向的型別而定
eg1: double *p; 一次移動 8個 bytes 的距離
eg2:
int a[10];
int (*ptr)[10] = &a;
上面的 ptr 指向的是 int [10] 整個陣列~~~
ptr + 1呢 XD?
eg: *ptr++, *++ptr;
Operator precedence in C:
int *p = &a[5];
int *q = &a[1];
What is p - q?
圖片取自:C Programming: A Modern Approach, 2nd Edition
git clone https://gist.github.com/da28be5dde2048d95f72.git
git clone https://gist.github.com/64ec7f9b9673683e8bb2.git
神奇的事情!?
好孩子不要這樣寫,因為你不知道會發生什麼事情
git clone https://gist.github.com/719ab459708f8969a21c.git
(下週上課後砍掉此頁)
eg: *ptr++; *++ptr;
Operator precedence in C:
int array[10];
int *ptr = &array[0];
在這種情況下,使用 ptr++; ++ptr; 只是將 ptr + 1
所以會將指標移動到下一個 int 也就是 array[1];
反之, ptr--; --ptr; 都是在做指標的移動。
int array[10];
int *ptr = &array[5];
*, ++, -- 有以下 4 種搭配用法
git clone https://gist.github.com/2b7df42c336e616f3b67.git
eg: *ptr++; *++ptr;
Operator precedence in C:
其實在某些時候,陣列的名稱(有些書會寫作:陣列變數)會被編譯器當成是指標處理。
例如:
對一個 integer array 傳入 function時,實際上編譯器會將 array 看成一個指標,指向 int 變數。所以 array 會被編譯器當成是 int *。
什麼時候,做什麼樣的操作呢?才會被當成 pointer 處理?
Bullshxt !
sizeof 運算子對指標變數運算的話,取得的會是指標變數的所需的空間大小。
指標變數可以是 int *, double *, int (*)[10], int *(*) 。這些變數用來儲存的記憶體的位址。
所以其大小最小要是能夠定址到每一個記憶體的 byte。假如今天是 4GB RAM 及 32-bit 電腦架構, 相當於有 2^32 個 bytes。為了定址這 2^32 個 byte,就要有 32 個 0, 1 來表示定址的位址。32 個 0, 1 及 32bits,相當於是 4bytes。所以在這樣的情況下指標的大小最小就是 4bytes
git clone https://gist.github.com/07ed25cc9a90ccc5318d.git
讓編譯器來告訴你!
編譯參數加上 -Wall
回家趕快學 gdb
git clone https://gist.github.com/07ed25cc9a90ccc5318d.git
warning: incompatible pointer types initializing 'int *' with an expression of type 'int (*)[5]'
[-Wincompatible-pointer-types]
int *ptr = &array;
Why? 在 C 語言中有
左值指的是實際在記憶體中佔有空間的內容。右值則是一個實際運算出的數值。左右之分,是因為放在 assignment 的左右邊而定名。但是!array name 雖然在 C 語言中佔有記憶體空間,可是卻無法修改。我們稱之為 unmodifiable lvalue
error: array type 'int [5]' is not assignable
array = array2;
陣列其實會包含大小的資訊 (由前面的 sizeof operator 可以看到 )。但是,當你把一個陣列指定 ( assign ) 給指標變數的時候,我們無法藉由指標變數來得知原本陣列的大小。這樣資訊流失的情況,被稱作是指標退化 ( pointer decay )
尤其是,當我們把陣列當作參數傳入函式的時候,一定會發生這種情況。所以你必須做的事情是在使用函式的時候,一併將陣列大小 ( Array size ) 當作變數傳入。
int a[10];
int *p = NULL;
p = a;
以下取自wikipedia:
An expression in a programming language is a combination of one or more explicit values, constants, variables, operators, and functions that the programming language interprets (according to its particular rules of precedence and of asso-ciation) and computes to produce ("to return", in a stateful en-vironment) another value.
2. array name + [index] 相當於 *(pointer + offset);
以往,有人會認為將 array + [index] 寫成 *(pointer + offset)的形式,執行效能會比較快。但是,在現今的編譯器來說,已經做了相當程度的最佳化,編譯出來的組合語言是一樣的。所以在執行效能上沒有太大差別,應該著重於語意表達。
3. 用於函式當作宣告的參數型別時,array name 會被當作是指向 array 第一個元素的指標。
Why? 因為 C 語言只有 Pass by Value 的概念,我們只有藉由 Pass address 給 function,藉由 address 和記憶體做連結,才可以取得 array 的內容。( pass by value 是複製 address 的數值給 formal parameter )
3. 用於函式當作宣告的參數型別時,array name 會被當作是指向 array 第一個元素的指標。
以上兩種參數對編譯器來說都會轉成是 int *
以編譯器的角度看
Type1: Function 可以修改 Function 外的變數
Type2: 傳遞連續的記憶體資料。(array, string)
Type2: 傳遞連續的記憶體資料。(array, string)
int scanf(const char *restrict format, ...);
(畫圖講解)
為什麼使用 char * 這樣的指標行別來存取 string 內容?
因為對於 string 來說,每一個元素都是 char 的型別,我們使用指標搭配指標移動可以存取在記憶體中連續的資料,這個資料是 char,所以我們使用 char * 這樣的指標。
1. 可以存取字串中的資料單元
2. 可以藉由遞移指標完成整個字串的查找
const 則是標記我們不會藉由指標更改記憶體中的內容
請實作 print_arr 這個函式,讓他能夠印出一維陣列中的每一個元素。請不要偷看前面的內容,誠實面對自己。你偷看了,寫對了不代表你真的學會!
int array[10] ----> int (*array);
但是由於衰退了
不知道存取到何時才結束( size 的資訊不見了!)
所以為什麼小試身手8 要給定 size,Cstring呢?
p.37
& + array name 會被轉成為一個指標指向整個 array 大小的資料。那 s[1], s[2] 呢?(畫圖
p.37
& + array name 會被轉成為一個指標指向整個 array 大小的資料。那 s[1], s[2] , s[0][0], s[0][1], s[0][2] 呢?
前面的例子: char (*s)[5] 為麼要有 5 ?
以 int array[3][4] 為例
method1:
優點:無腦好寫
缺點:array如果是[4][4] 就沒辦法使用了!
git clone https://gist.github.com/8e29e0b494e86bb783d9.git
以 int array[3][4] 為例
method1:
試試看,請問第 12, 13行的結果是什麼?
為什麼會這樣?
git clone https://gist.github.com/8e29e0b494e86bb783d9.git
以 int array[3][4] 為例
試試看,請問第 12, 13行的結果是什麼?
12 行:因為我們傳入的 arr 會轉成為 int (*)[4] 是一個指標,對 64bits 系統來說,指標的大小會是 8 bytes
13行:因為我們給予編譯器 arr的資訊是 int [3][4] 所以可以知道他每一個 row 有 int [4] 的大小於是就會是 16
sizeof 是編譯時期就能取的資訊的 operator
以 int array[3][4] 為例
method2:
為了讓它更有彈性!我們的參數是 int (*arr)[4]
這樣就能讓 int [1][4], int [3][4] .... int [100][4] 這樣的陣列都可以使用了!
git clone https://gist.github.com/c987c42713b421587d91.git
親手打看看
以 int array[3][4] 為例
method2:
其實參數 int (*arr)[4] 相當於 int arr[][4]。所以在這裡可以互換!
因為我們說 array 在當參數值,靠近名稱的維度會退化!
git clone https://gist.github.com/c987c42713b421587d91.git
親手打看看
在這裡就不能以 int arrat[3][4] 為例了
method3:
在參數中,int ** 和 int (*)[4] 顯然是有差距的,後者知道大小 ( array ) ,但是前者只知道他是一個指標,指向另一個指標。
常用於動態配置記憶體
git clone https://gist.github.com/ff414ea9a487fde3049e.git
在這裡就不能以 int arrat[3][4] 為例了
method3:
不覺得這很像是
int main(int argc, char **argv);
int main(int argc, char *argv[]);
// char *argv[] ---> char *(*argv)
為什麼這裡只需要用一個 argc 標記字串的個數?
也就是 char *argv[]
Iliffe vector
method4:
既然...... 陣列在記憶體其實只是一個連續的空間
那我們當然可以用一維陣列來模擬二維的行為呀!
method4:
用一維陣列模擬二維行為
git clone https://gist.github.com/2ac87731c30ce4499392.git
int scanf(const char *restrict format, ...);
你們應該懂上面 const char * 的意義了
int array[10];
extern int *array;
extern int array[];