欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C语言基础:006

程序员文章站 2023-08-18 10:11:10
结构体 结构体类型的定义: 结构体是由c语言中的基本数据类型构成的、并用一个标识符来命名的各种变量的组合,其中可以使用不同的数据类型。 结构体类型的定义 struct结构体名 {...

结构体

结构体类型的定义:

 结构体是由c语言中的基本数据类型构成的、并用一个标识符来命名的各种变量的组合,其中可以使用不同的数据类型。

结构体类型的定义
struct结构体名
{   类型标识符1     成员名1;
类型标识符2     成员名2;
……
类型标识符n     成员名n;
};
struct结构体名——结构体类型名
 

关于结构体类型的说明:

  (1)“struct 结构体名”是一个类型名,它和int、float等作用一样可以用来定义变量。
(2)结构体名是结构体的标识符不是变量名,也不是类型名。
(3)构成结构体的每一个类型变量称为结构体成员,它像数组的元素一样,单数组中元素以下标来访问,而结构体是按结构体变量名来访问成员的。
(4)结构体中的各成员既可以属于不同的类型,也可以属于相同的类型。
(5)成员也可以是一个结构体类型,如:
struct date
{
int month;
int day;
int year;
};
struct person
{
float num;
char name[20];
char sex;
int age;
struct date birthday;
char address[10];
};
  

结构体类型变量的定义:

   1.先定义结构体类型,再定义结构体变量
形式:
struct 结构体名
{类型标识符1 成员名1;
 类型标识符2 成员名2;
 ……
 类型标识符n 成员名n;
};
struct 结构体名 变量名表;
例如:
struct student
{char name[20];
 char sex;
 int age;
 float score;
};
struct student stu1,stu2;

2.在定义结构体类型的同时定义变量
形式:
struct 结构体名
{
类型标识符1  成员名1;
类型标识符2  成员名2;
……
类型标识符n  成员名n;
}变量名表;
例如:
struct student
{
char name[20];
char sex;
int age;
float score;
}stu1,stu2;

3.用匿名形式直接定义结构体类型变量
形式:
struct
{
类型标识符1  成员名1;
类型标识符2  成员名2;
……
类型标识符n  成员名n;
}变量名表;
例如:
struct
{
char naem[20];
char sex;
int age;
float score;
}stu1,stu2;
   

结构体变量的使用:

    结构体是一种新的数据类型,因此结构体变量也可以像其它类型的变量一样赋值、运算,不同的是结构体变量以成员作为基本变量。
结构体成员的表示方式为:结构体变量名.成员名
其中的圆点运算符称为成员运算符,它的运算级别最高。
如果将“结构体变量名.成员名”看成一个整体,则这个整体的数据类型与结构体中该成员的数据类型相同,这样就可以像前面所讲的变量那样使用,但应遵循以下规则:
(1)不能将一个结构体变量作为一个整体进行输入和输出,而只能对结构体变量中的各个成员分别进行输入和输出。
struct date
{
int month;
int day;
itn year;
}day;
scanf(“%d%d%d”,day);(错误)
scanf(“%d%d%d”,&day.year,&day.month,&day.day);(正确)
printf(“%d%d%d”,day);(错误)
printf(“%d%d%d”,day.year,day.month,day.day);(正确)
(2)如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级地找到最底的一级的成员,只能对最底级的成员进行赋值或存取运算。
struct date
{
int month;
int day;
int year;
};

struct student
{
long num;
char naem[20];
char sex;
int age;
struct date birthday;
char depart[10];
}stu1;

如:stu1.birthday.year=2004;
stu1.birthday.month=12;

(3)对结构体变量的成员可以像同类型普通变量一样进行各种运算。
    

结构体变量的初始化与存储:

     1.结构体变量的初始化:在定义结构体变量的同时给它赋以初值。
struct student
{
char name[20];
char sex;
int age;
float score;
}stu1,stu2={“wangwu”,’m’,20,88.5};

2.结构体变量所占内存的字节数
·struct 类型用内存字节数=?
·是所有成员变量的内存总和吗?
用运算符sizeof获得结构体大小
sizeof(变量或表达式)
sizeof(类型)

typedef struct sample
{
char m1;
int m2;
char m3;
}sample;
printf(“%d\n”,sizeof(struct sample));
printf(“%d\n”,sizeof(sample));

并非所有成员变量的内存总和

事实上,所有数据类型在内存中都是从偶数地址开始存放的且结构所占的实际空间一般是按照机器字长对齐的不同编译器、平台,对齐方式会有变化
结构体变量的成员存储对齐规则是与机器相关的
具有特定数据类型的数据项大小也是与机器相关的
所以一个结构体在内存中的存储格式也是与机器相关的

3.结构体变量存储分配示意图
     

C语言基础:006


结构体类型数组

      1、结构体数组的定义
结构体数组就是具有相同结构体类型的变量集合。
struct 结构体名
{
类型标识符1  成员名1;
类型标识符2  成员名2;
……
类型标识符n  成员名n;
}数组名[整型常量表达式];

加入要定义一个班级100个同学的学号、姓名、性别、年龄,可以定义成一个结构体数组。如下所示:
struct{
long num;
char name[20];
char sex;
int age;
}stu[100];
      

       2 结构体数组的初始化
一般形式是在定义的数组后面加上={初始值表列};
struct student
{
long num;
char name[20];
char sex;
int age;
float score;
char add[5];
}stu[3]={
{101,”wgj”,’m’,28,88.5,”cs”}
{102,”dyh”,’f’,26,88.0,”cs”}
{103,”dyc”,’m’,24,78.5,”cs”}
};
       

C语言基础:006


        结构体数组的使用:
结构体数组成员的访问十一数组元素为结构体变量的,其形式为:结构体数组元素名.成员名
如:stu[2].age
候选人得票的统计。设有三个候选人,每次输入一个得票的候选人名字,要求最后输出各人得票结果。

struct person
{
char name[20];
int count;
}leader[3]={“zhang”,0,”li”,0,”wang”,0};

main()
{
int i,j;
char leader_name[20];
for(i=1;i<=10;i++){
scanf(“%s”,leader_name);
for(j=0;j<3;j++)
if(strcmp(leader_name,leader[j].name)==0)
leader[j].count++;
}
printf(“\n”);
for(i=0;i<3;i++)
printf(“%5s:%d\n”,leader[i].name,leader[i].count);
}
        

结构体类型指针

         一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以定义一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。

1.指向一个结构体变量的指针定义形式:
struct 结构体名 *指针变量名;
例如:
struct student
{
long int num;
char name[20];
char sex;
float score;
};
struct student *pstu,stu;
pstu=&stu;
         

          2.由指向结构体变量的指针变量引用结构体成员的形式为:
(*指针变量).成员名 或指针变量名->成员名

->为指向运算符
如:上面示例用指针变量引用其成员的方式为:
(*pstu).num , (*pstu).name
(*pstu).sex   ,   (*pstu).score

可以等价表示为:
pstu->num,pstu->name,pstu->sex,pstu->score

比较结构体成员的几种引用方式
#include “stdio.h”
main(){
struct student{
long int num;
char name[20];
char sex;
float score;
};
struct student stu_1;
struct student *p;
p=&stu_1;
stu_1.num=200601;
strcpy(stu_1.name,”liping”);
stu_1.sex=’m’;
stu_1.score=811.5;
printf(“no.:%ld\nname:%ssex:%c\nscore:%f\n”,stu_1.num,sut_1.name,stu_1.score);

printf(“\nno.:%ld\nname:%s\nsex:%cscore:%f\n”,(*p).num,(*p).name,(*p).sex,(*p).score);
printf(“\nno.:%ld\nname:%s\nsex:%c\nscore:%f\n”,p->num,p->name,p->sex,p->score);
}
          

           指向结构体数组元素的指针
一个指针变量可以指向一个结构体数组元素(将该结构体数组的数组元素地址赋给此指针变量)。例如:
struct
{
int a;
float b;
}arr[3],*p;
p=arr;

此时使p指向arr数组的第一个元素,“p=arr;”等价于“p=&arr[0];”。若执行“p++;”则此时指针变量p此时指向arr[1]。

输出数组中各元素中各成员的值。
struct student
{
int num;
char name[20];
char sex;
int age;
};
struct student stu[3]={
{10101,”zhang”,’m’,18},
{10102,”li”,’m’,19},
{10103,”wang”,’f’,20}
};

main(){
struct student *p;
printf(“no.name sex age\n”);
for(p=stu;pnum,p->name,p->sex,p->age);
}
           

C语言基础:006
注意:
(1)如果p的初值为stu,即指向第一个元素,则p+1后指向下一个元素。请区别:(++p)->num和(p++)->num
(2)指针p已定义为指向struct student类型的数据,它只能指向该结构体类型数据,而不能指向一元素的某以成员(即p的地址不能是成员的地址)。如下面的赋值是错误的
:p=&stu.num编译时将给出“警告”信息,表示地址的类型不匹配。不要认为反正p是存放地址的,可以将任何地址赋给它。

结构体与函数

             结构体变量作函数参数

将一个结构体变量的值传递给另一个函数,可以采用以下两种方式:
·用结构体变量的成员作参数。用法和普通变量作实参是一样的,属“值传递”方式。
·形参与实参都用结构体变量
直接将实参结构体变量的各个成员之全部传递给形参的结构体变量。注意:实参和形参类型应当完全一致。

例 有一个结构体变量stu,内含学生学号、姓名和三门课的成绩。要求在main函数中赋值,在另一函数print中将它们打印输出,程序如下。
struct stu
{
int num;
char name[20];
int score[3];
};
main(){
void print(struct stu p);
struct stu stu;int j;
scanf(“%d”,&stu.num);
scanf(“%s”,stu.name);
for(j=0;j<3;j++)
scanf(“%d”,&stu.score[j]);
print(stu);
}
void print(struct stu p){
int j;
printf(“%d\n%s\n”,p.num,p.name);
for(j=0;j<3;j++)
printf(“%d\n”,p.score[j]);
}

注意:ansi c允许用整个结构体作为函数的参数传递,但是必须保证实参与形参的类型相同。

·值得指出的是,把一个完整的结构体变量作为参数传递,虽然合法,但要将全部成员一个一个传递,既费时间又费空间,开销大,因此一般不采用。
            

             用指向结构体的指针作函数参数
用指向结构体变量(或数组)的起始地址传给形参。
形参为指针变量,实参为结构体变量的地址或指向结构体变量的指针。

例的print函数形参改用指向结构体指针后程序如下:
#include “stdio.h”
struct student
{
int num;
char name[20];
int score[3];
};

main()
{void print(struct student *p);
struct student stu;int j;
scanf(“%d”,&stu.num);
strcpy(stu.name,”liping”);
for(j=0;j<3;j++)
scanf(“%d”,&stu.score[j]);
print(&stu);
}
void print(struct student *p)
{
int j;
printf(“%d\n%s\n”,p->num,p->name);
for(j=0;j<3;j++)
printf(“%d\n”,p->score[j]);
}
             

              返回结构体类型值的函数
ansi c还允许函数返回结构体类型的值。设有struct student 类型,结构体变量定义如下:
struct student stud;
若函数input()的功能是输入一个学生结构体数据,并将其返回给学生记录stud,即:
#include “stdio.h”
struct student
{int num;
char name[10];
int score[3];
};

struct student input()
{
int k;
struct student stud;
scanf(“%d”,&stud.num);
getchar();
gets(stud.name);
for(k=0;k<3;k++)
scanf(“%d”,&stud.score[k]);
return stud;
}

main(){
struct student stud;
int k;
stud=input();
pritnf(“%d%s”,stud.num,stud.name);
for(k=0;k<3;k++)
pritnf(“%d”,stud.score[k]);
}
              

链表

               链表概述:
链表是一种常见的重要的数据结构,它是动态地进行存储分配的一种结构,根据需要开辟内存单元,使用链表可以减少内存空间的浪费。
所谓链表是指若干个结构体变量(每个结构体变量称为一个“结点”)按一定的原则连接起来。每个结构体变量都包含有若干个数据和一个指向下一个结构体变量的指针,
依靠这些指针将所有的结构体变量连接成一个链表。

例:建立和输出一个简单的链表

struct student
{
long num;
char name[20];
struct student *next;
};

main(){
struct student a,b,c,*head,*p;
a.num=2002001;b.num=2002002;c.num=2002003;
strcpy(a.name,”zhang”);
strcpy(b.name,”sun”);
strcpy(c.name,”li”);
head=&a;
a.next=&b;
b.next=&c;
c.next=null;
p=head;
do
{
printf(“%ld,%s”,p->num,p->name);
p=p->next;
}while(p!=null);
}
               

链表的基本操作

                链表的基本操作包括建立动态链表、链表的插入、删除、输出和查找等。

1.建立动态链表
所谓建立动态链表是指一个一个地输入各结点数据,并建立起各结点前后相链的关系。建立动态链表有两种方式:一种是在链表尾插入节点,一种是在链表头插入结点。

例:写一个函数建立一个若干学生数据的单向动态链表。(要求按学号从小到大的顺序建立)
#define null 0
struct student
{
long num;
struct student *next;
};

struct student *creat()
{
struct student *head,*p,*q;
long x;
q=head=(struct student *)malloc(sizeof(struct student));
head->next=null;
printf(“\nplease input datas to the list:”);
scanf(“%ld”,&x);
while(x!=0){
p=(struct student *)malloc(sizeof(struct student));
p->num=x;
p->next=null;
q->next=p;
q=p;
scanf(“%ld”,&x);
}
return (head);
}
                

                 2.输出链表
void print(struct student *head)
{
struct student *p;
p=head->next;
printf(“\nthe list is:”);
if(p==null)
printf(“the list is null!\n”);
while(p!=null)
{
printf(“%6ld”,p->num);
p=p->next;
}
printf(“\n”);
}
                 

                  3.删除链表中的一个结点
void del(struct student *head)
{
struct student *p,*q;
long num;
printf(“\nplease input the student’ num you want to delete:”);
scanf(“%ld”,&num);
p=head->next;
while(num!=p->num && p->next!=null){
q=p;
p=p->next;
}
if(num==p->num)
{q->next=p->next;
free(p);}else
printf(“\n%ld is not found!\n”,num);
}
                  

                   4.在链表中插入一个结点
void insert(struct student *head)
{
struct student *p,*q,*t;
p=(struct student *)malloc(sizeof(struct student));
printf(“\nplease input the student you want to insert:”);
scanf(“%ld”,&p->num);
q=head;
while(q-next!=null && q->next-numnum)
q=q->next;
p->next=q->next;
q->next=p;
}
                   

                    5.对链表的综合操作
void main(){
struct student *la;
la=creat();
print(la);
del(la);
print(la);
insert(la);
print(la);
}
                    

链表与结构体数组的主要区别

                     1、数组的元素个数是固定的,而组成链表的结点个数可按需要增减;
2、数组中的元素顺序关系由元素在数组中的位置(即下标)确定,链表中的结点顺序关系由结点所包含的指针来体现。
3、对于不是固定长度的列表,用可能最大长度的数组来描述,会浪费许多内存空间。另外,对于元素的插入、删除操作非常频繁的列表处理场合,用数组表示
列表也是不适宜的。若用链表实现,会使程序结构清晰,处理的方法也较为简单。
                     

枚举类型

                      字符型或其它类型显然是不妥当的。为此,c语言提供了一种称为“枚举”的类型。在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。应该说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。
                      


枚举类型的定义和枚举变量的说明:
1、枚举的定义枚举类型定义的一般形式为:
enum枚举名{枚举值表};
在枚举值表中应罗列出所有可用值。这些值也称为枚举元素。例如:

该枚举名为weekday,枚举值共有7个,即一周中的七天。凡被说明为weekday类型变量的取值只能是七天中的某一天。
2、枚举变量的说明
如同结构和联合一样,枚举变量也可用不同的方式说明,即先定义后说明,同时定义说明或直接说明。设有变量a,b,c被说明为上述的weekday,可采用下述任一种方式:
enumweekday{sun,mou,tue,wed,thu,fri,sat};
enumweekdaya,b,c;
或者为:
enumweekday{sun,mou,tue,wed,thu,fri,sat}a,b,c;
或者为:
enum{sun,mou,tue,wed,thu,fri,sat}a,b,c;

枚举类型变量的赋值和使用

                       枚举类型在使用中有以下规定:
1、枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。例如对枚举weekday的元素再作以下赋值:
sun=5;mon=2;sun=mon;都是错误的。
2、枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0,1,2…。如在weekday中,sun值为0,mon值为1,…,sat值为6。
main(){
enumweekday
{sun,mon,tue,wed,thu,fri,sat}a,b,c;
b=mon;
c=tue;
printf("%d,%d,%d",a,b,c);}
说明:只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如:a=sum;b=mon;是正确的。而:
a=0;b=1;
是错误的。如一定要把数值赋予枚举变量,则必须用强制类型转换。如:a=(enumweekday)2;
其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于:
a=tue;
还应该说明的是枚举元素不是字符常量也不是字符串常量,使用时不要加单、双引号。
                       

文件操作

                        要点:
1.文件操作,就是通过程序,操作文件
2.两个方面:读,写
                        

fopen 打开文件

                         函数原型:file *fopen(char *filename, char *type);int fclose(file *fp);
头文件:#include
是否是标准函数:是
函数功能:函数fopen:打开一个流,即:打开一个文件。该函数有两个参数,filename是需要打开文件的文件名,type是打开文件的方式。函数fclose:关闭一个流,
即:关闭一个文件,并释放文件缓冲区。fclose函数与fopen函数是相对的两个函数。fclose函数的参数是指向文件的指针,应用该函数用以在程序结束之前关闭文件,并释放
文件缓冲区。这样可以保证文件的数据不流失。返回值:fopen:file类型,如果打开的文件存在,返回指向该文件的指针;如果打开的文件不存在,则在指定的目录下建立该文件打开,
并返回指向该文件的指针。fclose:整型,有错返回非0,否则返回0。
例子1:
#include 
int main(void)
{
    file *fp=fopen("文件","打开方式");//返回值是file是一个结构体,是一个结构体的宏。
    fclose(fp);//打开之后,用完了,就要关闭文件,fclose是跟fopen一起使用的一个函数。
    return 0;
}
                         

frend 读取

                          fread:从流中读取字符串函数
函数原型:int fread(void *buf, int size, int count, file *fp);
头文件:#include
是否是标准函数:是
函数功能:从fp指向的文件中读取长度为size 的count个数据项,并将它输入到以buf为首地址的缓冲区中。此时,文件指针fp会自动增加实际读入数据的字节数,
即fp指向最后读入字符的下一个字符位置。
返回值:返回实际读入数据项的个数,即count值。若遇到错误或文件结束返回0。
例子1:
#include 
int main(void)
{
    file *fp=fopen("文件","方式”);
                  if(!fp){
                       perror("fopen");
                       exit(-1);//当没打开的时候就终止
                   }
                   char buf[64]={};//首先需要一个空间
                   fread(buf,)//fread就是把文件中的内容读到一个空间,第一个参数是要存放的一个任意类型的空间,第二个参数是一次读取的单位信息长度是多少,
                    第三个参数是要读取这种长度的信息一共有多少个,第四个参数是读取那个文件。
                   fclose(fp);
                   return 0;
}
                          

fwrite 写

                           fwrite:向文件写入数据函数
函数原型:int fwrite(void *buf, int size, int count, file *fp);
头文件:#include
是否是标准函数:是
函数功能:将buf所指向的count*size个字节的数据输出到文件指针fp所指向的文件中去。该函数与fread相对,输出数据后,文件指针fp自动指向输出的最后一个字符的下一个位置。
该函数常用于分批将数据块输出到文件中。
返回值:返回实际写入文件数据项个数。
#include 
#include 
#include 
int main(void)
{
    file *fp=fopen("./file","w");//w意思是打开时如果没有这个文件,就会创建这个文件。如果已经存在会将它清空,
   if (!fp) {
        perror("fopen");
        exit(-1);
    }//打不开时,报错,终止程序。

    fwrite("hello\n",1,strlen("hello\n"),fp);//第一个参数写之前,必须有一个内存空间的地址,void*表示人一个空间的首字节的地址,第二个参数是,
    写入的单位信息占多少个字节(长度),第三个参数,一共要写多少个这样的单位信息。第四个参数是,写到哪个文件中去。
    //strlen:计算字符串长度函数函数原型: int strlrn (char *str);头文件:#include是否是标准函数:是函数功能:求字符串的长度,
    也就是求字符串str中有多少个字符返回值:字符串str字符的个数
   fclose(fp);
    return 0;
}
                           

文件拷贝

                            #include 
#include 
int main(void)
//{
  char buf[20];
  file *fp=fopen("./file","r");
  if (!fp) {
       perror("fopen");
      exit(-1);
  }
   file *fp2=fopen("./file2","w'");
   if (!fp2) {
      perror("fopen_2");
   }
  while (1) {
      size_t ret=fread(buf,1,20,fp);
       if (ret<=0) {
           break;
       }
       fwrite(buf,1,ret,fp2);
   }
    fclose(fp);
    fclose(fp2);
    return 0;
}