一、结构体
声明一个结构体类型: struct 结构体名
结构体名首字母要大写。
struct Student
{
int id;
float score;
char name[20];
};
int main(void)
{
struct Student s;
s.id = i;
s.score = 95.5;
strcpy(s.name, "zhangsan");
printf("%d, %f, %s", (*s).id, (*s).score, (*s).name);
return 0;
}
. 结构体成员运算符 一级 自左向右
-> 指向结构体成员运算符 一级 自左向右
(*s). id <=> s->id,看左边为指针还是变量,指针就可以写为s->id
上面输出也可改为s->id, s->score, s->name
struct Student s = {1, 95.5, "zhangsan"}; //初始化
struct Student s = {.score = 95.5}; //部分初始化 其余的成员会补零
结构体对齐原则
1、结构体按照其最长成员大小对齐,意味着最终的大小必须是最长成员大小的整数倍;
2、结构体成员按照结构体成员声明先后次序依次存放,并且每个成员的首字节放置的位置必须能够整除成员的字节数;
3、如果结构体某个成员的字节数大于CPU的字节数,则最长按照CPU的字节数对齐;
4、用预处理命令#pragma pack(n) 可以强制编译器按照指定的n来对齐,合法的n的数值分别是1、2、4、8、16。
结构体作为函数传参传递时,通常使用指针传参。
结构体变量允许赋值,不是数组。
使用结构体调用函数封装函数时,要注意形参和实参匹配,满足类型匹配。
输出缓冲区: 1.遇见\n, 输出。
2.fflush(stdout)函数,表示强制刷新缓冲区,输出。
3.缓冲区满,输出。
二、用指针处理链表
链表有一个“头指针"变量,图中以head表示,它存放一个地址,该地址指向一个元素。链表中每一个元素称为“结点”,每个结点都应包括两个部分:用户需要用的实际数据和下一个结点的地址。可以看出,head 指向第一个元素;第一个元素又指向第二个元素……直到最后一个元素,该元素不再指向其他元素,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束。
可以看到链表中各元素在内存中可以不是连续存放的。要找某一元素,必须先找到上一个元素,根据它提供的下一元素地址才能找到下一个元素。如果不提供“头指针”(head),则整个链表都无法访问。链表如同一条铁链一样,一环扣一环,中间是不能断开的。
//结构体声明
struct Node
{
int data;
struct Node *next;
};
//头插
void push_front(struct Node *pHead, int n)
{
struct Node *pNew = malloc(sizeof(struct Node));
pNew->next = pHead->next;
pHead->next = pNew;
pNew->data = n;
}
//遍历
void printList(struct Node *pHead)
{
struct Node *p = pHead->next;
while(p != NULL)
{
printf("%d\n", p->data);
p = p->next;
}
}
//计算有效节点
int size(struct Node *pHead)
{
struct Node *p = pHead->next;
int counter = 0;
while(p != NULL)
{
++counter;
p = p->next;
}
return counter;
}
//判空
int isEmpty(struct Node *pHead)
{
return pHead->next == NULL;
}