C 结构体
和本地变量一样,在函数内部声明的结构类型只能在函数内部使用,所以通常在函数外部声明结构类型。
结构类型和结构变量是两回事,在声明了结构类型后,可以用这种类型定义很多种变量。
声明结构体的形式:
### 1. ###
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度浮点型的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct
{
int a;
char b;
double c;
} s1;
### 2. ###
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度浮点型的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
int a;
char b;
double c;
};
//用标签为SIMPLE的结构体分别声明了变量t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
### 3. ###
//也可以用typedef创建新类型
typedef struct
{
int a;
char b;
double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;
结构体的初始化
和其它类型变量一样,对结构体变量可以在定义时指定初始值。
#include <stdio.h>
struct date
{
int month;
int day;
int year;
} today = {8,05,2020}; //定义结构变量并初始化赋值
int main()
{
struct date thismonth = {.month=8, .year=2020}; //定义结构变量并初始化赋值
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;
}
结构体作为函数参数
eg:
int numberOfDays(struct date d)
整个结构体可以作为参数的值传入函数,也可返回一个结构体。
#include<stdio.h>
#include<stdbool.h> //声明调用布尔类型
struct date{ //声明了一个结构体叫做date
int month;
int day;
int year;
};
bool isLeap(struct date d); //声明创建一个布尔类型的函数,用来判断这一天所在的那一年是不是闰年
int numberOfDays(struct date d);
int main(int argc, char const *argv[])
{
struct date today,tomorrow; //声明两个结构体变量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.\n", 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; //闰年的2月是29天
}
else
{
days=daysPerMonth[d.month-1]; //数组是从0开始计数的,所以这里减1
}
return days; //单一出口,统一返回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;
}
输出为:
Enter today's date(mm dd yyyy):12 12 2222
Tomorrow's date is 2222-12-13.
Enter today's date(mm dd yyyy):02 28 2011
Tomorrow's date is 2011-3-1.
写一个函数来读入结构体
没有直接的方式可以一次scanf一个结构体,如果写一个函数来读入结构体,读入的结构体如何传回来呢?解决方案:在这个输入函数中,创建一个临时的结构体变量,然后把这个结构体返回给调用者。
#include<stdio.h>
struct point{
int x;
int y;
};
struct point getStruct(void);
void output(struct point);
int main(int argc, char const *argv[])
{
struct point y={0,0};
y=getStruct(); //两个结构变量是可以赋值的,通过下边的getStruct函数返回一个结构变量并赋值给另一个结构变量y。
output(y);
}
struct point getStruct(void)
{
struct point p;
scanf("%d", &p.x);
scanf("%d", &p.y);
printf("%d, %d\n", p.x, p.y);
return p;
}
void output(struct point p)
{
printf("%d, %d", p.x, p.y);
}
输出为:
sunyuhang@666:~/c$ ./1
12 24
12, 24
12, 24
指向结构体的指针
struct date{
int month;
int day;
int year;
}myday;
struct date *p = &myday;
(*p).month=12; //通过指针访问结构变量中的成员
p->month=12; //或者,用运算符“->”表示指针所指的结构变量中的成员
#include<stdio.h>
struct point{
int x;
int y;
};
struct point *getStruct(struct point*);
void output(struct point);
void print(const struct point *p);
int main(int argc, char const *argv[])
{
struct point value_xy={0,0};
value_xy.x=0;
value_xy.y=0;
getStruct(&value_xy);
output(value_xy);
output(*getStruct(&value_xy));
print(getStruct(&value_xy));
}
struct point *getStruct(struct point *p)
{
scanf("%d", &p->x);
scanf("%d", &p->y);
printf("%d, %d\n", p->x, p->y);
return p;
}
void output(struct point p)
{
printf("%d, %d\n", p.x, p.y);
}
void print(const struct point *p)
{
printf("%d, %d\n", p->x, p->y);
}
输出为:
sunyuhang@666:~/c$ ./1
1
2
1, 2
1, 2
1
2
1, 2
1, 2
1
2
1, 2
1, 2 //1和2是输入的,总计输入6个数,输出6次。
C enum(枚举)
C enum(枚举).
枚举是C语言中一种基本数据类型。定义格式为:
enum 枚举名 {枚举变量1,枚举变量2,...};
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
可以在定义枚举类型时改变枚举变量的值:
enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加 1。也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5。
C语言共用体(union)
在C语言中,还有另外一种和结构体非常类似的语法,叫做共用体(Union),它的定义格式为:
union 共用体名{
成员列表
};
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。
共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
共用体也是一种自定义类型,可以通过它来创建变量,例如:
union data{
int n;
char ch;
double f;
};
union data a, b, c;
上面是先定义共用体,再创建变量,也可以在定义共用体的同时创建变量:
union data{
int n;
char ch;
double f;
} a, b, c;
如果不再定义新的变量,也可以将共用体的名字省略:
union{
int n;
char ch;
double f;
} a, b, c;
为了访问共用体的成员,我们使用成员访问运算符(.)。成员访问运算符是共用体变量名称和我们要访问的共用体成员之间的一个句号。您可以使用 union 关键字来定义共用体类型的变量。下面的实例演示了共用体的用法:
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
return 0;
}
输出为:
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming
在这里,我们可以看到共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。现在让我们再来看一个相同的实例,这次我们在同一时间只使用一个变量,这也演示了使用共用体的主要目的:
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
printf( "data.i : %d\n", data.i);
data.f = 220.5;
printf( "data.f : %f\n", data.f);
strcpy( data.str, "C Programming");
printf( "data.str : %s\n", data.str);
return 0;
}
输出为:
data.i : 10
data.f : 220.500000
data.str : C Programming
在这里,所有的成员都能完好输出,因为同一时间只用到一个成员。