【数据结构与算法】第2篇:C语言核心机制回顾(一):指针、数组与结构体

【数据结构与算法】第2篇:C语言核心机制回顾(一):指针、数组与结构体 一、写在前面上一篇我们把环境搭好了这一篇开始花两三篇的时间把C语言里和数据结构最相关的知识点过一遍。如果你已经对指针、结构体很熟了这篇可以快速浏览当个复习。但如果这些概念还模糊建议耐心看完因为后面的链表、树全都要靠它们。二、指针C语言的灵魂2.1 什么是指针指针就是一个变量它存的是地址不是具体的数值。cint a 10; int *p a; // p存的是a的地址画个图理解一下text变量a: [10] 地址假设是 0x1000 指针p: [0x1000] 地址假设是 0x2000p里面存的是0x1000通过*p就能找到a拿到10。2.2 指针的运算指针的加减运算不是简单的地址加1而是加上它所指向类型的大小。cint arr[] {10, 20, 30, 40}; int *p arr; // p指向arr[0] printf(%p\n, p); // 假设输出 0x1000 printf(%p\n, p1); // 输出 0x1004因为int占4字节关键点p1跳过了4个字节指向了arr[1]p也是同样的效果指针向后移动一个元素这个特性特别重要后面遍历数组、操作链表节点全靠它。2.3 指针和数组的关系数组名在很多情况下可以当作指针用但它们不完全一样。cint arr[5] {1, 2, 3, 4, 5}; int *p arr; printf(%d\n, arr[2]); // 输出3 printf(%d\n, p[2]); // 也是3p可以像数组一样用 printf(%d\n, *(arr2)); // 也是3arr可以像指针一样用关键区别arr是常量不能改变指向比如arr会报错p是变量可以随意改比如p是合法的访问数组元素的本质arr[i]其实等价于*(arr i)。三、结构体自定义数据类型3.1 基本用法结构体可以把多个不同类型的数据打包在一起。cstruct Student { char name[20]; int age; float score; }; // 使用 struct Student s1 {张三, 20, 85.5}; printf(%s的分数是%.1f\n, s1.name, s1.score);3.2 结构体指针操作结构体时经常用指针因为传指针比传整个结构体效率高。cstruct Student s1 {李四, 21, 90.0}; struct Student *p s1; // 两种访问方式 printf(%s\n, (*p).name); // 写法麻烦不常用 printf(%s\n, p-name); // 箭头操作符推荐记住p-name等价于(*p).name。3.3 内存对齐重点这是一个容易踩坑的点。结构体的大小不是成员大小的简单相加。cstruct Example1 { char c; // 1字节 int i; // 4字节 }; printf(%d\n, sizeof(struct Example1)); // 输出8不是5为什么是8因为编译器会做内存对齐char c占1字节但为了int i对齐到4字节边界c后面会空出3个字节填充最终占134 8字节cstruct Example2 { int i; // 4字节 char c; // 1字节 }; printf(%d\n, sizeof(struct Example2)); // 输出8还是8这个也是8但原因不同int占4char占1整体大小需要是最大成员4的倍数所以补3个字节还是8。cstruct Example3 { char c1; // 1 char c2; // 1 int i; // 4 }; printf(%d\n, sizeof(struct Example3)); // 输出8112填充4对齐规则每个成员相对于结构体起始地址的偏移量必须是该成员大小的整数倍结构体的总大小必须是最大成员大小的整数倍写数据结构时要注意如果结构体内有指针指针在64位系统占8字节对齐会影响整个结构体的大小。四、typedef给类型起别名typedef能让代码更简洁尤其是后面写链表不用每次都写struct Node。c// 不用typedef struct Node { int data; struct Node *next; }; struct Node n1; // 每次都要写struct // 用typedef typedef struct Node { int data; struct Node *next; } Node; Node n1; // 简洁多了还有一种写法更常见ctypedef struct Node { int data; struct Node *next; } Node, *PNode; // PNode是指向Node的指针类型 Node n1; PNode p n1; // p是Node*类型五、一个综合例子把前面学的串起来定义一个学生结构体用指针操作c#include stdio.h #include string.h typedef struct Student { char name[20]; int age; float score; } Student; void printStudent(Student *p) { printf(姓名%s年龄%d分数%.1f\n, p-name, p-age, p-score); } int main() { Student s1; strcpy(s1.name, 王小明); s1.age 19; s1.score 88.5; Student *p s1; printStudent(p); // 演示指针运算数组 Student class[3] { {张三, 20, 85}, {李四, 21, 90}, {王五, 19, 87} }; Student *q class; printf(第一个学生%s\n, q-name); printf(第二个学生%s\n, (q1)-name); // 指针移动 return 0; }输出text姓名王小明年龄19分数88.5 第一个学生张三 第二个学生李四六、小结这一篇讲了三个核心概念概念要点指针存地址加减运算跳过的字节数取决于指向的类型数组数组名可当指针用但是常量不能改指向结构体自定义类型大小要考虑内存对齐typedef简化类型名让代码更简洁这些是写数据结构的基石下一篇我们会讲动态内存分配malloc和free学会之后就可以开始手写链表了。七、思考题下面的结构体占多少字节假设是32位系统指针4字节ctypedef struct { char a; int *p; char b; } Test;为什么结构体要有内存对齐不直接按顺序存