在学习C语言时,结构体指针

常见的数据结构如链表、栈、队列、二叉树等,是绕不开的重点。
本篇博客用通俗易懂的方式,介绍这些概念,结合简单的代码示例,带你逐步掌握这些基础知识。


1. 结构体和指针

我们先来看一眼结构体和指针,不懂这些的话,下面的代码肯定看不懂,
没学过这些的朋友可以先看完这些内容再进行数据结构的学习

结构体就是用来存储不同类型的数据。比如你想保存一个人的姓名、年龄和身高,用结构体就很方便。
个人感觉这里有点象Java里的class,这里叫做struct

#include <stdio.h>

// 定义一个结构体,用于存储人的信息
struct Person {
    char name[50];  // 姓名
    int age;        // 年龄
    float height;   // 身高
};

int main() {
    struct Person person1;  // 创建一个Person类型的变量person1

    // 赋值给person1
    person1.age = 25;
    person1.height = 1.75;
    snprintf(person1.name, sizeof(person1.name), "Alice");

    // 打印信息
    printf("姓名: %s, 年龄: %d, 身高: %.2f\n", person1.name, person1.age, person1.height);

    return 0;
}

指针 是C语言中一个重要的工具,指向内存中的某个地址。用指针操作结构体可以提高效率,特别是当你要操作大的结构体或者需要传递结构体给函数时。

#include <stdio.h>

// 定义结构体
struct Person {
    char name[50];
    int age;
    float height;
};

int main() {
    struct Person person1 = {"Bob", 30, 1.80};  // 定义并初始化结构体
    struct Person *p = &person1;  // 定义指针指向结构体person1

    // 通过指针访问结构体数据
    printf("姓名: %s, 年龄: %d, 身高: %.2f\n", p->name, p->age, p->height);

    return 0;
}

简单解释

  • struct Person *p = &person1;:我们创建了一个结构体指针 p,它指向 person1 这个结构体。
  • p->name:用 -> 来访问指针指向的结构体成员。

2. 链表(Linked List)

链表 是由节点组成的,每个节点包含数据和指向下一个节点的指针。相比数组,链表的优势是可以随时增减节点,非常灵活。

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

// 定义链表节点
struct Node {
    int data;            // 节点数据
    struct Node* next;   // 指向下一个节点的指针
};

// 打印链表
void printList(struct Node* n) {
    while (n != NULL) {  // 循环打印每个节点的数据
        printf("%d ", n->data);
        n = n->next;
    }
}

int main() {
    // 动态分配内存创建三个节点
    struct Node* head = (struct Node*)malloc(sizeof(struct Node));
    struct Node* second = (struct Node*)malloc(sizeof(struct Node));
    struct Node* third = (struct Node*)malloc(sizeof(struct Node));

    // 初始化节点数据
    head->data = 1;  // 第一个节点数据
    head->next = second;  // 第一个节点指向第二个节点

    second->data = 2;  // 第二个节点数据
    second->next = third;  // 第二个节点指向第三个

    third->data = 3;  // 第三个节点数据
    third->next = NULL;  // 第三个节点是最后一个,指向空

    printList(head);  // 打印链表

    return 0;
}

简单解释

  • 链表中的每个节点通过 next 指针连接起来。
  • 你可以随时添加或删除节点,不像数组那样需要预先确定大小。

3. 栈(Stack)

是一种后进先出(LIFO)的数据结构,意思是,最后放进去的元素最先被取出来。常用的操作有 push(入栈)和 pop(出栈)。

#include <stdio.h>
#define MAX 5  // 栈的最大容量

int stack[MAX];
int top = -1;  // 栈顶指针,初始值为-1

// 入栈操作
void push(int data) {
    if (top == MAX - 1) {
        printf("栈满了,不能再入栈\n");
    } else {
        stack[++top] = data;  // 栈顶指针先加1,再将数据放入栈
    }
}

// 出栈操作
int pop() {
    if (top == -1) {
        printf("栈空了,不能再出栈\n");
        return -1;
    } else {
        return stack[top--];  // 先返回栈顶数据,再将栈顶指针减1
    }
}

int main() {
    push(10);  // 入栈10
    push(20);  // 入栈20

    printf("出栈: %d\n", pop());  // 应该输出20
    printf("出栈: %d\n", pop());  // 应该输出10

    return 0;
}

简单解释

  • stack[++top] = data;:将数据入栈,同时移动栈顶指针。
  • return stack[top--];:取出栈顶数据,并将栈顶指针减1。

4. 队列(Queue)

队列 是一种先进先出(FIFO)的数据结构,最早入队的元素最先被取出。主要操作有 enqueue(入队)和 dequeue(出队)。

#include <stdio.h>
#define MAX 5  // 队列的最大容量

int queue[MAX];
int front = -1, rear = -1;  // 队列的前后指针

// 入队操作
void enqueue(int data) {
    if (rear == MAX - 1) {
        printf("队列满了,不能再入队\n");
    } else {
        if (front == -1) front = 0;  // 队列为空时,初始化front
        queue[++rear] = data;  // 入队
    }
}

// 出队操作
int dequeue() {
    if (front == -1 || front > rear) {
        printf("队列空了,不能再出队\n");
        return -1;
    } else {
        return queue[front++];  // 出队并移动front指针
    }
}

int main() {
    enqueue(10);  // 入队10
    enqueue(20);  // 入队20

    printf("出队: %d\n", dequeue());  // 应该输出10
    printf("出队: %d\n", dequeue());  // 应该输出20

    return 0;
}

简单解释

  • queue[++rear] = data;:将数据入队,队尾指针后移。
  • return queue[front++];:出队操作,队首指针前移。

5. 二叉树(Binary Tree)

二叉树 是每个节点最多有两个子节点(左、右)的数据结构,常用于搜索树或表达层次结构。

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

// 定义二叉树节点
struct Node {
    int data;
    struct Node* left;   // 左子节点
    struct Node* right;  // 右子节点
};

// 创建新节点
struct Node* newNode(int data) {
    struct Node* node = (struct Node*)malloc(sizeof(struct Node));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;
}

// 先序遍历(根 -> 左 -> 右)
void preOrder(struct Node* node) {
    if (node == NULL) return;
    printf("%d ", node->data);
    preOrder(node->left);
    preOrder(node->right);
}

int main() {
    struct Node* root = newNode(1);  // 创建根节点
    root->left = newNode(2);  // 左子节点
    root->right = newNode(3);  // 右子节点
    root->left->left = newNode(4);  // 左子节点的左子节点
    root->left->right = newNode(5);  // 左子节点的右子节点

    printf("先序遍历结果:\n");
    preOrder(root);  // 打印顺序:1 2 4 5 3

    return 0;
}

简单解释

  • preOrder 是一种遍历方式,先访问根节点,再依次访问左子节点和右子节点。上面例子中,按照先序遍历的顺序,会依次打印节点的数据。

总结

在这篇文章里,我们学习了C语言中的结构体指针和几种常见的数据结构,包括链表、栈、队列和二叉树。这些数据结构是程序设计中非常基础的概念,
必须掌握,否则之后会寸步难行

学数据结构的几点建议

  1. 理解指针:指针是C语言的灵魂,多写写指针相关的代码,理解它怎么指向内存、怎么操作数据。
  2. 多练习链表:链表的操作虽然看起来简单,但很多细节很容易出错,多加练习有助于提高编程能力。
  3. 掌握栈和队列的应用场景:栈和队列常常在算法题中出现,比如递归、广度优先搜索等,这些数据结构的实现可以加深我们对这些算法的理解。

以上的这些代码,不仅需要看,更需要跟着敲一遍代码,才能理解这其中的结构.
代码是需要肌肉记忆的.


Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐