C程式-陣列與指標

37,591 views

Published on

艾鍗學院C語言與資料結構課程講義

Published in: Education
  • Be the first to comment

C程式-陣列與指標

  1. 1. 第六章 陣列與指標 1
  2. 2. 陣列 Arrays 陣列 : 由一連續且相同資料型態的元素所組成 . 1. 所謂“連續”指的是元素是循序地儲存在記憶體裡 . 陣列的宣告方式 datatype name [constant-size] 1. constant-size 不可以是變數 , 只能是常數且是一個正整數 , 例如 : 2. #define MAX_SIZE 16 3. ... a[0] 5 4. int list[MAX_SIZE + 1]; a[1] -2 陣列的初始化 : a[2] 17 1. 如 int a[3] = {5, -2, 17}; 2. 陣列元素若未進行初始化 , 其每個元素的值都是 garbage value • 註 : 對於 global 和 static 陣列其元素會初始為 0 1. 陣列初始最後一個元素後 , 其它未初始化元素都會設為 0 • int b[5] = {5,-2};2
  3. 3. 陣列 Arrays (cont.) 若未明確指定 constant-size, 則編譯器會依列示的初始 值個數來計算陣列所需的元素個數 • int q[] = {1, 2, 3}; 等同於 int q[3] = {1, 2, 3}; 陣列大小無法再更動 C 陣列索引由 0 開始 , 因此 int a[3]; 為表示有 3 個 int, 分別為 a[0], a[1] 和 a[2] 1. 小心別存取 a[3]. 字串 (String) 的宣告可以是為字元陣列加上一個空字 元 (NULL character), 如下 : 1. char str1[] = {a, b, c, 0}; char str2[] = "abc";3
  4. 4. 陣列 Arrays (cont.) extern int a[]; 此非陣列宣告 , 而是明確表示陣 列 a 為外部參考 .(External Reference), 即陣列 a 是定義在其他檔案 (file scope) 或任何函數外 (global variable) const int a[3] = {5, -2, 17}; const 關鍵字可以使陣 列元素只能讀無法修改 .4
  5. 5. 範例 -1D 陣列 #include <stdio.h> #include <stdio.h> #define SIZE 33 #define SIZE #define SHOW_1D(X,Y) for ((X)=0;(X)<(Y);(X)++) #define SHOW_1D(X,Y) for ((X)=0;(X)<(Y);(X)++) printf("a[%d]=%-5d <--%pn",(X),a[(X)],&a[(X)]); printf("a[%d]=%-5d <--%pn",(X),a[(X)],&a[(X)]); int main() int main() {{ int i=0; int i=0; int a[SIZE]={5,-2,17}; int a[SIZE]={5,-2,17}; SHOW_1D(i,SIZE); SHOW_1D(i,SIZE); a[1]=100; a[1]=100; SHOW_1D(i,SIZE); SHOW_1D(i,SIZE); return 0; return 0; }}5
  6. 6. 要讓程式當掉其實很容易void fun(void){ char a[4] ; a[4] = 0 ; // crash!}void fun(void){ char a[3] ; a[3] = 0 ; // may be still alive! Why?}6
  7. 7. 隨堂練習Please implement this function:float D2A(int Digital_value) ;-10bit AD (0-1023)-對應電壓 0~3.3V-每次取樣 10 筆,過濾掉最大與最小值,取平均-傳入 Digital 值 ( 八筆平均 ) ,傳回電壓值test.c – 使用者輸入 AD 值,呼叫 AD 模組的功能,傳回正確的電壓值。AD.c – AD 模組的實現AD.h – AD 模組相關的宣告7
  8. 8. D2A Converter Digital : 0~1023 Voltage: 0 ~ 3.3V 3.3V Input Digital value = X ?1023 X = 0 X 10233.3 V V = 3.3 * X / 1023 8
  9. 9. 指標 Pointer
  10. 10. 指標 指標 : 目的是用來指向其他變數 . 1. 本身亦為一個變數 , 2. 指標內容為”所指向變數之記憶體位址” . 和指標有關的二個運算子 (Operator) 1. & 傳回變數的位址 2. * Redirect operator( 間接存取運算子 ) 提取 (dereferences) 被指標所指向的位置裡所儲存之值10
  11. 11. 範例 int i,i,j;j; int int **p; int p; /* pointer to `int */ /* pointer to `int */ i i= 6; = 6; pp= &i; = &i; /* set `p to address of `i */ /* set `p to address of `i */ j j= *p; = *p; /* set `j to 66(value of `i) */ /* set `j to (value of `i) */ *p = 5; *p = 5; /* set `i to 55*/ /* set `i to */ i 0xba04 6 5 j 0xba08 6 *p p 0xba18 0xba0411
  12. 12. 範例 #include <stdio.h> #include <stdio.h> int data[2] = {100, 200}; int data[2] = {100, 200}; int moredata[2] = {300, 400}; int moredata[2] = {300, 400}; int main(void) int main(void) {{ int **p1, **p2, **p3; int p1, p2, p3; p1 = p2 = data; p1 = p2 = data; p3 = moredata; p3 = moredata; printf(" *p1 = %d, *p2 = %d, *p3 = %dn", printf(" *p1 = %d, *p2 = %d, *p3 = %dn", *p1 , , *p2 , , *p3); *p1 *p2 *p3); printf("*p1++ = %d, *++p2 = %d, (*p3)++ = %dn", printf("*p1++ = %d, *++p2 = %d, (*p3)++ = %dn", *p1++ , ,*++p2 , ,(*p3)++); *p1++ *++p2 (*p3)++); printf(" *p1 = %d, *p2 = %d, *p3 = %dn", printf(" *p1 = %d, *p2 = %d, *p3 = %dn", *p1 , , *p2 , , *p3); *p1 *p2 *p3); return 0; return 0; }}12
  13. 13. data[1] = 200 data[0] = 100data moredata[0]=400 *p1 = 100, *p2 = 100, *p3 = 300 moredata[0]=300moredata P3 P2 P1 13
  14. 14. data[1] = 200 data[0] = 100data moredata[0]=400 *p1++ = 100moredata moredata[0]=300 301  access (*p1)  p1++ *++p2 = 200  p2++  access (*p2) P3 P2 (*p3)++ = 300  access *p3 P1  (*p3) = (*p3) + 1 14
  15. 15. data[1] = 200 data[0] = 100data moredata[0]=400 *p1 = 200, *p2 = 200, *p3 = 301moredata moredata[0]=300 301 P3 P2 P1 15
  16. 16. 算我求求你們,千萬別寫出 *++p2 這樣的鬼東西!對軟體團隊而言,程式的可讀性與可維護性, 其重要性超過一切!16
  17. 17. 指標 (cont.) null pointer : 指標內容為 0 (NULL) 1. if (ptr) statement ; 2. if ( ptr!= 0) statement ; 指標不可以指向 1. 常數值 (constants) 如 3, 因為 3 並沒有一個固定的記 憶體位址 . 2. 暫存器 (register) 變數 , 因為暫存器變數的內容並不 是放在記憶體 , 因此沒有記憶體位址 . 3. 運算式 (expressions) 如 8 * k , 運算式的結果即如同常 數值 , 一樣沒有一個固定的記憶體位址 .17
  18. 18. 陣列與指標 (cont.) 指標與陣列的關聯 1. 陣列的存取是直接存取 (direct addressing), 而指標的存取是間接 存取 (indirect addressing) 2. 陣列名稱 arr 為一個常數值 , 代表陣列元素的起始位址 , 而指標 p 的內容為 arr 陣列起始位址 3. &arr[0]==arr 4. arr[i]==*(arr+i) 5. arr++; 是錯誤的 ( 提醒 :++ 和—運算子的對象是變數 ) 6. sizeof 運算子 • sizeof(arr) 傳回陣列所佔記憶體空間 • sizeof(p) 傳回指標變數所佔記憶體空間18
  19. 19. 陣列與指標 (cont.) 1. & 運算子 • &array == arrary == &arrary[0] • &pointer 傳回指標變數的位址 . 1. 字串常數 • 字串常數儲存在一個唯讀的記憶位置 . • char arr[] = "abc" 陣列 arrary 其第一個元素至第 4 個元素為別被設為 a, b, c, 0 如同 strcpy(arr,”abc”) • char *ptr = "abc" • ptr 指向字串常數所在記憶體位址 “abc” 到底儲存在哪裡?19
  20. 20. 範例 -1D 陣列 #include <stdio.h> #include <stdio.h> #define SIZE 3 #define SIZE 3 void show_1d(int *ar,int size); void show_1d(int *ar,int size); int main() int main() {{ int i=0; int i=0; int a[3]={5,-2,17}; int a[3]={5,-2,17}; show_1d(a,SIZE); show_1d(a,SIZE); a[1]=100; a[1]=100; show_1d(&(a[0]),SIZE); show_1d(&(a[0]),SIZE); return 0; return 0; }} void show_1d(int *ar,int size) void show_1d(int *ar,int size) {{ int i=0; int i=0; for (;i<size;i++) printf("ar[%d]=%-5d <--%pn",i,ar[i],&ar[i]); for (;i<size;i++) printf("ar[%d]=%-5d <--%pn",i,ar[i],&ar[i]); }}20
  21. 21. #include <stdio.h> #include <stdio.h> #define SIZE 3 #define SIZE 3 void show_1d(int *start, int *end); void show_1d(int *start, int *end); int main() int main() {{ int i=0; int i=0; int a[3]={5,-2,17}; int a[3]={5,-2,17}; show_1d(a,a+SIZE); show_1d(a,a+SIZE); a[1]=100; a[1]=100; show_1d(&a[0],a+SIZE); show_1d(&a[0],a+SIZE); return 0; return 0; }} void show_1d(int *start, int *end) void show_1d(int *start, int *end) {{ int i i= 0 ;; int = 0 for (;stasrt<end;start++,i++) for (;stasrt<end;start++,i++) printf(“start[%d]=%-5d <-- %pn",i,*start, start); printf(“start[%d]=%-5d <-- %pn",i,*start, start); }}21
  22. 22. 陣列與指標 (cont.) 千萬別存取一個未初始化的指標 1. int *pt; // an uninitialized pointer 2. *pt=4; // a terrible error 3. 使用指標前 , 必須先讓指標指向一個已存在的變數的 記憶體位址22
  23. 23. 指標的運算 ptr++ 或 ptr=ptr+1 會使指標的數值增加一個單位 . 此 單位大小由指標所指向資料型態的大小而定 例如 ptr 指向位址 0xff00: 1. 若 ptr char 的指標 , 則 ptr++ 將使 ptr 成為 0xff01 2. 若 ptr 是一個指向 int (4 bytes) 的指標 , 則 ptr++ 將使 ptr 成為 0xff04 若 ptr1 指向 a[0] 且 ptr2 指向 a[3], 則 1. ptr2 > ptr1 的結果為真 2. ptr2 - ptr1 的結果 3 ( 和 a 陣列的資料型態無關 )23
  24. 24. 指標的運算 (cont.) 若 ptr1 指向 a[0] 且 ptr2 指向 b[3], 則 1. 無法確定 ptr2 > ptr1 或 ptr2 < ptr1 的結果 , 唯一能確 定的只有 ptr2 != ptr124
  25. 25. 指標的指標int **p2; // a pointer to a pointer int **p2; // a pointer to a pointer int *p1; int *p1; int arr[3]={3,6,9}; int arr[3]={3,6,9}; p1=arr+2; p1=arr+2; p2=&p1; p2=&p1; **p2= 100; **p2= 100;25
  26. 26. 多維陣列 宣告一個二維陣列如 float m[3][2]; m 陣列的 6 個元素 , 分別為 m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]26
  27. 27. 範例 -2D 陣列 #include <stdio.h> #include <stdio.h> #define ROW 33 #define ROW #define COL 22 #define COL int main() int main() {{ int i,j; int i,j; int m[ROW][COL]={{100,200},{300,400},{500,600}}; int m[ROW][COL]={{100,200},{300,400},{500,600}}; m[1][1]=900; m[1][1]=900; for (i=0;i<ROW;j=0,i++) for (i=0;i<ROW;j=0,i++) for (j=0;j<COL;j++) for (j=0;j<COL;j++) printf("m[%d][%d]=%-5d <--%pn",i,j,m[i][j],&m[i][j]); printf("m[%d][%d]=%-5d <--%pn",i,j,m[i][j],&m[i][j]); return 0; return 0; }}27
  28. 28. 多維陣列 (cont.) 多維陣列即為多個”陣列的陣列” (of arrays of arrays...) 宣告一個二維陣列如 float m[3][2]; //array of 3 array of 2 floats 1. m 是一個 float 型態的陣列其可儲存 3 項資料而每一項 又具有 2 個元素 (2-int 資料型態單位的陣列 ) 2. float m[3][2]; // m is an array of 3 somethings 3. float m[3][2]; // an array of 2 floats (*m)[2] 4. m[3][2] 是一個以 2-int 資料型態為單位的陣列 m[0] m[0][0] m[0][0] m[1] m[0][0] m[0][0] pointer array m[2] m[0][0] m[0][0]28
  29. 29. An array of arrays zippo the address of the first 2-int element zippo+2 the address of the third 2-int element *(zippo+2) the third element, a 2-int array, hence the address of its first element, an int *(zippo+2) + 1 the address of the second element of the 2-int array, also an int *(*(zippo+2) + 1) the value of the second int in the third row (zippo[2][1]) zippo[m][n] == *(*(zippo + m) + n)29
  30. 30. Pointers to Multi-Dimensional Arrays int (* pz)[2]; // pz points to an array of 2 ints pz=zippo; pz[m][n] == *(*(pz + m) + n)30
  31. 31. Example #include <stdio.h> int main(void) { int zippo[4][2] = { {2,4} , {6,8} , {1,3} , {5, 7} }; printf(" zippo = %p, zippo + 1 = %pn", zippo, zippo + 1); printf("zippo[0] = %p, zippo[0] + 1 = %pn", zippo[0], zippo[0] + 1); printf(" *zippo = %p, *zippo + 1 = %pn", *zippo, *zippo + 1); printf("zippo[0][0] = %dn", zippo[0][0]); printf(" *zippo[0] = %dn", *zippo[0]); printf(" **zippo = %dn", **zippo); printf(" zippo[2][1] = %dn", zippo[2][1]); printf("*(*(zippo+2) + 1) = %dn", *(*(zippo+2) + 1)); return 0; }31
  32. 32. Pointer to const data int * p1; const int * p2; const int ** pp2; p1 = p2; // not valid -- assigning const to non-const p2 = p1; // valid -- assigning non-const to const pp2 = &p1; // not valid -- assigning non-const to const const int **pp2; int *p1; const int n = 13; pp2 = &p1; // not allowed, but suppose it were *pp2 = &n; // valid, both const, but sets p1 to point at n *p1 *p1= 10; // valid, but changes const n32
  33. 33. Constant pointer ch6/const_ptr.c #include <stdio.h> int main() { const int a=10; int b=20; const int * const ptr=&a; *ptr=100; ptr=&b; return 0; }33
  34. 34. Functions and Multidimensional Arrays #define ROWS 3 #define ROWS 3 #define COLS 4 #define COLS 4 void sum_rows(int ar[][COLS], int rows); void sum_rows(int ar[][COLS], int rows); int main() {{ int main() int junk[3][4] = {{ int junk[3][4] = {2,4,5,8} ,, {2,4,5,8} {3,5,6,9} ,, {3,5,6,9} {12,10,8,6} {12,10,8,6} }; }; sum_rows(junk, ROWS); sum_rows(junk, ROWS); return 0; return 0; }}34
  35. 35. void sum_rows(int ar[][COLS], int rows) void sum_rows(int ar[][COLS], int rows) {{ int r; int r; int c; int c; int tot; int tot; for (r = 0; rr < rows; r++) for (r = 0; < rows; r++) {{ tot = 0; tot = 0; for (c = 0; c < COLS; c++) for (c = 0; c < COLS; c++) tot += ar[r][c]; tot += ar[r][c]; printf("row %d: sum = %dn", r, tot); printf("row %d: sum = %dn", r, tot); }} }}35
  36. 36. Lab #6 - Homework 矩陣相乘 – 除了影像處理或加解密…等,我不認 為你 在嵌入式系統裡會碰 到更複雜的陣列運算了 ! (http://zh.wikipedia.org/zh-hk/ 矩陣乘法 ) 早晚你 要碰 到的 – 泡泡排序法。36
  37. 37. 37
  38. 38. Bubble Sort#include <stdio.h> /* Bubble Sort */void main(void){ int data[50]; int i,j,n,temp;printf("Please input integer number you want to sort:");scanf("%d" ,&n);printf("n");if (n > 49){ printf("number should be less than 49n"); return;}for (i = 1;i <= n;i++){ printf("input data[%d]=", i); scanf("%d", &data[i]);}38 38
  39. 39. Sortingfor (i=1; i <= n; i++) { for (j = n; j > i; j--) { if (data[j-1] > data[j]) { temp = data[j-1]; data[j-1] = data[j]; data[j] = temp; } } } printf("nSorting result: n"); for(i = 1; i <= n; i++) printf("%d ", data[i]);}39 39
  40. 40. 矩陣相乘1. 輸入矩陣 row, column2. 輸入矩陣所有元素3. 矩陣相乘40
  41. 41. 矩陣相乘 (1)#include <stdio.h>// Matrix A : m x n (row = m, column = n)// Matrix B : n x p// A x B = C (C should be m x p)//#define m 3#define n 2#define p 241
  42. 42. 矩陣相乘 (2)void main(){ int A[m+1][n+1], B[n+1][p+1], C[m+1][p+1]; int i, j, k; printf("Please input Matrix A:"); for (i =1 ; i<= m; i++) { for(j=1;j<=n;j++) scanf("%d",&A[i][j]); }/*end for*/ printf("Please input Matrix B:"); for (i =1 ; i<= n; i++) { for(j=1;j<=p;j++) scanf("%d",&B[i][j]); }/*end for*/ 42
  43. 43. 矩陣相乘 (3) for (i=1; i<=m; i++) { for (j=1; j<=p; j++) { C[i][j]=0; for(k = 1; k <= n; k++) { C[i][j] = C[i][j] +A[i][k] * B[k][j]; } printf("%d ", C[i][j]); } printf("n"); }}43
  44. 44. How to access CPU registers? CPU Internal Registers Memory Mapping Registers 44
  45. 45. CPU Internal Registers 45
  46. 46. /* 透過一般暫存器 R4 ,設定 Stack Point ( SP )暫存器的值 (通常特殊暫存器都不允許直接設值 , 必須透過一般暫存器) */ asm("xld.w %r4,0x20000"); asm("ld.w %sp,%r4"); /* 假設第 0 個 bit 代表 IE bit ( Interrupt Enable ),值為 1 時表示允許中 斷產生 以下的程式會令 CPU 允許中斷發生 */ asm("ld.w %r8, 0x000001"); // Set PSR to interrupt enable asm("ld.w %psr, %r8"); GCC-Inline-Assembly-HOWTO Reference-246
  47. 47. Memory Mapping Registers47
  48. 48. “volatile” 這兩個程式等價嗎?48
  49. 49. 使用 C 語言開發驅動程式 (1) Volatile 變數volatile unsigned int * xxx_register = 0x300024 ;// 對暫存器 0x300024 連續設值,因為變數形態設定為 volatile// 所以對 xxx_register 的操作不會被最佳化//*xxx_register = 0x00ABCDEF ;*xxx_register = 0x12345678 ; ;// 0x300021 是 CPU 的 memory mapping Register// 以下的運算是將 bit 4 設為 1// *((volatile unsi gned char *)0x300021) |= 0x10; *((volatile unsigned char *)0x300021) = *((volatile unsi gned char *)0x300021) | 0x10 ;49
  50. 50. volatile unsigned char data ;data = *((volatile unsigned char *)0x300021) ;data = data | 0x10 ; // set bit 4 as 1*((volatile unsi gned char *)0x300021 = data ;50
  51. 51. IF FPSHIFT-mask-enable set to 16bpp and color modeELSE set LCD-panel-data-width to 4-bit mode51
  52. 52. 動態記憶體配置void * malloc(int size) ;void free(void * ptr) ;52
  53. 53. main() { int A_array[20] ; int * B_array ; int array_no ; int i ; for(i=0;i<20;i++) A_array[i] = 0 ; printf("please input size of array B: ") ; scanf("%d",&array_no) ; B_array = (int *)malloc(array_no * sizeof(int)) ; if(B_array != NULL) { for(i=0;i<array_no;i++) B_array[i] = 0 ; } free(B_array ) ; }53
  54. 54. Lab #6-2使用 malloc/free 修改 homework1 (多項式)程式,使其可以處理超過 10 項的多項式。54
  55. 55. Lab #6-3 4 個學生的四科成績 求全部平均 求第 n 位學生的平均55 Page:97
  56. 56. 記憶體測試 記憶體會出問題的機會比想像的多, e.g. 1. 電路設計或 Layout 錯誤 2. Chip Select 選錯 3. SDRAM controller 設定錯誤 4. Timing 設定 基本的硬體測試 - 把各個記憶體的每 一個 byte 都測 過,確定讀寫 ( 如果能寫的話 ) 都沒問題56 Copyright © 2007 FITPI. All rights
  57. 57. Lab #6-41. Allocate Memory as SRAM2. write(address,value)3. verify(address,value)4. calculate_checksum(address,size)57
  58. 58. 嵌入式系統標準函式庫的效能考量 memcpy() void memcpy(void * dest, void * src, int n) { // 這個 function 也有可能是用 assembly 實現的 , 但算法基本上一樣 // int i ; char *Dest = (char *)dest ; char *Src = (char *)src ; for(i=0;i<n;i++) Dest[i] = Src[i] ; 安全的作法 安全的作法 } void my_memcpy(void * dest, void * src, int n) { // for Video-RAM copy only // 加速版 int i ; long *Dest = (long *)dest ; 加速版 long *Src = (long *)src ; for(i=0;i<(n>>2);i++) // 往右移兩個 bit  n/4 Dest[i] = Src[i] ; }58 58
  59. 59. 59
  60. 60. 60
  61. 61. 61

×