目录
11.2.2 结构与函数
int、float等数据类型可以作为函数的参数,结构作为一种数据类型也可以做到(上面说过)
int NumberOfDays(struct date d)
- 整个结构可以作为参数的值传入函数
- 此时是在函数内新建了一个结构变量,并复制了调用者的结构的值
- 返回值也可以是结构
- (以上特性与数组完全不同)
例子
//求明天的日期
#include<stdio.h>
#include<stdbool.h>
//定义了一个 结构 类型,里面包含三个int类型的变量
struct date{
int month;
int day;
int year;
};
bool isLeap(struct date d);//判断是否是闰年函数
int numberOfDays(struct date d);//给出每个月有几天的函数
int main()
{
struct date today;
struct date tomorrow;
int sig=1;
printf("Enter today's date (mm dd yyyy):");
scanf("%i %i %i",&today.month,&today.day,&today.year);
//%i是旧写法,和%d一样是输出十进制整数的格式控制符
//&today.month是先进行点运算,取成员month,再将输入的数据用&取地址。
//因此点运算优先级高
//若今天不是本月最后一天(也就是本月的天数)
//明天就是天数加一
// printf("today mm %d\n",today.month);
//printf("这个月有%d天\n",numberOfDays(today));//这个月有32764天
if(today.day > numberOfDays(today) ||
today.month >12 ){
sig =0;
printf("非法输入,请检查输入是否正确");
}
if(today.day != numberOfDays(today)){
tomorrow.day = today.day+1;
tomorrow.month = today.month;
tomorrow.year = today.year;
//12月的最后一天,那么第二天就是第二年
}else if(today.month == 12){
tomorrow.day = 1;
tomorrow.month = 1;
tomorrow.year = today.year+1;
}else{
//不是12月,而且今天是本月最后一天,明天就是下个月第一天
tomorrow.day = 1;
tomorrow.month = today.month+1;
tomorrow.year = today.year;
}
if (sig){
printf("Tomorrow's date is %i-%i-%i.\n",
tomorrow.month,tomorrow.day,tomorrow.year);
}
return 0;
}
int numberOfDays(struct date d) //给出每个月有几天的函数
{
int days=0;
const int daysPerMonth[12]={31,28,31,30,31,30,31,31,30,31,30,31};
//printf("函数中的月份 %d\n",d.month); // 3
//printf("daysPerMonth[d.month-1]: %d\n",daysPerMonth[d.month-1]); //31
if(d.month == 2 && isLeap(d))//【if语句里面 判断相等!!一定要用==啊啊啊】
days = 29;
else{
days = daysPerMonth[d.month-1];
//因为数组下标从0开始,month.1对应数组【0】
}
//printf("days %d\n",days);
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;
}
//结构变量是可以直接在函数之间传递的
bool isLeap(struct date d); //判断是否是闰年函数
int numberOfDays(struct date d); //给出每个月有几天的函数
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){ //接收到y结构变量的值,是另外一个结构变量 //因此执行完这个程序后,y不会改变,output函数输出0,0 scanf("%d",&p.x); scanf("%d",&p.y); printf("getstruct:%d,%d\n",p.x,p.y); } void output(struct point p){ printf("output:%d,%d\n",p.x,p.y); }
结果:(没有改变main里面的值)但由于传入函数的是外面结构的克隆体,而不是指向内容的指针
解决方法(一):临时变量
上面用函数读入结构想法是好的,但由于传入函数的是外面结构的克隆体,而不是指向内容的指针,所以有一个方法是:
在传入函数中,创建一个临时的结构变量,然后把临时结构返回给调用者
#include<stdio.h>
struct point{
int x;
int y; };
struct point getStruct(void);
void output(struct point);
int main()
{
struct point y = {0,0};
y = getStruct();//这个函数不需要传入参数
//结构之间可以直接赋值,将临时结构p赋给y
output(y);
}
//一个返回类型为结构的函数
struct point getStruct(void)//这个函数不需要传入参数 但是它返回一个结构类型
{
struct point p;
scanf("%d",&p.x);
scanf("%d",&p.y);
printf("getStruct: %d,%d\n",p.x,p.y);
return p;//但是它返回一个结构类型
}
void output(struct point p){
printf("output: %d,%d\n",p.x,p.y);
}
解决方法(二):结构指针作为参数(更推荐)
当你向函数里传递一个很大的结构,传递指针是比复制更有效的方法。因为C传递结构的方式是值的传递,在被调函数里要建立一个与外面完全相同的结构,既费空间又费时间。
有一个更加简便的符号来表示指针所指的结构变量中的成员:->
struct date{int month; int day; int year ;} myday; //声明了结构类型 同时创建了结构变量
struct date *p = &myday; //取出myday的地址 交给指针p
——指针所指的结构变量(myday)中的成员(month)——
(*p).month = 12; //写法1
p->month = 12; //写法2,中文称为 p所指的month
知道了这个简化写法,解决方法1中的程序就可以写为:
#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()
{
struct point y = {0,0};
getStruct(&y);//int *p=&a; 得到p(指向输出的xy) //getstruct函数返回的是指针p 也就是地址
output(y);
output(*getStruct(&y));
//output函数对其进行了*运算(地址上面的值)
//最后用%d 输出 printf函数拿出了地址 所以要用->指向指针p对应的地址
//才能用%d输出
/************************************************************************************************ */
//这里的参数意义:
//*代表了他要取出指针指向的变量,而此时指针指向的是另一个函数的返回值
//所以实际上传入的参数就是上一个函数的返回值
print(getStruct(&y)); //getStruct得到的指针 传给print print继续操作
//还可以做的是:
*getStruct(&y) = (struct point){1,2};
//为什么?因为左边实际上是个变量,这完全符合编译规则和语法
}
//传入指针操作之后又返回,好处是可以在之后继续调用这个指针
struct point* getStruct(struct point *p) //返回指针 struct point * 参数指针
{
scanf("%d",&p->x); //取p所指的y的x
scanf("%d",&p->y); //取p所指的y的y
printf("getStruct:%d,%d\n",p->x,p->y);
return p;
//返回得到的参数p
}
void output(struct point p){
printf("output:%d,%d\n",p.x,p.y);
}
void print(const struct point *p)
{
//由于只进行输出,传入指针可以是const
printf("print: %d %d",p->x,p->y);
}
11.2.3 结构中的结构
结构数组
定义了一个数组,数组里面的每一个单元都是我自己定义的变量
int a[100] //基础类型
struct time a[100] //自己声明的类型
例子:
struct date dates[100];
//定义了一个结构数组,100字节大小的数组,每一个单元都是一个结构变量
struct date dates[]={
{4,5,2005},//dates[0]
{2,4,2005} //dates[1]
};
//调用内部数据:
printf("%i",dates[0].day);
printf("%.2f");
表示以实型(也成浮点型)输出且保留两位小数.如果小数点后不够两位用0表示或着随机的数字,如果超过2位,四舍五入
结构中的结构
结构变量中可以是基础变量类型,也可以是结构变量
struct dateAndTime{
struct date sdate;
struct time stime;
};
嵌套的结构(以两个对角点确定的矩形)
struct point{
int x;
int y;
};
struct rectangle{
struct point p1;
struct point p2;
};
int main()
{
struct rectangle r;
}
就可以有r.p1.x、r.p1.y、r.p2.x、r.p2.y等
来表示每个点
若有
struct rectangle r,*rp;
rp = &r;
则有四种等价形式:
- r.p1.x 取出r里的p1的x
- rp->p1.x rp指向的p1里的x
- (r.p1).x 加不加括号没什么区别,因为点运算符是从左往右的
- (rp->p1).x 同上,因为是指针先指,结构再取
- 但没有rp->p1->x (因为p1不是指针,它是个结构,当然不能指向别的东西)
结构中的结构的数组
无限套娃:
#include<stdio.h>
struct point{
int x;
int y;
};
struct rectangle{
struct point p1;
struct point p2;
};
void printRect(struct rectangle r)
{
printf("<%d,%d> to <%d,%d>\n",
r.p1.x, r.p1.y, r.p2.x, r.p2.y);
}
int main()
{
int i;
struct rectangle rects[]=
{ {{1,2},{3,4}},//{ 1 2, 3 4} 12是p1 34是p2
{{5,6},{7,8}}
}
//rects[0].p1.x和y == {1,2}
//rects[0].p2.x和y == {3,4}
for(i=0;i<2;i++)
printRect(rects[i]);
return 0;
}