C语言基础day9

本文详细讲解了C语言中的结构体,包括结构体的定义、成员访问、结构体指针、结构体数组及初始化,以及结构体在内存中的对齐规则。此外,还介绍了如何利用结构体实现特定功能,如统计低于平均分的学生人数、反向存储数组、字符串追加等。同时,讨论了结构体大小的计算,并提供了多个实例和练习题以加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

复习

什么是指针变量

存放地址的变量

地址运算

第一 偏移 ±

​ 偏移一个单位是地址的类型长度

第二 间接 * 一元运算 在操作数左边

​ 间接运算是针对地址的运算,间接运算得到的结果不是值,而是一个变量本身。its self

定义指针变量

指向整型的指针变量

int* p;//*说明p是指针变量,*在这里不是运算符,只要是出现在声明的语句中的符号都不是运算符,都是用来说 明标识符身份的。

[]

()

int a;

int* p = &a;//初始化

int b;

p = &b;//赋值

在不是针对硬件编程时,不要自己写地址常量,因为我们自己写的地址常量不合法。

指针变量的意义

可以再被调函数中直接使用主调函数的变量。

指针与数组的关系

数组名就是数组的首元素地址,地址的类型是数组的元素类型,数组名是地址常量;

数组的元素是连续的。所以可以通过地址偏移,访问每个数组的元素。

C语言字符串的两个要素

首地址 和 ‘\0’ %s无论是输入输出还将来我们要学的格式化字符串,%s都需要字符串的首地址,到’\0’

作业1:

编写函数fun:从n个学生的成绩中统计出低于平均分的学生人数,

由返回值返回,平均分存放在形参 ptr_aver 所指的存储单元中(即平均分由参数返回)。

函数最终会有两个结果,人数通过返回值给主调函数,平均分通过形参里的指针变量ptr_aver 给主调函数。

函数原型为:int fun( float *s, int n, float *ptr_aver );

例如:若输入8名学生的成绩如下 : 80.5 60 72 90.5 98 51.5 88 64

则低于平均分的学生人数为:4 (平均分:75.5625)。

#include <stdio.h>

int fun(float* s, int len, float* ptr_aver);

int main()
{
	float scores[] = {80.5,60, 72, 90.5, 98, 51.5, 88, 64};
	float avg;
	//将得到的平均值 给avg
	//返回值给 count
	int count = fun(scores, 8, &avg);
	printf("%d  %f\n", count, avg);
	return 0;
}

int fun(float* s, int len, float* ptr_aver)
{
	int i;
	float sum = 0;
	for(i = 0;i < len;i++)
	{
		sum += s[i];//s[i]等价于*(s+i)
	}
	*ptr_aver = sum/len;
	int count = 0;
	for(i = 0;i < len;i++)
	{
		if(s[i] < *ptr_aver)
		{
			count++;
		}
	}
	return count;
}

作业2:

编写一个函数,将数组中n个数按反顺序存放

假设主调函数中有数组int a[8] = {3,4,5,6,7,9,9,2};

执行完被调函数,数组变成2 9 9 7 6 5 4 3

#include <stdio.h>


void reverse(int* p, int len);
int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9};
	reverse(arr, 9);
	int i;
	for(i = 0;i < 9;i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

void reverse(int* p, int len)
{
	int start = 0;
	int end = len-1;
	while(start<end)
	{
		int t = p[start];
		p[start] = p[end];
		p[end] = t;
		start++;
		end--;
	}
}

作业3:

编写函数,实现字符串追加 功能。但不能调用字符串函数;

char s1[20] = “hello ”;

char s2[] = “world”;

调用函数后,将world放到s1数组的”hello ”后面

知识点:当数组存放字符串时,作为函数的参数只需要传递首地址,因为字符串有’\0’作为结尾。

实现strcat的功能,但是不能调用strcat

#include <stdio.h>


void myStrcat(char* buf, char* str);

int main()
{
	char buf[11] = "hello ";
	*(buf+12) = 12;
	//使用字符串常量作为参数,是将字符串常量的首地址传给形参
	myStrcat(buf, "world");
	printf("%s\n", buf);
	return 0;
}

void myStrcat(char* buf, char* str)
{
	//找到buf的\0位置
	int i = 0;
	while(buf[i] != '\0')
	{
		i++;
	}
	int j = 0;
	while(str[j] != '\0')
	{
		buf[i++] = str[j++];
	}
	//第二个循环,并不能把\0写入buf
	//buf[i] = str[j];//在循环结束时str[j]就是\0
}

1、结构体

1. 什么是结构体

是我们自定义的类型。

格斗游戏

人物用什么类型表示?

人物

名字 性别 年龄 身材 功 防 血

char name[100];

char gender;

int age;

身材(身高 int high 体重 weight)

int act;

int def;

int hp;

结构体可以将无数的属性封装成一个类型。

结构体的本质是类型。

2. 如何定义变量

//struct是定义结构体的关键字
//student是结构体的名字
struct student  //定义了一个结构体类型叫 student
{
    char name[20];	    //结构体的成员
    char number[20];  	//结构体的成员
	int grade;			//结构体的成员
	float score;		//结构体的成员
}; //{}中包含结构体类型的成员   注意,定义结构体类型,结尾有;

int main()
{
    //struct student是定义结构体变量时的完成的类型名
    struct student xiaoming;//xiaoming是一个结构体变量
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QpNDviBT-1645663529862)(C:\Users\mudonghai\Desktop\C基础\day9\image-20220114103711108.png)]

3.访问成员变量

成员运算符 .

成员运算符的运算结果,不是值,而是成员变量本身。

结构体变量名.成员名

给成员变量赋值的方式,取决于成员变量类型。

strcpy(xiaoming.name, "Alice"); //xiaoming.name得到的是xiaoming里的name数组名,是常量
strcpy(xiaoming.number,"17001001001");
xiaoming.grade = 90;//xiaoming.grade得到的是成员变量grade本身。
xiaoming.score= 6000.00;

4. 输出

不能直接把结构体变量格式化,只能一个一个属性单独进行格式化。

printf("name:%s,number:%s,grade:%d,score:%f\n", xiaoming.name, xiaoming.number, xiaoming.grade, xiaoming.price);

示例1:

#include <stdio.h> 
#include <string.h>

struct student 
{
	char name[20];
	char number[20];
	int grade;
	float score;
};

int main()
{
	struct student  s1;
	strcpy(s1.name, "Alice");
	strcpy(s1.number,"17001001001");
	s1.grade = 90;
	s1.score= 6000.00;
	printf("%s : %s, %d, %f\n", s1.name, s1.number, s1.grade, s1.score);
}

练习1:

构建一个学生结构体,包含成员:姓名、年龄、学号、专业(major/professor)

定义变量用来保存自己的学生信息、赋值、并输出。

#include <stdio.h>
#include <string.h>

struct Student
{
	char name[20];
	int age;
	char number[20];
	char major[20];
};

int main()
{
	struct Student xiaoming;
	strcpy(xiaoming.name, "xiaoming");
	xiaoming.age = 18;
	strcpy(xiaoming.number, "2022114");
	strcpy(xiaoming.major, "计算机");

	printf("name:%s\nage:%d\nnumber:%s\nmajor:%s\n",
			xiaoming.name, 
			xiaoming.age,
			xiaoming.number,
			xiaoming.major);
	return 0;
}

练习2:

在上题基础上,请将输出的功能。 用一个函数 来实现: void print_info(struct Student stu);

#include <stdio.h>
#include <string.h>

struct Student
{
	char name[20];
	int age;
	char number[20];
	char major[20];
};

void print_info(struct Student stu);

int main()
{
	struct Student xiaoming;
	strcpy(xiaoming.name, "xiaoming");
	xiaoming.age = 18;
	strcpy(xiaoming.number, "2022114");
	strcpy(xiaoming.major, "计算机");

	print_info(xiaoming);
	return 0;
}

void print_info(struct Student stu)
{
	printf("name:%s\nage:%d\nnumber:%s\nmajor:%s\n",
			stu.name, 
			stu.age,
			stu.number,
			stu.major);
}

5. 结构体指针

指针访问成员: 结构体指针名->成员名称。

-> 成员运算符 和 . 有一个区别, ->左值是结构体指针变量 .左值是结构体变量

int main()
{	
    //定义结构体变量,并赋值
    struct Student xiaoming;	
    strcpy(xiaoming.name, "xiaoming");	
    xiaoming.age = 18;	
    xiaoming.number = 10010;	
    strcpy(xiaoming.major, "huaqing"); 
    
    
    struct Student* p = &xiaoming;//使用结构体变量小明初始化结构体变量p
    //因为p是结构体指针变量,所以使用p访问结构体变量的成员,需要使用成员运算符->
    printf("%s %d %d %s\n", p->name, p->age, p->number, p->major); 
    return 0;
}

练习3:

编写函数setStudent,实现功能:设置学生信息

1.在main函数中定义一个结构体变量

2.在setStudent函数中调用scanf给结构体的每一个成员赋值

3.需要把main函数中的结构体变量的地址传给setStudent

#include <stdio.h>
#include <string.h>

struct Student
{
	char name[20];
	int age;
	char number[20];
	char major[20];
};

void setStudent(struct Student* p);

int main()
{
	struct Student xiaoming;
	setStudent(&xiaoming);
	printf("%s %d %s %s\n", xiaoming.name, 
			xiaoming.age, xiaoming.number,
			xiaoming.major);
	return 0;
}

void setStudent(struct Student* p)
{
	scanf("%s%d%s%s", p->name, &p->age, p->number, p->major);
}

6. 结构体数组

struct student  s[10];//结构体数组,10个元素,每个元素是结构体类型
s[0];//数组中的第一个元素,结构体类型
s[0].name;//得到的是第一个元素的name成员,是一个数组名

结构体初始化:

struct Student xiaoming = {"xiaoming", //初始化xiaoming的name成员,使用字符串常量初始化字符数组
                           18, //用整型常量初始化xiaoming的age成员
                           "10010", 
                           "huaqing"};

数组初始化:

//完全初始化:
struct Student students[2] = {
		{"xiaoming", 18, 10010, "huaqing"},
		{"xiaoqiang", 19, 10011, "huaqing"}
	};

//部分初始化:
struct Student students[3] = {
		{"xiaoming", 18, 10010, "huaqing"},
		{"xiaoqiang", 19, 10011, "huaqing"}
	};

//默认初始化:
struct Student students[] = {
		{"xiaoming", 18, 10010, "huaqing"},
		{"xiaoqiang", 19, 10011, "huaqing"}
	};

示例2:

struct Student
{
	char name[20];//字符数组具备存放字符串的能力
	//char* name;//指针不具备存放字符串的能力,它只能指向一个字符串
	int age;
	char number[20];
	char major[20];
};

int main()
{
	struct Student students[2] = {
		{"xiaoming", 18, "10010", "huaqing"},
		{"xiaoqiang", 19, "10011", "huaqing"}
	};

    int i;
	for(i = 0;i < 2;i++)
	{
		printf("%s %d %s %s\n", 
               students[i].name, students[i].age, students[i].number, students[i].major);
	}
	return 0;
}
int main()
{
	//字符数组可以存放字符串,我们可以对数组中的字符进行任意修改
	char name[20] = "hello world";
	//指针指向字符串常量,不能通过字符指针修改字符串常量。
	char* str = "hello world";

	name[0] = 'a';
	str[0] = 'a';
	printf("%s\n%s\n", name, str);
	return 0;
}

练习4:

写一个函数print_all,功能是打印所有学生信息,在main里定义学生结构体数组,

然后调用print_all,打印

#include <stdio.h>
#include <string.h>

struct Student
{
	char name[20];
	int age;
	char number[20];
	char major[20];
};

void print_all(struct Student* arr, int len);

int main()
{
	struct Student students[3] = {
		{"xiaolan", 18, "10010", "c++"},
		{"xiaolv", 18, "10086", "java"},
		{"xiaohong", 17, "10000", "c#"}
	};
	print_all(students, 3);
	return 0;
}

void print_all(struct Student* arr, int len)
{
	int i;
	for(i = 0;i < len;i++)
	{
		printf("%s %d %s %s\n",arr[i].name, 
				arr[i].age, arr[i].number, arr[i].major);
	}
}

练习5:

编写函数,实现功能: 查询打印: 年龄 > 25 的学生信息。

#include <stdio.h>
#include <string.h>

struct Student
{
	char name[20];
	int age;
	char number[20];
	char major[20];
};


void print_all(struct Student* arr, int len);

int main()
{
	struct Student students[3] = {
		{"xiaolan", 18, "10010", "c++"},
		{"xiaolv", 26, "10086", "java"},
		{"xiaohong", 17, "10000", "c#"}
	};
	print_all(students, 3);
	return 0;
}

练习6:

编写函数,实现功能: 查询打印:“xiaoli” 的学生信息。

int main()
{
    char name1[20] = "hello world";
    char name2[20] = "hello world";
    printf("%d\n", name1==name2);//数组名是数组的首地址,两个数组的首地址不可能相等。输出0
	return 0;
}
#include <stdio.h>
#include <string.h>

struct Student
{
	char name[20];
	int age;
	char number[20];
	char major[20];
};


void print_all(struct Student* arr, int len);

int main()
{
	struct Student students[3] = {
		{"xiaolan", 18, "10010", "c++"},
		{"xiaoli", 26, "10086", "java"},
		{"xiaohong", 17, "10000", "c#"}
	};
	print_all(students, 3);
	return 0;
}

void print_all(struct Student* arr, int len)
{
	int i;
	for(i = 0;i < len;i++)
	{
		if(strcmp("xiaoli", arr[i].name)==0)
		{
			printf("%s %d %s %s\n",arr[i].name, 
				arr[i].age, arr[i].number, arr[i].major);
	
		}
	}
}

7.结构体嵌套

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

struct id_card 
{
	char name[20];
	struct date birthday;//结构体成员可以是其他的结构体类型
};

示例3:

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

struct id_card 
{
	char name[20];
	struct date birthday;//结构体成员可以是其他的结构体类型
};
int main()
{
    struct id_card  id; 
    strcpy(id.name, "Kayla");//对结构体变量id的name成员赋值
    id.birthday.year = 2000;//对id的birthday成员的year成员赋值
    id.birthday.month = 6; 
    id.birthday.day = 1; 

    printf("%s : %d-%d-%d\n", id.name, id.birthday.year, id.birthday.month, id.birthday.day);
}
 

2、结构体大小计算

结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节

示例4:

#include <stdio.h> 

struct student 
{
	char a;
	char c;
	short b;
};

int main()
{
	printf("%d\n", sizeof( struct student) );//4
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BUr7UnpN-1645663529865)(C:\Users\mudonghai\Desktop\C基础\day9\image-20220114170949493.png)]

示例5:

#include <stdio.h> 

struct student 
{
	char a;
	short age;  
	char c;
};

int main()
{
	printf("%d\n", sizeof( struct student) );
}	

对齐方式,结构体的“一行”有几个字节。“一行”的字节数取决于结构体成员中最大的成员的字节数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xgsl4n60-1645663529865)(C:\Users\mudonghai\Desktop\C基础\day9\image-20220114170804374.png)]

示例6:

#include <stdio.h> 

struct student 
{
	int x;
	char a;
	short b;   
	char c;
};

一个成员,要么自己占一行,如果不能自己占一行,要么全在左边,要么全在右边。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-teVpcuy8-1645663529866)(C:\Users\mudonghai\Desktop\C基础\day9\image-20220114171653130.png)]

示例7:

struct Node
{
	char c;
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct Node));
	return 0;
}

Linux 32位操作系统: 最长的对齐字节数:是4. double 占2个4.

不同的操作系统,最长的对齐字节数不同。

作业1:

1 定义时间结构体: 包含年、月、日成员

2 完成set_date函数

​ 函数声明如下: void set_date(struct date *p, int y, int m, int d);

3 完成print_date函数

​ 函数声明如下: void print_date(struct date *p);

4 完成main函数,

​ 实现如下功能:

​ 1) 定义 时间结构体变量 d1

​ 2) 输入时间,并使用set_date函数设置给变量d1

​ 3) 使用print_date函数输出设置的时间

作业2:

ar a;
short age;
char c;
};

int main()
{
printf("%d\n", sizeof( struct student) );
}


 对齐方式,结构体的“一行”有几个字节。“一行”的字节数取决于结构体成员中最大的成员的字节数。

[外链图片转存中...(img-Xgsl4n60-1645663529865)]



 

## 示例6:

```C
#include <stdio.h> 

struct student 
{
	int x;
	char a;
	short b;   
	char c;
};

一个成员,要么自己占一行,如果不能自己占一行,要么全在左边,要么全在右边。

[外链图片转存中…(img-teVpcuy8-1645663529866)]

示例7:

struct Node
{
	char c;
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct Node));
	return 0;
}

Linux 32位操作系统: 最长的对齐字节数:是4. double 占2个4.

不同的操作系统,最长的对齐字节数不同。

作业1:

1 定义时间结构体: 包含年、月、日成员

2 完成set_date函数

​ 函数声明如下: void set_date(struct date *p, int y, int m, int d);

3 完成print_date函数

​ 函数声明如下: void print_date(struct date *p);

4 完成main函数,

​ 实现如下功能:

​ 1) 定义 时间结构体变量 d1

​ 2) 输入时间,并使用set_date函数设置给变量d1

​ 3) 使用print_date函数输出设置的时间

作业2:

定义学生结构体数组,编写函数对这个数组根据学生成绩进行降序排序,编写函数输出排序后的学生信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

encounter♌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值