结构体
为什么需要结构体
为了表示一些复杂的事物,而普通的基本类型无法满足实际要求
为什么叫结构体
把一些基本类型数据组合在一起形成的一个新的复合数据类型
如何定义一个结构体
3种方式,最好使用第一种
#include <stdio.h>
struct Student //第一种方式 这只是定义了一个新的数据类型,并没有定义变量
{
int age;
float score;
char name[100];
};
struct Student2 //第二种方式
{
int age;
float score;
char name[100];
}st2;
struct //第三种方式
{
int age;
float score;
char name[100];
}st3;
怎么去使用结构体变量
赋值和初始化
定义的同时可以整体赋初值
如果定义完之后,则只能单个的赋初值
#include <stdio.h>
struct Student
{
int age;
float score;
char sex;
};
int main()
{
struct Student st = {80, 66.6, 'F'}; //初始化 定义的同时赋初值
struct Student st2;
st2.age = 10;
st2.score = 88;
st2.sex = 'F';
printf("%d %f %c\n", st.age, st.score, st.sex);
printf("%d %f %c\n", st2.age, st2.score, st2.sex);
return 0;
}
如何取出结构体变量中的每一个成员
1.结构体变量名.成员名
2.指针变量名–>成员名 (第二种方式更常用)
——*在计算机内部会被转化(指针变量名).成员名的方式来执行
#include <stdio.h>
struct Student
{
int age;
float score;
char sex;
};//分号不能省略
int main()
{
struct Student st = {80, 66.6F, 'F'};
struct Student *pst = &st; //&st 不能够改成st
pst->age = 88; //第二种方式
st.score = 66.6f; //第一种方式
//66.6在C中默认是double类型,如果希望一个实数是float类型,则必须在末尾加F或f,
//因此66.6是double,66.6f是float
printf("%d %.2f\n", st.age, pst->score);
return 0;
}
/*
1.pst->age 在计算机内部会被转化(*pst).age 这是一种硬性规定
2.所以pst->age 等价于(*pst).age 也等价于 st.age
3.我们之所以知道pst->age 等价于 st.age, 是因为pst->age是被转换成了(*pst).age来执行
4.pst->age 的含义:pst所指向的那个结构体变量中age的成员
*/
结构体变量和结构体指针变量作为函数参数传递问题
推荐使用结构体指针变量作为函数参数来传递
#include <stdio.h>
#include <string.h>
struct Student
{
int age;
char sex;
char name[100];
}; //分号不能省略
void InputStudent(struct Student * pstu); //函数形参涉及结构体类型,要在声明结构体后再声明函数
void OutputStudent(struct Student *pst);
int main()
{
struct Student st;
InputStudent(&st);//对结构体变量输入,必须发送st的地址
OutputStudent(&st);//对结构体变量输出,可以发送st的地址也可以发送其内容
//但为了减少内存的耗费也为了提高执行速度,推荐使用地址
return 0;
}
void InputStudent(struct Student * pstu)//pstu只占4个字节
{
(*pstu).age = 10; //等价于st.age
strcpy(pstu->name, "张三");
pstu->sex = 'F';
}
/*
//本函数无法修改主函数st的值,error
void InputStudent(*struct Student stu)
{
stu.age = 10;
strcpy(stu.name, "张三"); //不能写成stu.name = "张三"
stu.sex = 'F';
}
*/
void OutputStudent(struct Student *pst)
{
printf("%d %c %s\n", pst->age, pst->sex, pst->name);
}
结构体变量的运算
结构体变量不能相加减乘除,但是结构体变量可以相互赋值
struct Student
{
int age;
float score;
char sex;
};//分号不能省略
struct Student st1;
struct Student st2;
st1+st2; st1-st2; st1*st2; st1/st2; //都是错误的
st1 = st2; //正确
动态构造存放学生信息的结构体数组
动态构造一个数组,存放学生信息,然后按分数排序输出
#include <stdio.h>
#include <malloc.h>
struct Student
{
int age;
float score;
char name[100];
};
void Output(int i, struct Student * parr);
void sort(struct Student * parr , int len);
void Iutput(int len, struct Student * parr);
int len;
struct Student * parr;
int i,j;
struct Student t;
int main()
{
printf("请输入学生个数:\n");
printf("len = ");
scanf("%d", &len);
parr = (struct Student *)malloc(len * sizeof(struct Student));//构造动态的一维数组
Iutput(len, parr);
//按学生的成绩升序排序
sort(parr, len);
printf("\n\n学生的信息是:\n");
//输出
Output(len, parr);
return 0;
}
void Output(int len, struct Student * parr)
{
//输出
for(i=0; i<len; ++i)
{
printf("第%d个学生信息:\n", i+1);
printf("age = %d\n", parr[i].age);
printf("name = %s\n", parr[i].name);
printf("score = %.2f\n", parr[i].score);
printf("\n");
}
}
void Iutput(int len, struct Student * parr)
{
for(i=0; i<len; ++i)
{
printf("请输入%d个学生信息:", i+1);
printf("age = ");
scanf("%d", &parr[i].age);
printf("name = ");
scanf("%s",parr[i].name); //name是数组名,本身就已经是数值首元素的地址,所以parr[i].name不能够改为&parr[i].name
printf("score = ");
scanf("%f", &parr[i].score);
}
}
void sort(struct Student * parr , int len )
{
for(i=0; i<len-1; ++i)
{
for(j=0; j<len-1-i; ++j)
{
if(parr[j].score > parr[j+1].score) //比较成绩 互换整体
{
t = parr[j];
parr[j] =parr[j+1];
parr[j+1] = t;
}
}
}
}
函数声明要在结构体之后
error: conflicting types for '***'
error: previous implicit declaration of '***' was here
原因一:
没有函数声明,且函数定义在主函数之后;
原因二:
头文件的被循环引用,在引用时考虑清楚包含顺序
原因三:
头文件函数声明和函数定义参数不同
头文件中声明 void test(const char * buf);
在定义时写作 void test(char * buf);
原因四:函数使用的参数类型是自定义类型(如结构体),而自定义类型的定义在函数的声明和函数定义之间,由于在函数声明时,结构体并没有被定义,不被系统识别为结构体,而后面定义函数时,结构体已经定义,系统将其识别为结构体,导致系统认为声明和定义使用的是不同的参数类型;所以才会出现上述问题;
算法定义
通俗定义
解题的方法和步骤
狭义定义
1.对存储数据的操作
2.对不同的存储结构,要完成某一个功能所执行的操作是不一样的
exp:要输出数组中所有的元素的操作 和 要输出链表中所有元素的操作肯定是不一样的
这说明算法是依附于存储结构的,不同的存储结构所执行的算法是不一样的
广义定义
1.广义的算法也叫泛型
2.无论数据是如何存储的,对该数据的操作都是一样的
int a[10] = {……};
int * ph = a;
for (i=0; i<10; ++i)
{
printf("%d\n", *ph);
ph++; //若是使用链表,假设有++函数视为ph = ph -> pNext;
//若上述不成立无法实现++函数,则不能够套用在链表上
}
我们至少可以通过两种结构来存储数据
数组
缺点
1.需要一个连续的很大的内存
2.插入和删除元素的效率很低
优点
存取速度快
链表
优点
1.插入删除元素效率高
2.不需要一个连续的很大的内存
缺点
查找某个位置的元素效率低
专业术语
1.头结点:
头结点的数据类型和首节点的类型是一模一样的;
头结点是首节点前面的那个节点;
头结点并不存放有效数据;
设置头结点的目的是为了方便对链表的操作;
2.头指针:
存放头结点地址的指针变量
3.首节点:
存放第一个有效数据的节点
4.尾节点:
存放最后一个有效数据的节点
确定一个链表需要一个参数:头指针
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdbool.h>
struct Node
{
int data; //数据域
struct Node * pNext; //指针域 递归
};
//函数声明
struct Node * Create_List(void);
void Traverse_List(struct Node * pHead);
bool empty_list(struct Node * pHead);
int main()
{
struct Node * pHead = NULL; //pHead用来存放链表头结点的地址
pHead = Create_List(); //创建一个链表,并将信息传递给主函数
Traverse_List(pHead); //对链表进行输出
return 0;
}
struct Node * Create_List(void)
{
int len; //用来存储有效节点的个数
int i;
int val; //用来临时存储用户输入的结点的值
//分配了一个不存放有效数据的头结点
struct Node * pHead = (struct Node *)malloc(sizeof(struct Node));
if (NULL == pHead)
{
printf("分配失败,程序终止!\n");
exit(-1); //终止程序
}
struct Node * pTail = pHead;
pTail->pNext = NULL;
printf("请您输入您需要生成的链表结点的个数:len = ");
scanf("%d", &len);
for (i=0; i<len; ++i)
{
printf("请输入第%d个节点的值:", i+1);
scanf("%d", &val);
struct Node * pNew = (struct Node *)malloc(sizeof(struct Node));
if (NULL == pNew)
{
printf("分配失败,程序终止!\n");
exit(-1);
}
pNew->data = val;
pTail->pNext = pNew;
pNew->pNext = NULL;
pTail = pNew;
}
return pHead;
};
bool empty_list(struct Node * pHead)
{
if ( pHead->pNext == NULL ) //pHead->pNext == (*pHead).pNext
return true;
else
return false;
}
void Traverse_List(struct Node * pHead)
{
struct Node * p = pHead->pNext;
/*if(empty_list(pHead))
{
printf("链表为空");
}*/
while (NULL != p)
{
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
return;
}