C语言结构体的基本理解

结构体的定义

结构体定义一般有以下几种:

直接定义
struct Stu
{
    char name[20];
    int age;
} ;
类型名称定义
typedef struct {
    char name[20];
    int age;
} Student;

另一种分开写的格式:

struct Stu {
    char name[20];
    int age;
};
typedef struct Stu Student;

结构体变量定义

使用结构体名称定义:

struct Stu student = {"张三", 12};

使用类型名称定义:

Student student = {"张三", 12};

使用赋值定义:

Student student;
student.age = 12;
// 由于 name 字段使用的是 char[20] 所以必须复制内容到 name 的预开辟内存
memcpy(student.name, "李四", sizeof("李四") + 1 );
// 如果 name 字段使用的是 char* 类型 则可以直接赋值
// student.name = "李四";

使用无须指定字段定义:

Student student = {
	.name = "李四",
	.age = 12,
};
结构体数组定义

直接定义

Student student[2] = {
	{"张三", 12},
	{"李四", 11},
};

或者不写数量也可以,默认会根据定义的数量自动推断:

Student student[] = {
	{"张三", 12},
	{"李四", 11},
};

结构体的基本理解

内存开辟

当我们使用以下命令定义一个 结构体的时候,同时等于开辟了整个结构体的内存空间,所有的结构体属性都类似这样连续的预开辟在空间中(事实上并不是所有的属性都会连续对齐,中间可能会预留一些间隔空间,至于原因可以度娘,这里不做展开);

Student student;

在这里插入图片描述
因此很明显, name char[20] 已经在内存上预留了 20 长度的空间,所以无法直接使用 student.name = "李四" 的方法赋值,必须将内容通过 memcpy 等内存复制方法将内容拷贝到当前空间。

可以通过 sizeof 来验证:

// 类型验证
printf("%s\n", sizeof(Student));	// 24 (char[20] + int(4))
printf("%d\n", sizeof(Student*));	// 4  (指针长度为 int(4))

// 实际取值验证
Student stu;
printf("%s\n", sizeof(stu));	// 24 (char[20] + int(4))
printf("%d\n", sizeof(&stu));	// 4  (指针长度为 int(4))
结构体变量与结构体变量指针

事实上结构体变量和变量指针,可以类似的理解成 char[20]char* 的区别;

char[20] str; 数组开辟之后, 虽然 str 事实上也是一个指针,但是由于指针与预开辟空间的头部地址已经绑定,因此无法再修改 str 地址, 也就是 str 是只读状态,不能通过简单的 = 赋值;

结构体也是类似 Student aStudent b 之间 无法使用 a = b 进行赋值;

Student stu1 = {"张三", 12};
Student stu2 = {"李四", 11};
stu1 = stu2;	// 编译无法通过!!!

需要使用指针操作来解决以上问题

Student stu1 = {"张三", 12};
Student stu2 = {"李四", 11};
Student* stu1p = &stu1;
stu1p = &stu2;	// 编译通过, stu1p 从原来指向 stu1 变为 指向 stu2
读取结构体属性

对于结构体,使用 . 符号读取, 例如: stu.name
对于结构体指针,使用 -> 符号读取, 例如: stu->name

Student stu = {"张三", 12};
// 对于结构体,可以直接使用 . 符号读取内容
printf("name : %s\n", stu1.name);
printf("age : %d\n", stu1.age);

// 对于结构体指针,可以使用 -> 读取内容
Student* stup = &stu;
printf("name : %s\n", stup->name);
printf("age : %d\n", stup->age);

结构体传参

单个结构体参数传递

结构体直接传参

Student changeAge(Student s, int age){
	s.age = age;
	return s;
}

Student stu = {"李四", 14};
Student stu2 = changeAge(stu, 100);
printf("%d\n", stu2.age); // 100

指针传参

Student changeAge(Student *s, int age){
	s->age = age;
	return *s;
}

Student stu = {"李四", 14};
Student stu2 = changeAge(&stu, 100);
printf("%d\n", stu2.age); // 100

也可以返回结构体指针

Student* changeAge(Student *s, int age){
	s->age = age;
	return s;
}

Student stu = {"李四", 14};
Student *stu2 = changeAge(&stu, 100);
printf("%d\n", stu2->age); // 100
结构体数组传参

数组传参需要注意的是结构体数组下标获取的不是结构体指针,而是结构体,类似 stu[1] 得到的下标为 1 的 结构体;

我们尝试定义一个数组,之后我们遍历数组并进行打印;

// 这里可以有几种写法,但结果最终都会被编译成 Student *stu 的样子
// 所以喜欢用什么语法就用什么语法
// void each( Student stu[], int n)
void each( Student *stu , int n){
	for( int i = 0 ; i < n ; i ++){
	 	// 这里得到的 stu[i] 是结构体,因此用 . 获取属性
		Student s = stu[i];
		printf("%s\n", s.name);
		// 也可以使用指针做
		Student *sp = &stu[i];
		printf("%s\n", sp->name);
	}
}

Student stu[] = {
	{"张三", 12},
	{"李四", 11},
};
// 传入的时候可以直接传入 stu , 因为 stu 本身就是数组起始地址
// 也可以传入 &stu 指针,最终没有任何区别
// each(&stu, 2)
each(stu, 2);

总结

C 语言结构体虽然有点绕,但在实际开发过程从,它可以最大限度的降低面向过程开发中的大量传参问题,减小变量规模,无论是可读性还是可维护性上,都能得到很大提升;

同时基于结构体,我们可以完成更多的数据结构,比如:树、图、链表、hashmap 等,因此认真和仔细了解结构体特性对于学习 C 语言来讲,是非常有必要的,他是必须克服的关卡!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值