C语言结构体:从声明、访问到传参的完整指南

云舒
云舒
发布时间:2026-10-16 | 8人浏览过
原创

在C语言中,内置数据类型(如int、char)仅能表示单一属性,难以应对复杂实体(如一本书包含书名、作者、价格等多类信息)。结构体(Struct)是解决此问题的关键工具,它将不同类型的数据组合在一起。本文将从结构体的声明定义、访问方式、指针操作及传参优化四个维度进行详细解析。

一、结构体的声明与定义

结构体是一种自定义数据类型,允许将多个不同类型的变量聚集在一个单元中。理解其“声明”与“定义”的区别是掌握内存管理的基础。

第1步:使用 struct 关键字声明结构体类型

结构体声明用于定义数据的“蓝图”或“框架”。它描述了结构体内有哪些成员变量以及它们的类型。此时编译器仅记录描述信息,不分配实际内存空间。

语法格式:

struct 结构体名称 {
	成员类型1 成员名1;
	成员类型2 成员名2;
	// ... 其他成员
}; // 注意末尾的分号不能丢失

示例:

struct Book {
	char name[20];   // 书名
	char author[20]; // 作者
	float price;     // 价格
};// 分号一定不能丢

成员类型限制: 成员可以是基本数据类型、数组、指针,甚至是另一个结构体(即嵌套结构体)。

第2步:定义结构体变量以分配内存

仅有声明不足以使用数据,必须定义结构体变量才能在内存中开辟空间。定义语法为:struct 结构体名称 变量名。

根据变量作用域不同,内存分布有所差异:

  1. 局部变量:在函数内部定义,存放在栈区(Stack),函数结束时自动释放。
  2. 全局变量:在结构体声明时直接定义,存放在静态区(Static Data Area),生命周期贯穿整个程序。

示例代码:

// 此处 b1, b2 为全局变量,已在静态区分配空间
struct Book {
	char name[20];
	char author[20];
	float price;
}b1, b2;

int main() {
	struct Book book; // 局部变量,在栈区分配空间
	return 0;
}

二、初始化与成员访问

第3步:初始化结构体变量

定义变量的同时可以赋予初始值。初始化顺序必须严格对应结构体成员定义的顺序,并使用大括号包裹。

int main() {
	// 定义并初始化:书名、作者、价格
	struct Book book = { "《笑傲江湖》", "金庸", 30.0f };
	return 0;
}

第4步:访问结构体成员

通过点操作符(.)访问结构体内部的成员变量。该操作符具有最高的优先级之一,且左侧必须是具体的结构体变量。

#include <stdio.h>

int main() {
	struct Book book = { "《笑傲江湖》", "金庸", 30.0f };
	
	// 使用点操作符访问各成员
	printf("书名: %s\n", book.name);
	printf("作者: %s\n", book.author);
	printf("价格: %.2f\n", book.price);
	
	return 0;
}

三、高级应用:嵌套、指针与传参

第5步:处理嵌套结构体

当结构体成员本身又是另一个结构体时,称为嵌套结构体。访问深层成员需连续使用多次点操作符,直至到达最底层的基本类型。

struct S {
	int a;
	char c;
	double d;
};

struct T {
	struct S s;   // 嵌套的结构体成员
	char name[20];
	int num;
};

int main() {
	// 初始化嵌套结构体需注意括号层级
	struct T t = { {100, 'c', 3.14}, "里斯", 30 };
	
	// 逐级访问:t -> s -> a
	printf("%d %c %f %s %d\n", t.s.a, t.s.c, t.s.d, t.name, t.num);
	return 0;
}

第6步:使用结构体指针优化访问

除了存储变量,还可以定义指向结构体的指针。由于数组名代表首元素地址,而结构体变量名不代表地址,获取地址需使用取地址运算符(&)。

访问指针所指向的成员有两种等价写法:

  1. (*指针).成员名:先解引用得到结构体实例,再用 . 访问。
  2. 指针->成员名:使用箭头运算符,专用于指针访问成员。

优先级提醒: 点操作符(.)优先级高于解引用(*),因此 *pt.name 会被解析为 *(pt.name),这通常不是预期行为。必须加括号:(*pt).name。

int main() {
	struct T t = { {100, 'c', 3.14}, "里斯", 30 };
	struct T* pt = &t; // 获取地址
	
	// 方法1:(*pt).member
	// 方法2:pt->member (推荐,直观表明是指针)
	printf("%d %c %f %s %d\n", pt->s.a, pt->s.c, pt->s.d, pt->name, pt->num);
	return 0;
}

第7步:结构体传参的最佳实践

在调用函数时,结构体可以作为参数传递。常见的误区是直接传递整个结构体实例,但这在结构体较大时会引发严重的性能问题。

性能对比:

  • 值传递(传结构体):将整个结构体内容复制到函数栈帧中。如果结构体包含大量数据或数组,会导致巨大的栈空间和拷贝开销,降低执行效率。
  • 地址传递(传指针):仅传递一个固定大小(通常为4或8字节)的地址。无论结构体多大,压栈开销恒定且极小。

推荐写法:

struct S {
	int arr[100];
	int num;
	char ch;
	double d;
};

// 错误示范:值传递,性能差
void print1(struct S ss) {
	printf("%d %d %d %c %.2f\n", ss.arr[0], ss.arr[2], ss.num, ss.ch, ss.d);
}

// 正确示范:地址传递,高效
void print2(const struct S* ps) {
	printf("%d %d %d %c %.2f\n", ps->arr[0], ps->arr[2], ps->num, ps->ch, ps->d);
}

int main() {
	struct S s = { {1,2,3,4,5}, 100, 'w', 3.14 };
	
	print2(&s); // 优先选择
	return 0;
}

总结

本文梳理了C语言结构体的核心知识点:从声明定义的区分,到使用 . 和 -> 运算符访问成员;从嵌套结构的层级展开,到结构体传参时的性能考量。在实际开发中,面对大型数据结构,始终应优先选择通过指针(地址)进行传参,以保证程序的高效运行。掌握这些基础不仅有助于理解C语言的底层内存模型,也为后续学习链表、树等复杂数据结构奠定基础。

以上就是C语言结构体声明、访问与优化的详细内容,更多关于C语言进阶实战的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表