#include <stdio.h>
#include <string.h>
#define NUMBER 5
#define NAME_LEN 64
void swap_int (int *x, int *y){
int temp = *x;
*x = *y;
*y = temp;
}
void swap_str(char *sx, char *sy){
char temp[NAME_LEN];
strcpy(temp, sx);
strcpy(sx, sy);
strcpy(sy, temp);
}
void sort(int num[],char str[][NAME_LEN], int n){
int i, j;
for(i = 0; i < n - 1; i++){
for(j = n - 1; j > i; j--){
if (num[j - 1] > num[j]){
swap_int(&num[j - 1], & num[j]);
swap_str(str[j - 1], str[j]);
}
}
}
}
int main(void){
int i;
int height[] = {178, 175, 170, 165, 189};
char name[][NAME_LEN] = {"陈二", "张三", "李四", "王五", "赵六"};
for(i = 0; i < NUMBER; i++){
printf("%2d : %-8s%4d\n", i + 1, name[i], height[i]);
}
sort(height, name, NUMBER);
puts("\n按身高进行升序排列。");
for(i = 0; i < NUMBER; i++){
printf("%2d : %-8s%4d\n", i + 1, name[i], height[i]);
}
return 0;
}
运行结果:
使用结构体简化代码:
#include <stdio.h>
#include <string.h>
#define NUMBER 5
#define NAME_LEN 64
typedef struct {
char name[NAME_LEN];
int height;
float weight;
long score;
} Student;
void swap_Student (Student *x, Student *y){
Student temp = *x;
*x = *y;
*y = temp;
}
void sort_by_height(Student a[], int n){
int i, j;
for(i = 0; i < n - 1; i++){
for(j = n - 1; j > i; j--){
if (a[j - 1].height > a[j].height){
swap_Student (&a[j - 1], &a[j]);
}
}
}
}
int main(void){
int i;
Student std[] = {
{"陈二", 178, 61.2, 80},
{"张三", 175, 62.5, 73},
{"李四", 170, 86.2, 60},
{"王五", 165, 72.2, 99},
{"赵六", 189, 77.5, 100},
};
puts("姓名 身高 体重 分数");
for(i = 0; i < NUMBER; i++){
printf("%-8s %6d %6.1f %5ld\n", std[i].name, std[i].height, std[i].weight, std[i].score);
}
sort_by_height(std, NUMBER);
puts("\n按身高进行升序排列。");
puts("姓名 身高 体重 分数");
for(i = 0; i < NUMBER; i++){
printf("%-8s %6d %6.1f %5ld\n", std[i].name, std[i].height, std[i].weight, std[i].score);
}
return 0;
}
运行结果
注:
- 可以使用 typedef 声明为结构体赋上简洁的 typedef 名。
- Student 为结构名。
- {}中声明的 name、height、weight、schols等成为结构体成员。
- 访问结构体对象的各个成员时使用 . 运算符,该运算符称为句点运算符。
例如:
a . b 表示结构体 a 的成员 b (对象名 . 成员名)
结构体的命名空间
只要命名空间不同,就可以使用拼写相同的标识符(名字)。命名空间的分类有以下四种。
- (标签)名
- 小标签 名
- 成员 名
- 一般性标识符
int main(void){
struct x { // 小标签名
int x; // 成员名
int y;
} x; // 变量名
x: // 标签名
x.x = 1; // 变量名.成员名
x.y = 2; // 变量名.成员名
return 0;
}
注:
只要不属于同一个命名空间,即使在同一有效范围内使用相同的名字,也不会产生任何问题。
typedef struct {
char name[NAME_LEN];
int height;
float weight;
long schols;
} Student;
上述结构体可以写成如下形式:
struct student{
char name[NAME_LEN];
int height;
float weight;
long schols;
};
该结构体形式的使用:
#include <stdio.h>
#include <string.h>
#define NAME_LEN 64
struct students {
char name[NAME_LEN];
int height;
float weight;
long score;
};
int main(void){
struct students stu;
strcpy(stu.name, "vvcat"); // 直接复制会报错
stu.height = 175;
stu.weight = 62.5;
stu.score = 88;
printf("姓名 = %s\n", stu.name);
printf("身高 = %d\n", stu.height);
printf("体重 = %.1f\n", stu.weight);
printf("分数= %ld\n", stu.score);
return 0;
}
运行结果:
strcpy(stu.name, "vvcat"); // 直接赋值会报错
如果写成
stu.name = "vvcat"; //初始化错误
相当于
char str[5];
str = "vvcat"; // 初始化错误
初始化也可以进行简写:
struct students stu;
strcpy(stu.name, "vvcat"); // 直接复制会报错
stu.height = 175;
stu.weight = 62.5;
stu.score = 88;
简写方式:
struct students stu = { "vvcat", 175, 62.5, 88};
这样一行就完成了多行的代码赋值操作,使代码更加简洁。
如果不对结构体进行初始化:
#include <stdio.h>
#include <string.h>
#define NAME_LEN 64
struct students {
char name[NAME_LEN];
int height;
float weight;
long score;
};
int main(void){
struct students stu = { "vvcat", 175};
printf("姓名 = %s\n", stu.name);
printf("身高 = %d\n", stu.height);
printf("体重 = %.1f\n", stu.weight);
printf("分数= %ld\n", stu.score);
return 0;
}
运行结果:
注:
结构体对象 o 中的成员 m 可以通过o.m来访问。
声明结构体时所赋的初始值的形式是,将各个结构体成员的初始值依次排列在{}里面,并用逗号分割。未赋初始值的成员被初始化为0。
结构体成员和 -> 运算符
#include <stdio.h>
#define NAME_LEN 64
struct student{
char name[NAME_LEN];
int height;
float weight;
long score;
};
void update_height_weight(struct student *std){
if((*std).height < 180){
(*std).height = 180;
}
if((*std).weight > 80){
(*std).weight = 80;
}
}
void update_score(struct student *std){
if(std -> score < 90){
std -> score = 90;
}
}
int main(void){
struct student stu = {"vvcat", 175, 62.5, 89};
update_height_weight(&stu);
update_score(&stu);
printf("姓名 = %s\n", stu.name);
printf("身高 = %d\n", stu.height);
printf("体重 = %.1f\n", stu.weight);
printf("分数 = %ld\n", stu.score);
return 0;
}
运行结果:
在函数中,身高和体重可以通过以下表达式来访问。
(*std).height
(*std).weight
也可以通过 -> 运算符进行访问
std -> height
std -> weight
在指针变量前加上指针运算符*,就表示该指针指向的对象实体。通过(*std).height 或 std -> height 等表达式可以表示std 指向的对象的成员。
注:
- 不能用 *std.height来表示 *std的身高成员。因为 . 运算符的优先级比指针运算符 * 高,表达式会被解释成 *(std .height),产生语法错误。
- -> 运算符 a->b
用指针访问结构体a 中的成员b。 -> 称为箭头运算符。 - 在表示指针 p 指向的结构体成员 m 时,推荐使用 -> 运算符将(*p)**.**m 简写为 p->m。
- . 运算符和 ->运算符统称为访问运算符。
返回结构体的函数
#include <stdio.h>
struct xyz{
int x;
long y;
double z;
};
struct xyz xyz_of(int x, long y ,double z){
struct xyz temp;
temp.x = x;
temp.y = y;
temp.z = z;
return temp; // 将结构体返回
}
int main(void){
struct xyz s ={0, 0, 0}; // 结构体初始化
s = xyz_of(123, 123456, 12.34); // 为结构体赋值
printf("xyz.x = %d\n", s.x);
printf("xyz.y = %ld\n", s.y);
printf("xyz.z = %f\n", s.z);
return 0;
}
数组和结构体在处理多个对象的集合方面具有诸多相同点,他们统称为聚合类型。
- 元素类型:
数组用于高效地操作“相同类型”数据的集合。二结构体这种数据结构通常用于高效地操作“不同类型”数据的集合(偶尔会有成员类型全部相同的情况)。 - 可否赋值
两个数组的元素个数相同,也不能相互赋值。但是,相同类型的结构体可以相互赋值。
int a[5], b[5];
a = b; // 赋值错误
struct student x, y;
x = y; // 赋值成功