第六章 其他编程知识
结构体
定义、赋值与输出
结构体的实质是根据需要自己定义一种复合的数据类型。
structStudent //定义结构体,Student为结构体名,即为新数据类型名。
{
intage; //定义结构体中的每一个成员。
charsex;
floatscore;
};
intmain(void)
{
structStudent st1 = {19, 'F', 66.6}; //初始化,定义的同时赋初始值,且只能在定义的同时整体赋值。
structStudent st2; //若在定义时没有赋值,则只能单项赋值
st2.age= 20; //赋值格式为:结构体名.成员名 = X值
st2.sex= 'm';
st2.score= 99;
Studentst3;//定义时结构体名即为变量类型,此语句类似于int i;
//但为表明定义的变量为结构体,前最好写上struct,即struct + 变量类型 + 变量名。
printf("%d,%c, %f\n", st1.age, st1.sex, st1.score); //结构体的输出,每一项按其基本格式输
printf("%d,%c, %f\n", st2.age, st2.sex, st2.score); 出。
结构体中每一个成员的表示
第一种方式:结构体变量名.成员名
第二种方式:指针变量名->成员名
指针变量名->成员名 等价于 (*指针变量名).成员名,在计算机内部会被转化成 (*指针变量名).成员 名的方式执行,也即两种方式是等价的。
structStudent st = {30, 78, 'F'};
structStudent * pst = &st; //定义pst为结构体类型指针变量并赋值结构体变量st的地址。
st.age= 20; //第一种方式:结构体变量名.成员名
pst->age= 25; //第二种方式:指针变量名->成员名
printf("%d\n",pst->age);
pst->age在计算机内部会被转化成(*pst).age,这就是->的含义。
所以,pst->age 等价于(*pst).age,即st.age。
pst->age的含义:pst所指向的那个结构体变量中的age这个成员。
结构体与函数
指针的优点:快速传递数据。
InputStudent(&st);//对结构体变量的输入,要修改st的值,必须发送地址。
OutputStudent(&st);//对结构体变量的输出,既可以发送地址,也可以发送变量的值,但发送地址能够减少内 //存耗费,也能够提高执行速度,提高效率。一般选择发送地址
voidOutputStudent_1(struct Student stu) //定义结构体变量stu要占108个字节,且需要复制108个字节内容 //耗费内存,执行速度慢。
voidOutputStudent_2(struct Student * pst) //定义指针变量pst只占4个字节,且只需要复制4个字节内容, //耗用内存少,执行速度快。
推荐使用结构体指针变量作为函数参数来传递,提高效率,节省内存。
字符与字符串
#include <stdio.h>
#include <string.h> //<string.h>,字符串处理头文件。
intmain(void)
{
chars;
chara[100]; //字符串的定义即为字符数组,100表示此字符串占100字节。数字与字母无论大小写,都只占 一个字节,汉字占两个字节。同样,数组名a为第一个字节的地址。
s= 'A'; //字符的赋值要在字符左右加单引号'',以便区分这是字符而不是变量名。
//a[100]= "大家好!呵呵!" //错误,字符串的赋值不能用等号。
strcpy(a,"大家好!呵呵!"); //字符串的赋值要用strcpy()函数,字符串拷贝函数,格式:strcpy(字符串变
量名,"字符串")。或用scanf()函数赋值。
printf("%c,%C\n", s, s); //%c用于输出字符,字符的输出用%c和%C与输出大小写没关系。
printf("%s\n",a); //字符串的输出用%s。
//printf("%c\n",a[0]); //错误,字符串名是a,而不是a[100],后不能加下标。
return0;
}
枚举
枚举:把一个事物所有可能的取值一一列举出来。
//定义枚举常量类型,把一个事物所有可能的取值一一列举出来,使此类型变量只能取列举出的值。
enumWeekDay //WeekDay为新类型名
{
MonDay,TuesDay = -1, WednesDay, ThursDay, FriDay, SaturDay, SunDay
};//列举的枚举常量,其实际值默认按顺序依次为0, 1, 2, 3...,若在某常量上赋值,则其后值依次加1。
枚举的优缺点
优点:代码更安全,不会出现越界。
缺点:书写麻烦。
链表
存储数据的结构
数组
优点:存取速度快。
缺点:插入和删除元素的效率很低;
需要一块连续的内存空间。
链表
专业术语
首节点
存放第一个有效数据的节点
尾节点
存放最后一个有效数据的节点
头结点
头结点的数据类型和首节点的类型是一样的(结构体变量类型)
头结点是首节点前面的那个节点,头结点并不存放有效数据,
设置头结点的目的是为了方便对链表的操作。
头指针
存放头结点地址的指针变量
优点:插入删除元素效率高;
不需要一块连续的很大的内存空间。
缺点:查找某个位置的元素效率低。
定义
链表的基本单元是节点,节点分为两部分:数据域和指针域,数据域用来存放有效数据,指针域存放 下一个节点的
地址。所以链表的节点都是结构体变类型。
用创建链表时,链表必须为动态创建,以便其它函数对其进行操作。
要确定一个链表,只需链表的头指针即可,即函数参数只需一个。
简单链表程序
# include <stdio.h>
# include <malloc.h>
# include <stdlib.h>
struct Node //通过结构体定义节点
{
intdata; //创建数据域
structNode * pNext; //创建指针域
};
//函数声明
struct Node * CreateList(void);
void TraverseList(struct Node *);
bool isEmpty(struct Node *);
int main(void)
{
structNode * pHead; //创建头指针,用来存放头结点的地址。
pHead= CreateList(); //CreateList()函数动态创建链表并返回头结点的地址。
printf("\n");
TraverseList(pHead);//函数参数只需头指针即可确定一个链表。
return0;
}
struct Node * CreateList(void) //函数返回值为struct Node * 类型。
{
intlen;
inti;
intval;
structNode * pHead = (struct Node *)malloc(sizeof(struct Node));
if(NULL == pHead)
{
printf("分配头结点空间失败,程序终止!\n");
exit(-1);//exit函数,退出程序。
}
structNode * pTail = pHead;
pTail->pNext= NULL;
printf("请输入链表的节点个数:len = ");
scanf("%d",&len);
for(i=0; i<len; ++i)
{
printf("请输入第%d个节点的值:", i+1);
scanf("%d",&val);
structNode * 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; //递归
}
returnpHead;
}
void TraverseList(struct Node *pHead) //遍历输出
{
structNode * p;
if(isEmpty(pHead))
{
printf("链表为空!\n");
}
else
{
p = pHead->pNext; //使指针指向下一个节点
printf("链表中的数据为:\n");
while(NULL != p)
{
printf("%d ", p->data);
p= p->pNext;
}
printf("\n");
}
return;
}
bool isEmpty(struct Node * pHead)
{
if(NULL == pHead)
returntrue;
else
returnfalse;
}
exit()函数
exit(-1);//exit函数,用于终止当前程序,防止程序出错崩溃。参数为int类型,一般写-1,exit函数位于<stdlib.h>头 文件中。
算法
通俗定义:解题的方法和步骤
狭义定义:对存储数据的操作
对不同的存储结构,要完成某一个功能所执行的操作是不一样的
如:要输出数组中所有元素的操作和要输出链表中所有元素的操作肯定是不一样的。
广义定义:广义的算法也叫【泛型】
无论数据是如何存储的,对该数据的操作都是一样的。
狭义算法与广义算法的区别在于看待问题的角度不同,狭义算法站在底层看待实现细节,广义算法站在高层 看待问题的总体
如:要输出数组中所有元素和输出链表中所有元素都是将元素一个一个地输出。
文件
C语言中对文件的操作是用函数实现的,而不是用流实现的。
需定义文件指针fp,fp = 函数
打开文件函数fopen,关闭文件函数fclose。
FILE* fp;
fp= fopen(E:\file.txt, r);
宏
又称宏定义,用来定义替换字符串,使书写简便。
无参数定义:
#define新字符串名 原字符串名
#defineP printf //宏定义,在以下书写中可用P代替printf。
#defineDD "%d" //宏定义,在以下书写中可用DD代替"%d"。
宏定义不是语句,后不需要加分号。
有参数定义:
#defineswap(a, b) {int c; c=a; a=b; b=c;} //用swap(a, b)代替后面的语句,其中包括参数的替换。
#defineMIN(a, b) (a<b)?a:b //取a,b最小值的的宏。
含参数的宏定义类似于函数,根本上不同于函数,不存在函数的调用,运行速度快。
C语言中有四种存储类型声明符auto,register,extern,static
自动变量auto,平时定义的int、char类型变量都是自动变量,int i;其实就是auto int i;由系统自动分配,自动释放。
寄存器变量register,若某个变量经常使用,可将其声明为寄存器变量类型,将其存储在寄存器中,提高效率。
局外变量extern,若某个变量或函数不在本函数中,可将其声明为局外变量,表明此变量或函数在其他模块寻找。
静态变量static,此类型变量,即使定义它的函数终止内存空间也不会释放,但不能被其他函数使用,只能在定义 它的函数运行期间被其它函数使用。
存储类型声明不仅用于声明变量,也可用于声明函数等。