国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C語言數(shù)據(jù)結(jié)構(gòu)一:動態(tài)數(shù)組

這篇具有很好參考價值的文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)一:動態(tài)數(shù)組。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一、概念認(rèn)知

先說一個概念:數(shù)組是一段連續(xù)的內(nèi)存空間。存儲相同的數(shù)據(jù)類型。

數(shù)組的兩個關(guān)鍵點:

  1. 連續(xù)內(nèi)存;
  2. 相同類型。

第一個特征:起點。

首先連續(xù)內(nèi)存:所以為了找到動態(tài)數(shù)組我們必須找到一個首元素地址。(內(nèi)存首地址。)

如果不知道首地址,那無法找到并操作內(nèi)存空間。

第二個特征:容量。

知道首地址了,我們需要知道元素的個數(shù)。動態(tài)數(shù)組:動態(tài),可以動態(tài)插入、刪除、擴(kuò)展容量。所以我們必須有個初始容量。開辟多大空間,放多少個元素。除了容量,最好還有一個狀態(tài)量:表示當(dāng)前的數(shù)據(jù)量:也就是說長度。

  • 容量說的是:可以放多少數(shù)據(jù)。
  • 長度說的是:當(dāng)前有多少數(shù)據(jù)。

我的錢包(=銀行)(容量)無限大,而我當(dāng)前擁有的錢有限(長度)。

第三個特征:類型。

我們雖然有了首地址,也有了元素個數(shù),但是我們還是不知道開辟多大空間。因為我們還需要元素的大小。元素所占內(nèi)存的大小,其實就是元素的類型??墒牵?/p>

C語言無法動態(tài)獲取數(shù)據(jù)的類型。

好在我們還有void*類型可以指向任意類型的指針。

所以我們雖然不知道具體元素的類型,但是我們可以定義一個通用的指針類型,指向我們的元素的地址即可。所以我們的元素就是地址。

所以我們的元素類型就是void*。所以我們的數(shù)組指針就是void**類型。


?二、實現(xiàn)

動態(tài)數(shù)組其實就是一種結(jié)構(gòu)體。?一種自定義類型。

2.1定義數(shù)據(jù)結(jié)構(gòu)。

先寫數(shù)據(jù)信息結(jié)構(gòu)。

?首地址,就像存儲字符串?dāng)?shù)組一樣。

char * arr[] = {"aaa", "bbb", "ccc"};

?arr數(shù)組中每個元素都是char*類型,那么arr如果寫入結(jié)構(gòu)體中,可以定義為:

struct DemoStruct
{
    char ** arr;
    int b;
}

?同樣道理:元素類型為void*。那么定義數(shù)組則為void** arr;

//1. 先把所需要的數(shù)據(jù)信息結(jié)構(gòu)定義下來
struct DynamicArray
{
	//數(shù)組存儲元素的空間的首地址
	void **addr;
	//存儲數(shù)據(jù)的內(nèi)存中最大能夠容納多少元素
	int capacity; //容量
	//當(dāng)前存儲數(shù)據(jù)的內(nèi)存中有多少個元素
	int size; //大小
};

2.2?頭文件

#pragma once

#include<stdlib.h>
#include<stdio.h>
#include<string.h>

#ifdef __cplusplus
extern "C"{
#endif

	//1. 先把所需要的數(shù)據(jù)信息結(jié)構(gòu)定義下來
	struct DynamicArray
	{
		//數(shù)組存儲元素的空間的首地址
		void **addr;
		//存儲數(shù)據(jù)的內(nèi)存中最大能夠容納多少元素
		int capacity; //容量
		//當(dāng)前存儲數(shù)據(jù)的內(nèi)存中有多少個元素
		int size; //大小
	};

	//初始化數(shù)組
	struct DynamicArray *Init_DynamicArray(int capacity);
	//插入元素
	void Insert_DynamicArray(struct DynamicArray *arr, int pos, void *data);
	//遍歷
	void Foreach_DynamicArray(struct DynamicArray *arr, void(*_callback)(void *));
	//位置刪除
	void RemoveByPos_DynamicArray(struct DynamicArray *arr, int pos);
	//按值刪除
	void RemoveByValue_DynamicArray(struct DynamicArray *arr, void *data, int(*compare)(void*, void *));
	//銷毀數(shù)組
	void Destroy_DynamicArray(struct DynamicArray *arr);

#ifdef __cplusplus
}
#endif

頭文件中,我們要寫入關(guān)于這個數(shù)組結(jié)構(gòu)體的一些方法聲明,然后去.c源文件中去實現(xiàn)。


2.3初始化

首先是初始化數(shù)組。給數(shù)組分配內(nèi)存,也就是給這個結(jié)構(gòu)體分配內(nèi)存。

我們的動態(tài)數(shù)組初始化也是:struct DynamicArray * arr = malloc(sizeof(struct DynamicArray));

arr這個指針指向這個數(shù)組的結(jié)構(gòu)體:即動態(tài)數(shù)組。

動態(tài)數(shù)組其實就是一種結(jié)構(gòu)體。?

然后這個結(jié)構(gòu)體的一個屬性:首地址:首地址指向整個數(shù)組地址。為整個數(shù)組開辟空間。就像是char * arr = malloc(sizeof(char*)*Num)一樣操作。

arr->addr = malloc(sizeof(元素類型)* Num);這里Num為Capacity。元素類型為void*由使用方指定。

arr->addr = malloc(sizeof(void*)*arr->capacity);

此時沒有插入值,所以size為零。

//初始化數(shù)組
struct DynamicArray *Init_DynamicArray(int capacity)
{
	if (capacity <= 0)
	{
		return NULL;
	}

	struct DynamicArray *arr = malloc(sizeof(struct DynamicArray));
	if (NULL == arr)
	{
		return NULL;
	}
	arr->capacity = capacity;
	arr->addr = malloc(sizeof(void *)*arr->capacity);
	arr->size = 0;

	return arr;
}

2.4插入元素?

此時注意我們輸入函數(shù)的是結(jié)構(gòu)體指針。

//插入元素
void Insert_DynamicArray(struct DynamicArray *arr, int pos, void *data)
{

	if (NULL == arr)
	{
		return;
	}

	if (NULL == data)
	{
		return;
	}

	if (pos < 0 || pos > arr->size)
	{
        // 如果插入時,pos大于當(dāng)前的長度,則直接把數(shù)據(jù)放在數(shù)組最后面。
		pos = arr->size;
	}

	//判斷空間是否足夠
	if (arr->size >= arr->capacity)
	{

		//1. 申請一塊更大的內(nèi)存空間
		int newcapacity = arr->capacity * 2;   // C#中動態(tài)數(shù)組也是如此操作的。
		void **newspace = malloc(sizeof(void *)* newcapacity);

		//2. 將原來空間的數(shù)據(jù)拷貝到新空間
		memcpy(newspace, arr->addr, sizeof(void *)* arr->capacity);

		//3. 釋放原來空間的內(nèi)存
		free(arr->addr);

		//4. 更新addr指向
		arr->addr = newspace;
		arr->capacity = newcapacity;

	}


	//移動元素,給pos位置空出位置來
	for (int i = arr->size - 1; i >= pos; --i)
	{
		arr->addr[i + 1] = arr->addr[i];
	}

	//將新元素插入到pos位置
	arr->addr[pos] = data;
	arr->size++;
}

2.5遍歷查看?

遍歷時,我們不知道元素的內(nèi)部結(jié)構(gòu)(數(shù)據(jù)結(jié)構(gòu)),所以這個打印查看函數(shù),需要使用方提供,

使用方調(diào)用我們這個方法,我們這個方法反向回調(diào)使用方的提供的函數(shù)。因為我們不知道使用方使用什么類型,使用方自己知道如何打印自己的數(shù)據(jù)結(jié)構(gòu)(數(shù)據(jù)信息)。

回調(diào)函數(shù)定義方法:

返回值類型(*回調(diào)函數(shù)名稱)(參數(shù)列表)

//遍歷
void Foreach_DynamicArray(struct DynamicArray *arr, void(*_callback)(void *))
{
	if (NULL == arr)
	{
		return;
	}

	if (NULL == _callback)
	{
		return;
	}

	for (int i = 0; i < arr->size; ++i)
	{
		_callback(arr->addr[i]);
	}


}

2.6刪除

//位置刪除
void RemoveByPos_DynamicArray(struct DynamicArray *arr, int pos)
{

	if (NULL == arr)
	{
		return;
	}

	if (pos < 0 || pos > arr->size - 1)
	{
		return;
	}


	for (int i = pos; i < arr->size - 1; ++i)
	{
		arr->addr[i] = arr->addr[i + 1];
	}


	arr->size--;
}
//按值刪除
void RemoveByValue_DynamicArray(struct DynamicArray *arr, void *data, int(*compare)(void*, void *))
{
	if (NULL == arr)
	{
		return;
	}

	if (NULL == data)
	{
		return;
	}

	if (NULL == compare)
	{
		return;
	}


	for (int i = 0; i < arr->size; ++i)
	{
		if (compare(arr->addr[i], data))
		{
			RemoveByPos_DynamicArray(arr, i);
			break;
		}
	}

}

2.7銷毀

//銷毀數(shù)組
void Destroy_DynamicArray(struct DynamicArray *arr)
{
	if (NULL == arr)
	{
		return;
	}

	if (arr->addr != NULL)
	{
		free(arr->addr);
		arr->addr = NULL;
	}

	free(arr);
	arr = NULL;
}

原則上,誰molloc的,誰free,但是具體元素數(shù)據(jù)指向哪里,我們并不知道,且使用方知道,使用方也必須開辟,使用方開辟,那就由使用方釋放。我們只free我們自己開辟的開辟從外到內(nèi),釋放是從內(nèi)到外:先釋放內(nèi)部屬性的指針:arr->addr的內(nèi)存,然后再釋放整個結(jié)構(gòu)體指針。

使用方測試:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"DynamicArray.h"


struct Person
{
	char name[64];
	int age;
};

void myPrint(void *data)
{
	struct Person *person = (struct Person *)data;
	printf("Name:%s Age:%d\n", person->name,person->age);
}

int myCompare(void *d1,void *d2)
{
	struct Person *p1 = (struct Person *)d1;
	struct Person *p2 = (struct Person *)d2;

	return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age;
}


void test()
{
	//創(chuàng)建動態(tài)數(shù)組
	struct DynamicArray *da = Init_DynamicArray(5);
	//動態(tài)數(shù)組添加元素
	struct Person p1 = { "aaa", 10 };
	struct Person p2 = { "bbb", 20 };
	struct Person p3 = { "ccc", 30 };
	struct Person p4 = { "ddd", 40 };
	struct Person p5 = { "eee", 50 };
	struct Person p6 = { "fff", 60 };
	
	Insert_DynamicArray(da, 0, &p1);
	Insert_DynamicArray(da, 0, &p2);
	Insert_DynamicArray(da, 0, &p3);
	Insert_DynamicArray(da, 1, &p4);
	Insert_DynamicArray(da, 1, &p5);

	printf("capacity:%d\n",da->capacity);
	Insert_DynamicArray(da, 100, &p6);  //3 5 4 2 1 6
	printf("capacity:%d\n", da->capacity);

	Foreach_DynamicArray(da, myPrint);

	printf("---------------\n");
	RemoveByPos_DynamicArray(da,2);//3 5 2 1 6
	Foreach_DynamicArray(da, myPrint);

	printf("---------------\n");
	struct Person pDel = { "aaa", 10 };
	RemoveByValue_DynamicArray(da, &pDel, myCompare);

	//da->addr = NULL;

	Foreach_DynamicArray(da, myPrint);

	//銷毀
	Destroy_DynamicArray(da);
}

int main(){

	test();

	system("pause");
	return EXIT_SUCCESS;
}

反思:

這樣的一個寫法有個問題:我們直接把我們的結(jié)構(gòu)體暴露了在外面,任何人都可以修改訪問。這樣是不安全的。

我們初始化函數(shù)時,返回的是我們的結(jié)構(gòu)體指針。直接把結(jié)構(gòu)體返回了。

解決方案是我們的函數(shù)體返回時,返回void*,無類型指針。這樣其他函數(shù)插入?yún)?shù)時,也是void*類型。然后在函數(shù)里面做個強制類型轉(zhuǎn)換即可。避免別人在外面直接調(diào)用結(jié)構(gòu)指針。

此時考慮面向?qū)ο蟮?strong>封裝特性。文章來源地址http://www.zghlxwxcb.cn/news/detail-742511.html

到了這里,關(guān)于C語言數(shù)據(jù)結(jié)構(gòu)一:動態(tài)數(shù)組的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包