Try   HackMD

[C語言] 指標、陣列與結構

相較於基本型別int, double,C語言還有衍伸資料型別(Derived datatype)

其中很常被應用的,就是指標(pointer)陣列(Array)結構(struct)

指標(Pointer)

指標的設計,是為了讓CPU可以間接取得資料,有以下優點:

  • 可動態配置/釋放記憶體
  • 不同的函式可共享大量的儲存空間
  • 改善程式存取資料的效率

指標的特性

指標本身是一個物件,指向任何一個已存在物件,可以被指定或拷貝

  • 指標宣告時,就要定義是指向哪種資料型態的物件
  • 指標本身儲存的值,有三種情況
    • 指向一個物件,值裡面存放另一個物件的位址
    • 指向另一個物件尾端後的位置
    • 沒有指向任何物件,值即為null
  int i = 42;
  int *p = &i;            //p指向i
#include <stdio.h> int main() { int i = 42; int *p; //* 跟在一個型別,且是宣告的一部份,所以p是一個pointer p = &i; //p指向i printf("i: %d\n", i); printf("*p: %d\n", *p); printf("p: %p\n", p); printf("&i: %p\n", &i); return 0; }

陣列(Array)

陣列的定義

是一個複合資料型別,放置單一型別物件的容器,並且以位置來存取它們,有固定的尺寸。

通常是存放char, int型別的資料

字元陣列的特性

在C語言,一個字串本身會在結尾自帶\0結束字元,所以當我們把一個字串用陣列儲存時,實際結尾會有一個\0也被儲存。

例如: char str[20] = "Hello"
在陣列的存放,是一個長度為6的陣列,最後擺放\0

宣告與初始化陣列

  • 靜態宣告
int a[10];   //宣告一個10個int物件的陣列
int *p[10];  //宣告一個10個int指標的陣列

因為陣列的長度要在編譯期就決定好。如果想要在執行期動態生成陣列,要用動態配置記憶體的方式。

  • 動態宣告
    我們同樣用 malloc() 函式來配置記憶體。參考以下敘述:
int *arr = (int *) malloc(sz * sizeof(int));

我們以 sizeof 求得單一元素的大小後,乘上陣列的長度 sz 即可配置一塊足夠大小的記憶體,用來儲存陣列 arr 的元素。由此可知,陣列在電腦中以是一整塊連續的記憶體來儲存,所以可以用索引值快速存取。

如果想要在配置記憶體時一併將元素初始化為 0,改用 calloc() 函式即可。但 calloc() 函式的參數略有不同:

int *arr = (int *) calloc(sz, sizeof(int));

由於多了初始化的動作,calloc() 函式會比 malloc() 函式慢一點點。

使用完後同樣要釋放記憶體:

free(arr);

由於陣列內部是單一且連續的記憶體區塊,所以可在單一 free() 函式呼叫中釋放掉。不論使用 malloc() 或 calloc(),皆使用 free() 來釋放記憶體。

我們來看一個動態配置記憶體陣列的範例:

#include <stdio.h> #include <stdlib.h> int main(void) { int size = 0; printf("輸入長度:"); scanf("%d", &size); int *arr = malloc(size * sizeof(int)); printf("指定元素:\n"); for(int i = 0; i < size; i++) { printf("arr[%d] = ", i); scanf("%d" , arr + i); } printf("顯示元素:\n"); for(int i = 0; i < size; i++) { printf("arr[%d] = %d\n", i, *(arr+i)); } free(arr); return 0; }

存取陣列元素

陣列使用零或正整數存取陣列元素。參考以下範例:

#include <stdio.h> int main(void) { int arr[] = {3, 4, 5}; printf("%d", arr[0]); printf("%d", arr[1]); printf("%d", arr[2]); return 0; }

指標算術

對一個指標做加減,會移動指向陣列中的位置

#include <iostream>
using namespace std;

int main() {
  int a[] = {1,2,3,4,5};
  int *ip = a;          //等同於int *ip = &a[0]
  int *ip2 = ip + 4;    //ip2指向a[4]

  cout << ip2 << endl;

  return 0;
}

走訪陣列

走訪陣列元素的方式是使用 for 迴圈搭配計數器 (counter)。參考下例:

#include <stdio.h> int main(void) { int arr[] = {3, 4, 5, 6, 7}; for (int i = 0; i < 5; i++) { printf("%d\n", arr[i]); } return 0; }

計算陣列大小

C 陣列本身沒有儲存陣列大小的資訊。如果想要知道陣列的大小,得自行計算。參考下例:

#include <stdio.h> int main(void) { int arr[] = {3, 4, 5, 6, 7}; int sz = sizeof(arr) / sizeof(int); printf("%d", sz); return 0; }

在此範例程式中,我們在第 6 行分別計算陣列大小和陣列元素大小,將其相除後即可得陣列長度。在本例中其值為 5。

但這個方式只對自動配置記憶體的陣列有效,若陣列使用動態配置記憶體,則無法使用這個方法。

多維陣列

先前的範例皆為一維陣列,但 C 語言允許多維陣列。參考以下宣告多維陣列的敘述:

int mtx[3][2] = { {1, 2}, {3, 4}, {5, 6} };

我們同樣可以對多維陣列存取索引值:

#include <stdio.h> int main(void) { int mtx[3][2] = { {1, 2}, {3, 4}, {5, 6} }; printf("%d\n", mtx[1][1]); return 0; }

結構(struct)

宣告struct

結構(struct)可以讓使用者創造自己定義的資料型別

例如一本書的銷售資訊包含多種資料:

struct Sales_data { char* book_no; int units_sold; int price; double revenue; };

其中book_no, units_sold , price,revenue是這個struct的data members,簡稱members

呼叫struct

#include <stdio.h> struct Sales_data { char* book_no; int units_sold; int price; double revenue; }; int main() { struct Sales_data book1; book1.book_no = "AAA"; book1.units_sold = 1000; book1.price = 200; book1.revenue = book1.units_sold * book1.price; printf("Revenue: %.2f\n", book1.revenue); return 0; }
tags: C/C++程式語言觀念