第十一周:结构类型

简介: 你会坚持下来的对吗?希望C语言不会成为你跨进编程世界的拦路虎,而是你的启蒙语言,梦的开始

11.1-1枚举

常量符号化

  1. 用符号而不是具体的数字来表示程序中的数字

#include

constintred=0;

constintyellow=1;

constintgreen=2;

intmain(intargc, charconst*argv[])

{

   intcolor=-1;

   char*colorName=NULL;

   

   printf("请输入你喜欢的颜色的代码:");

   scanf("%d",&color);

   switch( color ){

           casered:colorName="red";break;

           caseyellow:colorName="yellow";break;

           casegreen:colorName="green";break;

           default:colorName="default";break;

   }

   printf("你喜欢的颜色是%d",colorName);

   

   return0;

}

枚举

  1. 用枚举而不是定义独立的const int变量

#include

enumCOLOR{RED,YELLOW,GREEN}; //枚举

intmain(intargc, charconst*argv[])

{

   intcolor=-1;

   char*colorName=NULL;

   

   printf("请输入你喜欢的颜色的代码:");

   scanf("%d",&color);

   switch( color ){

           caseRED:colorName="red";break;

           caseYELLOW:colorName="yellow";break;

           caseGREEN:colorName="green";break;

           default:colorName="default";break;

   }

   printf("你喜欢的颜色是%d",colorName);

   

   return0;

}

  1. 枚举是一种用户定义的数据类型,它用关键字enum 以如下语法来声明:
  1. enum 枚举类型名字{名字0,.....,名字n};
  2. 枚举类型名字可以省略
  1. 枚举类型名字通常并不真的使用,要用的是在大括号里的名字,因为它们就是常量符号,它们的类型是int,值则依次从0到n。如:
  1. enum colors{red,yellow,green};
  2. 这样就创建了3个常量,red的值是0,yellow的值是1,而green的值是2
  3. 当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量名字

#include

enumcolor{ red,yellow,green};

voidf(enumcolorc);

intmain(void)

{

   enumcolort=red;

   

   scanf("%d",&t);

   f(t);

   

   return0;

}

voidf(enumcolorc)

{

   printf("%d\n",c);

}

  1. 枚举量可以作为值
  2. 枚举类型可以跟上enum作为类型
  3. 但是实际上是以整数来做内部计算和外部输入输出的

#include

enumCOLOR{RED,YELLOW,GREEN,NumCOLOR};

intmain(intargc, charconst*argv[])

{

   intcolor=-1;

   char*ColorNames[NumCOLOR] = {"red","yellow","green",};

   char*colorName=NULL;

   

   printf("请输入你喜欢的颜色的代码:");

   scanf("%d",&color);

   if( color>=0&&color<NumCOLORS ){

       colorName=ColorNames[color];

   }else{

       colorName="unknown";

   }

   printf("你喜欢的颜色是%s",colorName);

   

   return0;

}

  1. 这样需要遍历所有的枚举量或者需要建立一个用枚举量做下标的数组的时候就很方便
  2. 上面的套路:在进行枚举的时候最后面在放上一个数(NumCOLORS),这样就能够表示NumCOLORS前面有几个数了(例如里面有3个数,索引值到0-2,在后面加上一个数,索引值刚好等于实际我们想要表达的数量)

枚举量

  1. 声明变量可以指定值
  1. enum COLOR{RED = 1,YELLOW,GREEN = 5};

#include

enumCOLOR {RED=1; YELLOW,GREEN=5,NumberCOLORS};

intmain(intargc,charconst*argv[])

{

   printf("code for GREEN is %d\n",GREEN);

   

   return0;

}

//这样输出的话,会从1开始计数,YELLOW则变成1+1,然后到GREEN的5之前都会跳过了

枚举只是int

  1. 即使给枚举类型的变量赋不存在的整数值也没有任何warning或error

枚举

  1. 虽然枚举类型可以当作类型使用,但是实际上很(bu)少(hao)用
  2. 如果有意义上排比的名字,用枚举比const int方便
  3. 枚举比宏(macro)好,因为枚举有int类型,宏没有类型(宏我在后面会解释是啥)

11.2-1结构类型

声明结构类型

#include

intmain(intargc,charconst*argv[])

{

   structdate{

       intmonth;

       intday;

       intyear;

   };//声明在这里,最后要加上分号哦,这是在函数内部声明的,通常放在函数外面

   

   structdatetoday;//在这里我们定义了一个变量是today,类型是struct date的

   

   today.month=07;

   today.day=31;

   today.year=2014;

   

   printf("Today's date is %i-%i-%i.\n",today.year,today.month,today.day);

   

   return0;

}

//声明结构类型跟定义结构变量是两件事情哦

  1. 声明在函数内还是函数外?
  1. 和本地变量一样(就是局部变量),在函数内部声明的结构类型只能在函数内部使用
  2. 所以通常在函数外部声明结构类型,这样就可以被多个函数所使用了

structpoint{

   intx;

   inty;

};

structpointp1,p2;

p1和p2都是point

里面有x和y值//这是第一种声明方式

----------------------------------------------

struct{

   intx;

   inty;

}p1,p2;

p1和p2都是一种无名结构,里面有x和y  //这是第二种形式,没有名字(没有声明point)

   //只是定义了两个变量,因为作者并不打算接下来继续在其他地方去调用

   

--------------------------------------------------------

structpoint{

   intx;

   inty;

}p1,p2;

p1和p2都是point,里面有x和y的值t  //这是第三种声明方式

结构的初始化

#include

structdate{

   intmonth;

   intday;

   intyear;

};

intmain(intargc,charconst*argv[])

{

   structdatetoday= {07,31,2014};

   structdatethismonth= {.month=7,.year=2014};

   

   printf("Today's date is %i-%i-%i.\n",today.year,today.month,today.day);

   printf("This month is %i-%i-%i.\n",thismonth.year,thismonth.month,thismonth.day);

   //给的值会被填进去,没给的值跟数组一样默认为0

   return0;

}

结构成员

  1. 结构和数组有点像
  2. 数组用[]运算符和下标访问其成员
  1. a[0] = 10;
  1. 结构用.运算符和其名字访问其成员
  1. today.day
  2. student.firstName
  3. p1.x
  4. p2.y

结构运算

  1. 要访问整个结构,直接用结构变量的名字
  2. 对于整个结构,可以做赋值、取地址,也可以传递给函数参数
  3. p1 = (struct point){5,10}; //相当于p1.x = 5;p1.y = 10;
  4. p1 = p2;  //相当于p1.x = p2.x;p1.y = p2.y;
  5. 数组无法做这两种运算!但结构可以

结构指针

  1. 和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
  2. struct date *pDate = &today;

#include

structdate{

   intmonth;

   intday;

   intyear;

};

intmain(intargc,charconst*argv[])

{

   structdatetoday;

   

   today= (structdate){07,31,2014};

   

   structdateday;

   

   structdate*pDate=&today;  //指向地址

   

   printf("Today's date is %i-%i-%i.\n",today.year,today.month,today.day);

   printf("The day's date is %i-%i-%i.\n",day.year,day.month,day.day);

   

   printf("address of today is %p\n",pDate);

   

   return0;

}

11.2-2结构与函数

结构作为函数参数

int numberOfDays(struct date d)

  1. 整个结构可以作为参数的值传入函数
  2. 这时候是在函数内新建一个结构变量,并复制调用者的结构的值
  3. 也可以返回一个结构
  4. 跟数组完全不一样

#include

#include

structdate{

   intmonth;

   intday;

   intyear;

};

boolisLeap(structdated);

intnumberOfDays(structdated);

intmain(intargc,charconst*argv[]){

   structdatetoday,tomorrow;

   //输入今天的日期,月 日 年

   printf("Enter today's date (mm dd yyyy):");

   scanf("%i %i %i",&today.month,&today.day,&today.year);

   

   if( today.day!=numberOfDays(today)){

       tomorrow.day=today.day+1;

       tomorrow.month=today.month;

       tomorrow.year=today.year;

   }elseif( today.month==12 ){

       tomorrow.day=1;

       tomorrow.month=1;

       tomorrow.year=today.year+1;

   }else{

       tomorrow.day=1;

       tomorrow.month=today.month+1;

       tomorrow.year=today.year;

   }

   

   printf("Tomorrow's date is %i-%i-%i.\n",

   tomorrow.year,tomorrow.month,tomorrow.day);

   

   return0;

}

intnumberOfDays(structdated){

   intdays;

   

   constintdaysPerMonth[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

       

       if(d.month==2&&isLeap(d))

           days=29;

       else

           days=daysPerMonth[d.month-1];

   

   returndays;

}

boolisLeap(structdated){

   boolleap=false;

   

   if((d.year%4==0&&d.year%100!=0) ||d.year%400==0 )

       leap=true;

   

   returnleap;

}

输入结构

  1. 没有直接的方式可以一次scanf——一个结构
  2. 如果我们打算写一个函数来读入结构
  3. 但是读入的结构如何送回来呢?
  4. C语言在函数调用时是传值的
  5. 在函数读入了p的数值之后,没有任何东西回到main,所以y还是{0,0}
  1. 解决方案
  2. 之前的方案,把一个结构传入了函数,然后在函数中操作,但是没有返回回去
  1. 问题在于传入函数的是外面那个结构的克隆体,而不是指针
  2. 传入结构和传入数组是不同的
  1. 在这个输入函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者

#include

structpoint {

   intx;

   inty;

};

voidgetStruct(structpoint);

voidoutput(structpoint);

intmain(intargc,charconst*argv[])

{

   structpointy= {0,0};

   getStruct(y);

   output(y);

}

voidgetStruct(structpointp)

{

   scanf("%d",&p.x);

   scanf("%d",&p.y);

   printf("%d,%d",p.x,p.y);

}

voidoutput(structpointp)

{

   printf("%d,%d",p.x,p.y);

}

结构指针作为参数

  1. K&R说过(p.131)

指向结构的指针

用->表示指针所指的结构变量中的成员

structdate{

   intmonth;

   intday;

   intyear;

}myday;

structdate*p=&myday;

(*p).month=12;

p->month=12;

//第九行跟第十行是一样的意思,第十行会更便捷

#include

structpoint {

   intx;

   inty;

};

structpoint*getStruct(structpoint*);

voidoutput(structpoint);

voidprint(conststructpoint*p);

intmain(intargc,charconst*argv[])

{

   structpointy= {0,0};

   getStruct(&y);

   output(y);

   output(*getStruct(&y));

   print(getStruct(&y));

   *getStruct(&y) = (structpoint){1,2};

}

structpoint*getStruct(structpoint*p)

{

   scanf("%d",&p->x);

   scanf("%d",&p->y);

   printf("%d,%d",p->x,p->y);

   returnp;

}

voidoutput(structpointp)

{

   printf("%d,%d",p.x,p.y);

}

voidprint(conststructpoint*p);

{

   printf("%d,%d",p->x,p->y);

}

11.2-3结构中的结构

结构数组

  1. struct date dates[100]; //这是在初始化数组
  2. struct date dates[] = {{4,5,2005},{2,4,2005}};

#include

structtime{

   inthour;

   intminutes;

   intseconds;

};

structtimetimeUpdate(structtimenow);

intmain(void){

   structtimetestTimes[5] = {

       {11,59,59},{12,0,0},{1,29,59},{23,59,59},{19,12,27}

   };

   inti;

   

   for(i=0; i<5; ++i){

       printf("Time is %.2i:%.2i:%.2i",

              testTimes[i].hour,testTimes[i].minutes,testTimes[i].seconds);

       

       testTimes[i] =timeUpdate(testTimes[i]);

       

       printf("...one second later it's %.2i: %.2i: %.2i\n",

              testTimes[i].hour,testTimes[i].minutes,testTimes[i].seconds);

   }

   

   return0;

}

structtimetimeUpdate(structtimenow){

   ++now.seconds;

   if(now.seconds==60 ){

       now.seconds=0;

       ++now.minutes;

       

       if(now.minutes==60 ){

           now.minutes=0;

           ++now.hour;

           

           if(now.hour==24 ){

               now.hour=0;

           }

       }

   }

}

structdateAndTime{

   structdatesdate;

   structtimestime;

};

嵌套的结构

structpoint{

   intx;

   inty;

};

structrectangle{

   structpointpt1;

   structpointpt2;

};

如果有变量structrectangler;

就可以有:

   r.pt1.x、r.ptl.y

   r.pt2.x、r.pt2.y

   

如果有变量定义:

   structrectangler,*rp;

   rp=&r;

那么下面的四种形式是等价的:(结构中的结构)

   r.pt1.x

   rp->pt1.x

   (r.pt1).x

   (rp->pt1).x

但是没有rp->pt1->x(因为pt1不是指针)

结构中的结构的数组

#include

structpoint{

   intx;

   inty;

};

structrectangle{

   structpointp1;

   structpointp2;

};

voidprintRect(structrectangler)

{

printf("<%d,%d> to <%d,%d>\n",r.p1.x,r.p1.y,r.p2.x,r.p2.y);

}

intmain(intargc,charconst*argv[])

{

   inti;

   structrectanglerects[] = {{{1,2},{3.4}},{{5,6},{7,8}}};//2 rectangles

   for(i=0;i<2;i++)printRect(rects[i]);

}

11.3-1类型定义

自定义数据类型(typedef)

  1. C语言提供了一个叫做typedef的功能来声明一个已有的数据类型的新名字
  2. 比如:typedef int Length;
  3. 使得Length成为int类型的别名
  4. 这样,Length这个名字就可以替代int出现在变量定义和参数声明的地方了:
  1. Length a,b,len;
  2. Length numbers[10];

Typedef

  1. 声明新的类型的名字
  1. 新的名字是某种类型的别名
  2. 改善了程序的可读性

typedeflongint64_t;   //重载已有的类型名字  新名字的含义更清晰  具有移植性

typedefstructADate{

   intmonth;

   intday;

   intyear;

}Date; //简化了复杂的名字

//在这里Date等价于struct ADate,Date代表了到达struct ADate之前的所有

int64_ti=10000000000;

Dated= {9,1,2005};

typedefintLength;//Length就等价于int类型

typedef*char[10]Strings;  //String是10个字符串的数组的类型

typedefstructnode{

   intdata;

   structnode*next;

}aNode;

typedefstructnodeaNode;//这样用aNode就可以替代struct node

11.3-2联合

联合

  1. 存储
  1. 所有的成员共享一个空间
  2. 同一时间只有一个成员是有效的
  3. union的大小是其最大的成员
  1. 初始化
  1. 对第一个成员做初始化

#include

typedefunion{

   inti;

   charch[sizeof(int)];

}CHI;

intmain(intargc,charconstargv[])

{

   CHIchi;

   inti;

   chi.i=1234;

   for( i=0;i<sizeof(int);i++){

       printf("%02hhX",chi.ch[i]);

   }

   printf("\n");

   

   return0;

}

//输出D20400,          1234的十六进制是0x04D2

//低位在前(相当于倒置),小端的方式

目录
相关文章
|
消息中间件 监控 负载均衡
中间件故障转移与容错
【7月更文挑战第23天】
318 1
|
SQL 测试技术 Python
SqlAlchemy 2.0 中文文档(四十三)(6)
SqlAlchemy 2.0 中文文档(四十三)
263 0
|
机器学习/深度学习 传感器 算法
【雷达通信】基于均匀圆阵下CA-MUSIC的二维DOA估计算法附matlab代码
【雷达通信】基于均匀圆阵下CA-MUSIC的二维DOA估计算法附matlab代码
【程序设计】6大设计原则之单一职责
【程序设计】6大设计原则之单一职责
171 0
【程序设计】6大设计原则之单一职责
|
2天前
|
云安全 人工智能 算法
以“AI对抗AI”,阿里云验证码进入2.0时代
三层立体防护,用大模型打赢人机攻防战
1294 3
|
3天前
|
机器学习/深度学习 安全 API
MAI-UI 开源:通用 GUI 智能体基座登顶 SOTA!
MAI-UI是通义实验室推出的全尺寸GUI智能体基座模型,原生集成用户交互、MCP工具调用与端云协同能力。支持跨App操作、模糊语义理解与主动提问澄清,通过大规模在线强化学习实现复杂任务自动化,在出行、办公等高频场景中表现卓越,已登顶ScreenSpot-Pro、MobileWorld等多项SOTA评测。
588 3
|
3天前
|
人工智能 Rust 运维
这个神器让你白嫖ClaudeOpus 4.5,Gemini 3!还能接Claude Code等任意平台
加我进AI讨论学习群,公众号右下角“联系方式”文末有老金的 开源知识库地址·全免费
|
10天前
|
编解码 人工智能 自然语言处理
⚽阿里云百炼通义万相 2.6 视频生成玩法手册
通义万相Wan 2.6是全球首个支持角色扮演的AI视频生成模型,可基于参考视频形象与音色生成多角色合拍、多镜头叙事的15秒长视频,实现声画同步、智能分镜,适用于影视创作、营销展示等场景。
718 4