【零基础玩转C语言】结构体

本文详细介绍了C语言中结构体的基础概念,包括结构体声明、成员类型、变量定义和初始化,以及结构体成员的访问方法。重点讨论了结构体传参时的性能优化,提倡使用地址传参而非结构体实例以减少系统开销。

1. 结构体的声明

1.1 结构的基础知识

  • 结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.2 结构的声明

struct tag
{
member-list;
}variable-list;

例如描述一个学生:

typedef struct Stu
{
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
}Stu;//分号不能丢

1.3 结构成员的类型

结构的成员可以是标量、数组、指针,甚至是其他结构体。

1.4 结构体变量的定义和初始化

有了结构体类型,那如何定义变量,其实很简单。

truct Point
{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2

//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};

struct Stu //类型声明
{
	char name[15];//名字
	int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化


struct Node
{
	int data;
	struct Point p;
	struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

2. 结构体成员的访问

  • 结构体变量访问成员
    结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。
    例如:
    在这里插入图片描述

我们可以看到 s 有成员 nameage
那我们如何访问s的成员?

struct S s;
strcpy(s.name, "zhangsan");//使用.访问name成员
s.age = 20;//使用.访问age成员
  • 结构体指针访问指向变量的成员
    有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针。
    那该如何访问成员。
    如下:
struct Stu
{
	char name[20];
	int age;
};

void print(struct Stu* ps)
{
	printf("name = %s age = %d\n", (*ps).name, (*ps).age);
	//使用结构体指针访问指向对象的成员
	printf("name = %s age = %d\n", ps->name, ps->age);
}

int main()
{
	struct Stu s = {"zhangsan", 20};
	print(&s);//结构体地址传参
	return 0;
}

3. 结构体传参

直接上代码:

#include <stdio.h>

struct S
{
	int data[1000];
	int num;
};

struct S s = { {1,2,3,4}, 1000 };

//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}

//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}

int main()
{
	print1(s); //传结构体
	print2(&s); //传地址

	return 0;
}

上面的 print1print2 函数哪个好些?
答案是:首选print2函数。

原因:

  • 函数传参的时候,参数是需要压栈的。
  • 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

结论: 结构体传参的时候,要传结构体的地址。

### 关于二叉树的C语言实现 以下是基于给定引用内容以及专业知识整理的关于二叉树在C语言中的实现代码及其常见操作示例。 #### 1. 定义二叉树结构体 定义一个简单的二叉树节点结构体 `TreeNode` 和指针类型 `Tree`,用于表示一棵二叉树[^1]。 ```c struct tree_node { char id; struct tree_node* left; struct tree_node* right; }; typedef struct tree_node TreeNode; typedef struct tree_node* Tree; ``` --- #### 2. 构建二叉树 可以通过前序遍历序列来构建二叉树。以下是一个示例函数,输入字符串 `"ABD##E#H##CF##G##"` 来创建对应的二叉树[^2]。 ```c #include <stdio.h> #include <stdlib> char str[] = "ABD##E#H##CF##G##"; int index = 0; Tree create_tree() { if (str[index] == '#') { // 如果遇到'#',则返回NULL表示该位置为空 index++; return NULL; } Tree node = (Tree)malloc(sizeof(TreeNode)); node->id = str[index++]; // 当前字符作为节点值 node->left = create_tree(); // 创建左子树 node->right = create_tree(); // 创建右子树 return node; } ``` --- #### 3. 前序遍历 前序遍历按照根 -> 左子树 -> 右子树的顺序访问节点。 ```c void preorder(Tree root) { if (!root) return; // 空节点直接返回 printf("%c ", root->id); // 访问当前节点 preorder(root->left); // 遍历左子树 preorder(root->right); // 遍历右子树 } ``` --- #### 4. 中序遍历 中序遍历按照左子树 -> 根 -> 右子树的顺序访问节点。 ```c void inorder(Tree root) { if (!root) return; // 空节点直接返回 inorder(root->left); // 遍历左子树 printf("%c ", root->id); // 访问当前节点 inorder(root->right); // 遍历右子树 } ``` --- #### 5. 后序遍历 后序遍历按照左子树 -> 右子树 -> 根的顺序访问节点。 ```c void postorder(Tree root) { if (!root) return; // 空节点直接返回 postorder(root->left); // 遍历左子树 postorder(root->right); // 遍历右子树 printf("%c ", root->id); // 访问当前节点 } ``` --- #### 6. 层次遍历 层次遍历利用队列依次访问每一层的节点。 ```c #include <queue> void level_order(Tree root) { if (!root) return; // 若根节点为空,则退出 std::queue<Tree> q; q.push(root); while (!q.empty()) { Tree current = q.front(); q.pop(); printf("%c ", current->id); // 输出当前节点值 if (current->left) q.push(current->left); // 将左孩子入队 if (current->right) q.push(current->right); // 将右孩子入队 } } ``` --- #### 7. 销毁二叉树 释放整个二叉树占用的空间。 ```c void destroy_tree(Tree root) { if (!root) return; // 空节点无需处理 destroy_tree(root->left); // 递归销毁左子树 destroy_tree(root->right); // 递归销毁右子树 free(root); // 释放当前节点内存 } ``` --- #### 8. 统计节点数量 统计整棵树的节点总数。 ```c int count_nodes(Tree root) { if (!root) return 0; // 空节点不计入 int left_count = count_nodes(root->left); // 获取左子树节点数 int right_count = count_nodes(root->right); // 获取右子树节点数 return 1 + left_count + right_count; // 总结点数为左右子树之和加一 } ``` --- #### 9. 统计叶子节点数量 统计整棵树的叶子节点(即无子节点的节点)的数量。 ```c int count_leaves(Tree root) { if (!root) return 0; // 空节点不计入 if (!root->left && !root->right) return 1; // 叶子节点条件 return count_leaves(root->left) + count_leaves(root->right); // 递归计算左右子树 } ``` --- #### 10. 判断是否为完全二叉树 判断一棵二叉树是否为完全二叉树。 ```c bool is_complete_binary_tree(Tree root, int index, int total_nodes) { if (!root) return true; // 空节点视为满足条件 if (index >= total_nodes) return false; // 节点索引超出总节点数范围 bool left_check = is_complete_binary_tree(root->left, 2 * index + 1, total_nodes); bool right_check = is_complete_binary_tree(root->right, 2 * index + 2, total_nodes); return left_check && right_check; // 子树均需满足条件 } ``` ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值