1、位字段(Bit-fields)也是一种数据结构,允许在结构体(struct)或联合体(union)中定义其成员占用特定的位数。对于需要精确控制内存布局或处理硬件寄存器映射等场景非常有用。位字段使得开发者能够定义小于一个字节的变量,从而更有效地利用内存空间。位字段的声明方式是在结构体或联合体的成员后面加上冒号和该成员所占的位数。
测试代码:
#include <stdio.h>
// 温馨提示:本程序不能在实际的硬件环境中运行,仅做示例。
// 位字段
// 硬件寄存器结构体
struct hardware_registers {
unsigned int control: 1; // 控制位
unsigned int mode: 2; // 模式选择位
unsigned int speed: 3; // 速度控制位
unsigned int : 0; // 填充位,不占用空间
unsigned int status: 1; // 状态位,只读
// 其他寄存器...
// unsigned int another_register: 8;
};
// 硬件寄存器地址(硬件的物理地址,代码无法直接运行,0x12345678不是一个有效的硬件地址。)
volatile struct hardware_registers *hardware = (volatile struct hardware_registers *)0x12345678;
// 写入硬件寄存器的函数
void write_hardware_register(void) {
// 设置控制位为1,模式为2(从0开始计数),速度为4(同样从0开始)
hardware->control = 1;
hardware->mode = 2;
hardware->speed = 4;
// 检查写入是否成功(需要读取状态寄存器)
// check_hardware_status();
}
// 读取硬件状态并打印的函数
void read_hardware_status(void) {
// 读取状态位
if (hardware->status) {
printf("Hardware is in active state.\n");
} else {
printf("Hardware is in inactive state.\n");
}
}
int main() {
// 写入硬件寄存器
write_hardware_register();
// 读取并打印硬件状态
read_hardware_status();
return 0;
}
.........................................................................................................................................................
2、邻接表通过为每个顶点维护一个链表来存储与该顶点相邻的所有顶点,从而高效地存储和访问图中的边。
测试代码:
#include "date.h"
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTICES 100
// AdjListNode 表示邻接表中的一个节点,包含目标顶点和指向下一个节点的指针。
typedef struct AdjListNode {
int dest;
struct AdjListNode* next;
} AdjListNode;
// AdjList 包含一个指向邻接表头部的指针。
typedef struct AdjList {
struct AdjListNode* head;
} AdjList;
// Graph是一个结构体(struct),表示图论中的图。
// 结构体通常会包含顶点的数组(或指针数组)以及某种形式表示边(比如:邻接矩阵、邻接表等)。
// Graph 包含顶点数和一个指向邻接表数组的指针。
typedef struct Graph {
int V;
struct AdjList* array;
} Graph;
// 函数声明
void addEdge(Graph* graph, int src, int dest);
void printGraph(Graph* graph);
void DFS(Graph* graph, int v, int visited[]);
void BFS(Graph* graph, int s);
// newAdjListNode 函数:创建一个新的邻接表节点,其中 dest 参数表示目标顶点。
// 使用动态内存分配分配一个新的节点,并将目标顶点赋值给新节点的 dest 字段,
// 并将 next 字段设为 NULL。最后返回新节点的指针。
// 函数定义开始,接收一个整型参数dest,返回一个指向AdjListNode类型的指针
AdjListNode* newAdjListNode(int dest) {
// 使用malloc函数动态分配内存给一个新的AdjListNode结构体实例
// sizeof(AdjListNode)计算AdjListNode结构体所需的总字节数
// (AdjListNode*)类型转换,将void*类型的malloc返回值转换为AdjListNode*类型
// 分配的内存地址被存储在newNode指针中
AdjListNode* newNode = (AdjListNode*) malloc(sizeof(AdjListNode));
// 将传入的dest参数赋值给新节点的dest成员变量
// dest成员变量通常用于存储与该节点相关联的顶点或边的信息
newNode->dest = dest;
// 初始化新节点的next指针为NULL
// next指针用于指向链表中的下一个节点,初始时为NULL表示链表结束
newNode->next = NULL;
// 返回新创建的节点指针
return newNode;
}
// createGraph 函数:创建一个新的图,其中 vertices 参数表示图中顶点的数量。
// 首先分配一个 Graph 结构体的空间,然后设置顶点的数量。
// 然后再分配一个邻接表数组的空间,并对每个邻接表头部进行初始化。
// 最后返回创建的图的指针。
// vertices 作为参数,表示图中顶点的数量。函数返回一个指向 Graph 类型的指针。
Graph* createGraph(int vertices) {
// 为 Graph 结构体分配内存,并将返回的指针存储在 graph 变量中。(Graph*) 类型转换,将 void* 转换(Graph*) 指针类型。
Graph* graph = (Graph*) malloc(sizeof(Graph));
// 将图的顶点数(vertices)存储在 graph 结构的 V 成员中。
graph->V = vertices;
// 为AdjList 类型的数组分配内存,数组的大小与图的顶点数相同。
// 每个 AdjList 元素(为一个链表头,每个链表包含从该顶点出发的所有边的目标顶点)对应于图中的一个顶点。
graph->array = (AdjList*) malloc(vertices * sizeof(AdjList));
// 遍历邻接表数组,将每个 AdjList 的 head 成员初始化为 NULL。
// 创建图时,没有任何边存在(每个顶点的邻接链表都是空的)。
for (int i = 0; i < vertices; ++i)
graph->array[i].head = NULL;
return graph;
}
// addEdge 函数:向图中添加一条边,从顶点 src 到 dest。
// 首先创建一个新的邻接表节点,然后将该节点插入到 src 对应的邻接表中,
// 由于图是无向图,需要在 dest 对应的邻接表中插入一条边从 dest 指向 src。
void addEdge(Graph* graph, int src, int dest) {
// 将 dest 成员设置为给定的参数值,然后返回指向该节点的指针,创建一个新节点,
// 该节点的 dest 成员被设置为当前边的目标顶点 dest。
AdjListNode* newNode = newAdjListNode(dest);
// 将新节点的 next 指针设置为源顶点 src 当前邻接链表的头节点。
// 将源顶点 src 的邻接链表头指针 head 更新为新节点,从而将该新节点添加到链表的开头。
// 表示从源顶点 src 到目标顶点 dest 有一条边。
newNode->next = graph->array[src].head;
graph->array[src].head = newNode;
// 将新节点添加到目标顶点 dest 的邻接链表中。
// 表示从目标顶点 dest 到源顶点 src 也有一条边,无向图的特性。
newNode = newAdjListNode(src);
newNode->next = graph->array[dest].head;
graph->array[dest].head = newNode;
}
// DFS 函数:深度优先搜索。
// 从给定的起始顶点 v 开始,递归地遍历与它相邻的顶点,直到没有未访问的相邻顶点。
// 递归函数将递归访问每个相邻顶点,并用 visited 数组跟踪已访问的顶点。
void DFS(Graph* graph, int v, int visited[]) {
// 将整数数组 visited 中索引为 v 的元素设置为 1,表示顶点 v 已经被访问过。
visited[v] = 1;
// 打印当前正在访问的顶点 v 的值
printf("%d", v);
// 获取图中顶点 v 的邻接链表的头节点,并将其地址存储在指针 temp 中。
AdjListNode* temp = graph->array[v].head;
// while 循环遍历顶点 v 的所有邻接顶点。
// 循环继续执行,直到 temp 变为 NULL,遍历完所有邻接顶点。
while (temp) {
// 获取当前邻接链表节点 temp 的目标顶点 next。
int next = temp->dest;
// 检查该顶点是否已经被访问过(即 visited[next] 是否为 0)。
if (!visited[next])
// 如果 next 顶点尚未被访问,则递归调用 DFS 函数,以 next 为新的起始顶点继续搜索。
DFS(graph, next, visited);
// 将 temp 更新为当前节点的下一个节点,以下一次循环迭代中处理。
// 确保能够遍历完顶点 v 的所有邻接顶点。
temp = temp->next;
}
// 当 temp 变为 NULL 时,while 循环结束,表示顶点 v 的所有邻接顶点都已经被检查并已经被递归访问过。
}
// BFS 函数:广度优先搜索。
// 从给定的起始顶点 s 开始,利用队列依次访问和处理与起始顶点相邻的顶点,直到队列为空。
// while循环,函数将访问起始顶点的相邻顶点,并将们添加到队列中,同时使用 visited 数组跟踪已访问的顶点。
void BFS(Graph* graph, int s) {
// 声明整数数组 visited,其大小为图的顶点数 graph->V。用于跟踪哪些顶点已经被访问过。
int visited[graph->V];
// 循环遍历 visited 数组,将所有元素初始化为 0,表示开始时没有顶点被访问过。
for (int i = 0; i < graph->V; i++)
visited[i] = 1;
// 声明整数数组 queue 作为队列,用于存储待访问的顶点。
// 声明两个整数 front 和 rear 分别作为队列的前端和尾端索引,初始时都设为 0。
int queue[graph->V], front = 0, rear = 0;
// 将起始顶点 s 标记为已访问(将 visited[s] 设置为 1)。
// 将起始顶点 s 加入队列的尾部(通过增加 rear 索引并将 s 赋值给 queue[rear])。
visited[s] = 1;
queue[rear++] = s;
// 循环持续进行,直到队列为空(即 front 等于 rear)。
// 在队列不为空的情况下,执行循环体内的代码。
while (front < rear) {
// 从队列的前端取出一个顶点 s(通过增加 front 索引并读取 queue[front] 的值),并打印出来。表示顶点 s 已经被访问。
s = queue[front++];
printf("%d", s);
// 对于每个邻接顶点,如果未被访问(即 visited[temp->dest] 为 0),则将其标记为已访问,并将其加入队列的尾部。
// 通过遍历 s 的邻接链表(由 graph->array[s].head 指向)实现,其中 temp 是指向邻接链表节点的指针。
AdjListNode* temp = graph->array[s].head;
while (temp) {
if (!visited[temp->dest]) {
visited[temp->dest] = 1;
queue[rear++] = temp->dest;
}
temp = temp->next;
}
}
}
// 释放链表中所有节点
void freeAdjList(AdjList* adjList) {
// 指向AdjListNode类型的指针temp。用于临时存储链表中当前要释放的节点的地址。
AdjListNode* temp;
// 邻接表的头节点不为空(即链表中还有节点未被释放),循环继续执行。
while (adjList->head != NULL) {
// 将adjList->head(即当前链表的头节点)的地址赋值给temp。
// temp指向当前要释放的节点。
temp = adjList->head;
// adjList->head更新为adjList->head->next,让头节点指向下一个节点。
adjList->head = adjList->head->next;
// 释放temp所指向的内存
free(temp);
}
}
// 释放图占用的所有内存
void freeGraph(Graph* graph) {
// 释放每个顶点的邻接表
for (int i = 0; i < graph->V; i++) {
freeAdjList(&graph->array[i]);
}
// 释放顶点数组
free(graph->array);
// 释放图结构本身
free(graph);
}
int main() {
int time = getTime();
int V = 4; // 图的顶点数
Graph* graph = createGraph(V);
printf("Adding edges:\n");
addEdge(graph, 0, 1);
addEdge(graph, 0, 2);
addEdge(graph, 1, 2);
addEdge(graph, 2, 0); // 冗余,无向图已经通过addEdge添加了从0到2和从2到0的边。
addEdge(graph, 2, 3);
addEdge(graph, 3, 3); // 添加一个自环。
printf("\nGraph created. Following is Depth First Traversal (DFS):\n");
int* visited = (int*)malloc(V * sizeof(int));
if (visited == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < V; i++)
if (visited[i] == 0)
DFS(graph, i, visited); // 从每个未访问的顶点开始DFS
// 重置visited数组进行BFS
for (int i = 0; i < V; i++)
visited[i] = 0;
printf("\nFollowing is Breadth First Traversal (BFS), starting from vertex 2:\n");
BFS(graph, 2); // 从顶点2开始BFS
// 清理分配的内存
freeGraph(graph);
return 0;
}
运行结果如下:
标签:temp,int,graph,基础,二十七,dest,顶点,C语言,节点 From: https://blog.csdn.net/wehpd/article/details/141726501