第17天:结构与函数

165 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

1. 结构运算

#include <stdio.h>

struct date {
	int month;
	int day;
	int year;
};

int main (int argc, char const *argv [] ) {
	struct date today;

	today = (struct date) {
		07, 31, 2014
	};

	struct date day;

	day = 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) ;
	return 0;
}

分析:

  • 输出的两个结果都是一样的
  • 添加上:day.year = 2023;结果会变成: image.png
  • 所以说day和today是两个完全不同的结构的变量。
  • 在做:day = today;过程中,day得到了today里所有成员的值

2. 结构指针

  • 和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
  • struct date*pDate=&today;
    • 可以用这条语句定义一个指向结构体的指针,让它取了结构的地址
    • 若去掉了&,编译器会报错,所以结构的名字并不是地址

3. 结构与函数

  • 声明了结构就有了自定义的数据类型,可以作为函数的参数

3.1 结构作为函数参数

例如:

int numberOfdays(struct date d)
  • 整个结构可以作为参数的值传入函数
  • 这时候是在函数内新建一个结构变量,并复制调用者的结构的值
  • 也可以返回一个结构
  • 这与数组完全不同
#include <stdio.h>
#include <stdbool.h>

struct 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;
	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) {
		//月尾年尾:日需要加一,月份变成1月,年份需要加一
		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))
		//闰年,2月份
		days = 29;
	else
		//非闰年的2月份
		days = daysPerMonth [d.month - 1];
	//这里需要减一是因为d.month=2,只有减一才能取到28
	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;
}

分析:

  • 这是一个通过输入日期判断明天的日期的程序
  • isLeap这个函数是判断是否是闰年
  • &today.year取成员优先级高,先取成员,再取地址
  • numberOfDays符合单一出口原则

3.2 输入结构

  • 结构没有直接的方式可以一次scanf一个结构
  • 如果我们打算直接写一个函数来读入结构
#include <stdio.h>

struct point {
	int x;
	int y;
};
void getStruct (struct point);
void output (struct point);

int main () {
	struct point y = {0, 0};
	getStruct(y);
	output (y);
}

void getStruct (struct point p) {//接收到了y结构变量的数值
	scanf ("%d", &p.x);
	scanf ("%d", &p.y);
	printf ("%d, %d\n", p.x, p.y);
}

void output (struct point p) {
	printf ("%d, %d\n", p.x, p.y);
}
分析:
- 这个函数输出两组值,一组是用户输入的数值,一组是初始化时赋的0,0
- p只是有y的值,跟y没有任何联系,所以在函数里对y做任何操作,y都不会有任何改变
  • 但是读入的结构如何送回来呢?
  • 记住c在函数调用时是传值的
    • 所以函数中的p与main中的y是不同的
    • 在函数读入了p的数值之后,没有任何东西回到main,所以还是{0,0}

3.3 解决的方案

  • 之前的方案,把一个结构传入了函数,然后在函数中操作,但是没有返回回去
    • 问题在于传入函数的是外面那个结构的克隆体,而不是指针
    • 传入结构和传入数组是不同的在这个输入函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者
#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();
	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\n", p.x, p.y);
}

分析:

  • 不传入任何值,返回一个结构变量
  • 通过结构变量赋值,覆盖掉y原来的值 总结:
  • 在传一个结构给函数这种方式当中,不传结构,传结构的指针,这是一种更被推荐的方式
  • K&R说过(p.131)
    • “If a large structure is to be passed to afunction, it is generally more efficient to pass a pointer than to copy the whole structure”
    • c语言结构的传递时值的传递,既费空间,又费时间