《深入理解C指针》——指针和结构体

前言

本章继续拓展前面所学的指针概念,包括结构体的数组表示法、结构体的内存分配、结构体内存管理技术以及函数指针的用法。

介绍

结构体的声明:

struct _person {
	char* firstName;
	char* lastName;
	char* title;
	unsigned int age;
}

使用typedef关键字可以简化使用:

struct struct_person {
	char* firstName;
	char* lastName;
	char* title;
	unsigned int age;
} Person;
Person person;

声明Person结构体指针并为它分配内存:

Person *ptrPerson;
ptrPerson = (Person*) malloc(sizeof(Person));

注:为结构体分配内存需要考虑到内存对齐。

结构体释放问题

在为结构体分配内存时,运行时系统不会自动为结构体内部的指针分配内存。类似地,当结构体消失时,运行时系统也不会自动释放结构体内部的指针指向的内存。

typedef struct_person {
	char* firstName;
	char* lastName;
	char* title;
	uint age;
} Person;

当我们声明这个类型的变量或者为这个类型动态分配内存时,三个指针会包含垃圾数据。

void processPerson() {
	Person person;
	...
}

在这里插入图片描述
对结构体的字段进行初始化赋值:

void initializePerson (Person *person. const char* fn.
const char* ln,const char* title, uint age) {
	person->firstName = (char*) malloc(strlen(fn) + 1);
	strcpy(person->firstName, fn);
	person->lastName = (char*) malloc(strlen(ln) + 1);
	strcpy(person->lastName, ln);
	person->title = (char*) malloc(strlen(title)+ 1);
	strcpy(person->title, title);
	person->age = age;
}

void processPerson() {
	Person person;
	initializePerson(&person, "Peter", "Underwood", "Manager", 36);
	...
}
int main() {
	processPerson();
	...
}

在这里插入图片描述
因为这个声明是函数的一部分,函数返回后person的内存会消失。不过,动态分配的内存不会被释放,仍然保存在堆上。不幸的是,我们丢失了它们的地址,因此无法将其释放,从而导致了内存泄漏。

我们需要在processPerson函数结束前释放内存:

void deallocatePerson(Person *person) {
	free(person->firstName);
	free(person->lastName);
	free(person->title);
}

void processPerson( ) {
	Person person;
	initializePerson(&person, "Peter", "Underwood", "Manager"36);
	...
	deallocatePerson(&person);

避免malloc/free开销

重复分配然后释放结构体会产生开销,可能导致巨大的性能瓶颈。
解决办法(池化):为分配的结构体单独维护一个表。当用户不再需要某个结构体实例时,将其返回结构体池中。当我们需要某个实例时,从结构体池中获取一个对象。如果池中没有可用的元素,我们就动态分配一个实例。这种方法高效地维护一个结构体池,能按需使用和重复使用内存。

注:这种技术被广泛应用于诸多领域。

#define LIST_SIZE 10
Person *list[LIST_SIZE];

void initializeList() {
	for(int i = 0; i < LIST_SIZE; i++) {
		list[i] = NULL;
	}
}

//如果存在可用的结构体,这个函数从表中获取一个。将数组的元素跟NULL比较,返回第一个非空的元素,然后将它在list中的位置赋值为NULL。如果没有可用的结构体,那就创建并返回一个新的Person实例。
Person *getPerson() {
	for(int i = 0; i < LIST_SIZE; i++) {
		if(list[i] != NULL) {
			Person *ptr = list[i];
			list[i] = NULL;
			return ptr;
		}
	}
	Person *person = (Person*)malloc(sizeof(Person));
	return person;
}

//这个函数要么将结构体返回表,要么把结构体释放掉。我们会检查数组元素看看有没有NULL值,有的话就将person添加到那个位置,然后返回指针。如果表满了,就用deallocatePerson 函数释放person内的指针,然后释放person,最后返回NULL。
Person *returnPerson (Person *person) {
	for(int i=0; i<LIST_SIZE; i++) {
		if (list[i] == NULL) {
			list[i] = person;
			return person;
		}
	}
	deallocatePerson(person);
	free(person);
	return NULL;
}

//初始化,以及添加到表
initializeList();
Person *ptrPerson;
ptrPerson = getPerson();
initializePerson(ptrPerson,"Ralph", "Fitsgerald", "Mr.", 35);
displayPerson(*ptrPerson);
returnPerson(ptrPerson);

用指针支持数据结构

注:这部分内容主要介绍用指针实现常见的数据结构,考虑到之前已进行过充分的学习,因此此处略去大部分的方法介绍。

单链表

typedef struct _node {
	void *data;
	struct _node *next;
} Node;

typedf struct _linkedList {
	Node *head;
	Node *tail;
	Node *current;
} LinkedList;

用指针支持队列

typedef LinkedList Queue;

用指针支持栈

typedef LinkedList Stack;

用指针支持树

typedef struct _tree {
	void *data;
	struct _tree *left;
	struct _tree *right;
} TreeeNode;

小结

这部分内容很早之前就进行过设计,尤其与指针与结构体之间组成常见的数据结构。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值