C語言實現動態陣列 C語言函式指標和回撥函式
實現任意資料型別的動態陣列的初始化,插入,刪除(按值刪除;按位置刪除),銷燬功能。、
動態陣列結構體
實現動態陣列結構體的三個要素:(1)陣列首地址;(2)陣列的大小;(3)當前陣列元素的個數。
1 //動態陣列結構體 2 struct DynamicArray{ 3 void **addr; //指向陣列的首地址(指向陣列的指標) 4 int capacity; //陣列的大小 5 int size; //當前陣列元素的個數 6 };
注意事項:void **addr為二級指標,即陣列的元素也為指標,因為我們並不知道使用者的輸入資料是什麼型別,操作資料的地址是最安全的方法。
初始化
對動態陣列進行初始化,實際上為初始化動態陣列內的各個成員,另外對輸入的引數做邊界處理。
1 //初始化陣列,初始化結構體和裡面的元素。初始化之後返回該陣列,寫為void* 2 void *Init(int capacity_){ 3 if (capacity_ <= 0){ 4 return NULL; 5 } 6 7 struct DynamicArray *arr = malloc(sizeof(struct DynamicArray));//開闢一個結構體就可以了 8 if (NULL == arr){9 return NULL; 10 } 11 12 arr->capacity = capacity_; 13 arr->addr = malloc(sizeof(void*)*arr->capacity);//對陣列開闢記憶體 14 arr->size = 0; 15 16 return arr; 17 }
插入操作
對於動態陣列的插入操作,需要在pos位置處開始的元素統一往後移動一個位置,處理方式為:從後往前挨個移動,從前往後會覆蓋。
注意:(1)考慮動態陣列的順序插入和亂序插入,12行的程式碼;(2)若動態陣列被填滿,則需要對現有陣列進行擴大空間,這裡涉及到四步操作:開闢記憶體、拷貝資料(儘量使用memcpy)、釋放原記憶體、修改指標指向。(3)對輸入引數做邊界處理。
1 //插入值,從後往前挨個移動一位,如果插入的值過大,則擴大陣列 2 void Insert(void *arr_, int pos, void *data){ 3 4 struct DynamicArray *arr = (struct DynamicArray *)arr_; 5 6 if (NULL == arr || NULL == data){ 7 return; 8 } 9 10 //對於無效的pos,預設插入到現有元素的後面一個 11 //if (pos < 0 || pos > arr->capacity) 12 if (pos < 0 || pos > arr->size) 13 { 14 pos = arr->size; 15 } 16 17 //每次呼叫插入函式都會插入值,因此arr->size++,size一直增長,且沒有限制 18 if (arr->size >= arr->capacity) 19 //if (arr->size > arr->capacity) 20 { 21 22 //開闢新記憶體 23 int newcapacity = arr->capacity * 2; 24 void **newaddr = malloc(sizeof(void *)*newcapacity); 25 26 //拷貝資料,儘量使用記憶體拷貝函式 27 memcpy(newaddr, arr->addr, sizeof(void *) * arr->capacity); 28 29 //釋放原空間 30 if (arr->addr != NULL){ 31 free(arr->addr); 32 arr->addr = NULL; 33 } 34 35 //修改指標指向 36 arr->addr = newaddr; 37 arr->capacity = newcapacity; 38 } 39 40 for (int i = arr->size - 1; i >= pos; --i){ 41 arr->addr[i + 1] = arr->addr[i]; 42 } 43 arr->addr[pos] = data; //新增過後,size變化 44 arr->size++; 45 }
遍歷操作
遍歷一般的作用為列印資料,但這裡並不知道使用者的是什麼資料,這裡由回撥函式進行列印(C語言函式指標和回撥函式)。
1 //遍歷 2 void Foreach(void *arr_, void(*_callback)(void *)){ 3 struct DynamicArray * arr = (struct DynamicArray *)arr_; 4 if (NULL == arr || NULL == _callback){ 5 return; 6 } 7 for (int i = 0; i < arr->size; ++i){ 8 _callback(arr->addr[i]); 9 } 10 }
刪除操作
這裡分為按值刪除和按位置刪除,其中按值操作呼叫了按位置操作的程式碼,因此需要注意size--的問題。另外,按值操作也使用了回撥函式。
1 //刪除(按值刪除,按位置刪除) 2 void DeletePos(void *arr_, int pos){ 3 struct DynamicArray *arr = arr_; 4 if (NULL == arr){ 5 return; 6 } 7 8 for (int i = pos; i < arr->size - 1; ++i){ 9 arr->addr[i] = arr->addr[i + 1]; 10 } 11 12 arr->size--;//size會減小 13 } 14 void DeleteValue(void *arr_, void * data, int(*_compare)(void *, void*)){ 15 struct DynamicArray *arr = arr_; 16 if (NULL == arr || NULL == data || NULL == _compare){ 17 return; 18 } 19 for (int i = 0; i < arr->size; i++){ 20 if (_compare(arr->addr[i], data)){ 21 DeletePos(arr, i); 22 break; 23 } 24 } 25 //arr->size--; DeletePos裡面已經有size--了 26 }
銷燬操作
注意事項:需要先釋放成員函式,再釋放結構體。
1 //銷燬 2 void Destory(void * arr_){ 3 struct DynamicArray *arr = arr_; 4 if (NULL == arr){ 5 return; 6 } 7 if (arr->addr != NULL){ 8 free(arr->addr); 9 arr->addr = NULL; 10 } 11 if (arr != NULL){ 12 free(arr); 13 arr = NULL; 14 } 15 }
元素個數和陣列大小函式
之所以提供這兩個函式,是因為不希望使用者能直接看到我們定義的結構體內部,也不希望使用者可以隨便更改,因此我們各個函式的返回值都是void型別,這裡提供兩個函式,以便使用者可以檢視元素的個數和陣列的大小。
1 int capaArray(void *arr_){ 2 struct DynamicArray *arr = (struct DynamicArray *)arr_; 3 return arr->capacity; 4 } 5 6 int sizeArray(void *arr_){ 7 struct DynamicArray *arr = (struct DynamicArray *)arr_; 8 return arr->size; 9 }
測試
進行測試時,需要自定義列印和比較回撥函式,不需要關心void*data是什麼,僅僅實現自定義資料的比較和列印即可。另外回撥函式使用時不需要任何引數,只需要函式名;另外,測試了結構體和整型動態陣列,可以對動態陣列結構體中的void **addr為二級指標有更好的理解。
1 struct Person{ 2 char name[100]; 3 int age; 4 }; 5 6 // 自定義輸出函式 7 void print(void *data_){ 8 if (NULL == data_){ 9 return; 10 } 11 struct Person *data = (struct Person *)data_; 12 printf("name:%s, age:%d\n", data->name, data->age); 13 } 14 //整型自定義輸出函式,訪問時需要解引用 15 void printInt(void *data_){ 16 if (NULL == data_){ 17 return; 18 } 19 int *data = (int *)data_; 20 printf("age:%d\n", *data); 21 } 22 23 //自定義比較函式 24 int compare(void *d1, void *d2){ 25 if (NULL == d1|| NULL == d2){ 26 return 0; 27 } 28 struct Person *p1 = d1; 29 struct Person *p2 = d2; 30 31 return (strcmp(p1->name, p2->name) == 0 && (p1->age == p2->age)); 32 } 33 34 void test(){ 35 struct Person p1 = {"aaa", 10}; 36 struct Person p2 = { "bbb", 20 }; 37 struct Person p3 = { "ccc", 30 }; 38 struct Person p4 = { "ddd", 40 }; 39 struct Person p5 = { "eee", 50 }; 40 struct Person p6 = { "fff", 60 }; 41 //int p1 = 1; 42 //int p2 = 2; 43 //int p3 = 3; 44 //int p4 = 4; 45 //int p5 = 5; 46 47 48 void * arr = Init(4); 49 Insert(arr, 1, &p1); 50 Insert(arr, 2, &p2); 51 Insert(arr, 3, &p3); 52 Insert(arr, 4, &p4); 53 printf("%d\n", capaArray(arr)); 54 Insert(arr, 100, &p5); 55 printf("%d\n", capaArray(arr)); 56 Insert(arr, 1, &p6); 57 Foreach(arr, print); 58 printf("-----------------\n"); 59 DeleteValue(arr, &p2, compare); 60 Foreach(arr, print); 61 Destory(arr); 62 63 } 64 65 66 int main(){ 67 68 test(); 69 system("pause"); 70 return 0; 71 }