第十一周:结构类型

简介: 你会坚持下来的对吗?希望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

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

目录
相关文章
|
3月前
|
Java
上周考试错题类型总结(接口型)
上周考试错题类型总结(接口型)
|
4月前
|
存储 安全 Unix
C#.Net筑基-类型系统②常见类型--日期和时间的故事
在System命名空间中,有几种表示日期时间的不可变结构体(Struct):DateTime、DateTimeOffset、TimeSpan、DateOnly和TimeOnly。DateTime包含当前本地或UTC时间,以及最小和最大值;DateTimeOffset增加了时区偏移信息,适合跨时区操作。UTC是世界标准时间,而格林尼治标准时间(GMT)不稳定,已被更精确的UTC取代。DateTimeOffset和DateTime提供了转换为UTC和本地时间的方法,以及各种解析和格式化函数。
|
4月前
|
Java
十二时辰与现代时间的互转(精确版)
十二时辰与现代时间的互转(精确版)
61 0
|
存储 编译器 程序员
爱上c++的第一天:内存存储模型,引用和函数方面的知识
在运行的时候调用程序分配内存可以在任何时候决定分配内存即分配的大小,用户自行决定在何时释放。堆中的所有东西都是匿名的,不能用名字访问,只能通过指针来访问。
161 0
爱上c++的第一天:内存存储模型,引用和函数方面的知识
|
算法 数据安全/隐私保护
软考加密类型题(7个历年类型题【答案在底部】选择题1分必得)
对称秘钥算法也叫:私钥、私有秘钥、共享秘钥。 非对称秘钥算法也叫:公钥、公开秘钥、数字签名。 还有消息摘要加密:MD5
244 0
软考加密类型题(7个历年类型题【答案在底部】选择题1分必得)
|
C++ 人工智能
2015级C++第9周程序阅读 类和指针
阅读程序,写出程序的运行结果并理解其运行机制。 (1) #include &lt;iostream&gt; using namespace std; class A { public: A(){cout&lt;&lt;"A";} ~A(){cout&lt;&lt;"~A";} }; class B { A *p; public: B()
900 0
|
C++
2014秋C++第19周 项目3参考 应用枚举
课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,课程资源在云学堂“贺老师课堂”同步展示,使用的帐号请到课程主页中查看。  【项目3-应用枚举】(1)阅读教材7.3节,了解枚举类型的一般用法。阅读下面输出He先生买车方案的程序,理解使用枚举类型的意义。 #include &lt;iostream&gt; using na
971 0
|
自然语言处理 C++ iOS开发
2014秋C++第17周 项目7参考 电子词典结构体版
课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,课程资源在云学堂“贺老师课堂”同步展示,使用的帐号请到课程主页中查看。  【项目7-电子词典结构体版】做一个简单的电子词典。在文件dictionary.txt中,保存的是英汉对照的一个词典,词汇量近8000个,英文、中文释义与词性间用’\t’隔开。编程序,由用户输入英文词
1127 0
|
C++ C语言
2014秋C++第16周 项目2参考 用指针玩字符串
课程主页在http://blog.csdn.net/sxhelijian/article/details/39152703,课程资源在云学堂“贺老师课堂”同步展示,使用的帐号请到课程主页中查看。  【项目2-用指针玩字符串】  指针是神奇的,指向整型的指针int *p1,可以操作整型数组int a[];指向字符型的指针char *p2,可以操作字符数组(字符串)char str[];更灵活的是
1140 0