22-结构类型
枚举
枚举
- 枚举是一种用户自定义的数据类型,它用关键字enum,以如下语法来声明:
enum枚举类型的名字{名字0,名字1,…,名字n}; - 枚举类型名字通常并不真的被使用,要用的是在大括号里的名字,因为它们就是常量符号,它们的类型是int,值则依次从0到n。
如:enum colors{red,yellow,green}; - 创建了三个常量,red的值是0,yellow是1,green是2
- 当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量名字
- 枚举量可以作为值
- 枚举类型可以跟上enum作为类型
- 但实际上,是以整数来做内部计算和外部输入输出的
eg:
#include <stdio.h>
enum color { red, yellow,green};
void f(enum color c);
int main(void)
{
enum color t = red;
scanf ( "%d" , &t);
f(t);
return 0;
}
void f(enum color c)
{
printf("%d \n", c);
}
自动计数的枚举
#include <stdio.h>
enum COLOR {RED,YELLOw,GREEN,NumCOLORS}; //NumCOLORS自动计数
int main(int argc, char const *argv [])
{
int color = -1;
char *ColorNames [NumCOLORS] = {"red" , "yellow" , "green",};
char *colorName = NULL;
printf("输入你喜欢的颜色的代码:");
scanf( "%d", &color) ;
if ( color >=0 &&color < NumCOLORS ) {
colorName = ColorNames [color] ;
}else {
colorName= "unknown";
}
printf("你喜欢的颜色是%s\n", colorName) ;
return 0;
}
这样需要遍历所有的枚举量或者需要建立一个用枚举量做下标的数组的时候就很方便了。
枚举量
- 声明枚举量的时候可以指定值
- enum COLOR{RED = 1,YELLOW , GREEN = 5};
#include <stdio.h>
enum COLOR {RED=1,YELLOw,GREEN=5,NumCOLORS};
int main(int argc, char const *argv [])
{
printf("code for GREEN is %d\n",GREEN); //5
return 0;
}
枚举只是int
- 即使给枚举类型的变量赋的不存在的整数值,也没有任何waring或error
#include <stdio.h>
enum COLOR {RED=1,YELLOw,GREEN=5,NumCOLORS};
int main(int argc, char const *argv[])
{
enum COLOR color = 0;
printf( "code for GREEN is %d\n",GREEN);
printf("and color is %d\n", color) ;
return 0;
}
- 虽然枚举类型可以当作类型使用,但实际上很少用
- 如果有意义上排列的名字,用枚举比const int方便
- 枚举比宏(macro)好,因为枚举有int类型
结构
结构
- 声明结构类型
#include <stdio.h>
struct date {
int month;
int day;
int year;
};
int main(int argc, char const *argv[ ])
{
struct date today;
today . month = 07;
today. day = 31;
today . year = 2014;
printf( "Today's date is %i-%i-%i. \n" ,today.year,today.month,today.day ) ;
return 0;
}
- 和本地变量一样,在函数内部声明的结构类型只能在结构内部使用
- 所以通常在函数外部声明结构类型,这样就可以被多个函数使用。
声明结构的形式
-
struct point {
int x;
int y;
}
struct point p1,p2;
p1,p2都是point里面x和y的值 -
struct {
int x;
int y;
}p1,p2;
p1,p2都是一种无名结构,里面有x和y -
struct point {
int x;
int y;
}p1,p2;
p1和p2都是point里面有x和y的值
结构变量
#include <stdio.h>
struct date { //结构类型
int month;
int day;
int year;
};
int main(int argc, char const *argv[ ])
{
struct date today; 结构变量
today . month = 07;
today. day = 31;
today . year = 2014;
printf( "Today's date is %i-%i-%i. \n" ,today.year,today.month,today.day ) ;
return 0;
}
结构的初始化
#include <stdio.h>
struct date {
int month;
int day;
int year;
};
int main(int argc, char const *argv [ ])
{
struct date today = {07,31,2014}; //第一种
struct date thismonth = {.month=7, .year=2014}; //第二种,day没有赋值,自动补零
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 );
return 0;
}
结构成员
- 结构和数组有点像
- 数组用[]运算符和下标访问其他成员,成员必须是相同类型
- 结构用.运算符和名字访问其成员,成员可以是不同类型
today.day
student.firstName
p1.x
p1.y
结构运算
- 要访问整个结构,直接用结构变量的名字
- 对于整个结构,可以做赋值、取地址,也可以传递给函数
eg: p1 = (struct point){5,10} //相当于p1.x = 5; p1.y = 10;
p1 = p2; //相当于p1.x=p2.x ; p1.y=p2.y;
而数组不能这样做
结构指针
和函数不同的是,结构变量的名字并不是结构变量的地址,必须使用&运算符
eg; struct date *pDate = &today
结构与函数
结构作为函数参数
int number (struct date b)
- 整个结构可以作为参数的值传入函数
- 这时候是在函数内新建一个结构变量,并复制调用者结构的值
- 也可以返回一个整个结构
- 这与数组完全不同
eg:明天是几年几月几号
int numberOfDays ( struct date d);
int main(int argc, char const *argv[])
{
struct date today,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;
}else if ( 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.ln",
tomorrow.year, tomorrow.month, tomorrow.day);
return 0;
}
int numberOfDays(struct date d)
{
int days;
const int daysPerMonth[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];
return days;
}
bool isLeap(struct date d)
{
bool leap = false;
if ( (d.year %4 ==0 && d.year %100 !=0 ) || d.year%400 ==0 )
leap = true;
return leap;
}
输入结构
- 没有直接的方式可以一次scanf一个结构
- 如果我们打算写一个函数来读入结构
错误例子:
#include <stdio.h>
struct point {
int x;
int y;
};
void getStruct(struct point);
void output(struct point);
void main( ) {
struct point y = {0,0};
getStruct(y);
output(y);
}
void getStruct(struct point p) { //该函数中p与main函数中y是不同的,在函数读入的p的数值之后,没有任何东西回到main所以y还是{0,0}
scanf("%d",&p.x);
scanf("%d",&p.y);
printf("%d, %d",p.x, p.y);
}
void output(struct point p) {
printf("%d,%d", p.x, p.y);
}
- 这个输入函数中,创建了一个临时的结构变量,然后把这个结构返回给调用者
正确例子:
结构指针作为参数
下面用一个例子来理解:
struct pointx getStruct(struct point*);
void output(struct point);
void print(const struct point *p);
int main(int argc, char const *argv[])
{
struct point y = {0,0};
getStruct(&y );
output(y ) ;
output(*getStruct(&y ) ) ; //将返回的结构指针取值
print(getStruct(&y) ) ;
}
struct point* getStruct(struct point *p) //传入结构指针
{
scanf ( "%d", &p->x); //指针p指的x的值,&则取这个值(x)的地址
scanf ( "%d",&p->y );
printf ( "%d,%d" ,p->x, p->y );
return p; //返回指针
}
void output(struct point p){ //传入结构值
printf( "%d,%d",p.x, p.y);
}
void print(const struct point *p){
printf("%d,%d",p->x,p->y); //p所指的x的值
}
指向结构的指针
struct date {
int month;
int day;
int year;
}myday;
struct date *p = &myday;
(*p ).month = 12;
p->month = 12;
用->表示指针所指的结构变量中的成员
结构数组
struct date dates[100];
struct date dates[] = {{4,5,2005},{2,4,2005}};
eg:下一秒是什么
struct time {
int hour;
int minutes;
int seconds;
};
struct time timeUpdate(struct time now);
int main(void)
{
struct time testTimes [5] = {{11,59,59},{12,0,0},{1,29,59},{23,59,59},{19,12,27}};
int i;
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);
}
return 0;
}
struct time timeUpdate(struct time now)
{
++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 ;
}
}
}
}
结构中的结构
结构中的结构数组
联合
自定义数据类型
- c语言提供了一种叫做typedef的功能来声明一个已有的数据类型和新名字。比如:
typedef int Length;
使得Length成为int类型的别名 - 这样,length这个名字就可以代替int出现在变量定义和参数声明的地方了
Length a,b,len;
Length number[10];
Typedef
- 声明新类型的名字
- 新的名字是某种类型的别名
- 改善了程序的可读性
typedef char* string[10];
string 是10个字符串的数组的类型
联合union
- 储存:所有成员共享一个空间
同一时间只有一个成员是有效的
union的大小是其最大的成员 - 初始化:对第一个成员做初始化
#include <stdio.h>
typedef union {
int i;
char ch[sizeof(int)];
}CHI;
int main(int argc, char const *argv[])
{
CHI chi;
int i;
chi.i = 1234;
for ( i=0; i<sizeof(int); i++ ) {
printf("%02hhX" , chi.ch[i] );
}
printf( "\n");
return 0;
}
输出:D2040000