C语言初阶-初识结构体

简介: C语言初阶-初识结构体

1.结构体的声明

1.1结构体的基础知识

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

生活中描述:

人:名字+性别+年龄+身高+身份证号码+地址...

书:书名+作者+出版社+定价+书号...

这些复杂对象不能通过内置类型直接描述和表示,于是就有了结构体。

1.2结构体的声明

例如描述一个学生:

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

其中name,age,sex,id是结构体的成员变量,用来描述结构体对象的相关属性。

上述的strcut Stu是我们的创建的结构体类型,那在主函数中我们就可以通过这个类型创建结构体变量。

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct Stu
{
  char name[20];//姓名
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
};//注意分号不能丢
int main()
{
  struct Stu s1;
  return 0;
}

上述代码中的s1就是我们创建的结构体变量,当然我们也有另外一种方式,就是在创建结构体类型struct Stu的时候,直接在它的大括号外面捎带着创建结构体变量s2,s3和s4。(如下)

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct Stu
{
  char name[20];//姓名
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
}s2, s3, s4;//s2,s3,s4就是结构体变量 - 全局变量
int main()
{
  struct Stu s1;//局部变量
  return 0;
}

代码中的s1,s2,s3,s4都是结构体变量,只不过s1是局部变量,s2,s3和s4是全局变量。

我们也可以在创建结构体类型时,用 typedef 对其进行类型重命名,

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
typedef struct Stu
{
  char name[20];//姓名
  int age;//年龄
  char sex[5];//性别
  char id[20];//学号
}Stu;//类型重命名为Stu
int main()
{
  struct Stu s1;//局部变量
  Stu s2;
  return 0;
}

上述代码用 typedef struct Stu重命名为Stu,那在主函数中创建结构体变量时就可以直接用Stu来代替struct Stu,代码中的s2就是这样创建的。

注意:如果使用 typedef 的时候,结构体类型的大括号外面不是结构体变量,而是类型的重命名

1.3结构体成员的类型

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

struct S
{
  int a;
  char arr[5];
  int* p;
};
struct B
{
  char ch[10];
  struct S s;
  double d;
};
int main()
{
  return 0;
}

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

struct S
{
  int a;
  char arr[5];
  int* p;
}s1 = {100,"bit",NULL};
struct S s2 = { 99,"hello",NULL };
int main()
{
  struct S s3 = { 1,"abc",NULL };
  return 0;
}

上述代码的s1,s2和s3都是结构体变量,{}中是对它们的初始化。

有人这时候有问题了,如果不想按照结构体类型中规定的顺序初始化怎么办?

那就要换一种写法了:

struct S
{
  int a;
  char arr[5];
  int* p;
}s1 = { .p = NULL,.a = 100,.arr = "bit" };
struct S s2 = { .arr = "hello",.p = NULL,.a = 99 };
int main()
{
  struct S s3 = { .a = 1,.p = NULL,.arr = "abc" };
  return 0;
}

这两段代码的作用是一样的,我们可以将它们打印一下,不过这就涉及到结构体成员的访问了。

2.结构体成员的访问

前面我们讲过两个结构体成员访问操作符,即 .  ->

下面我们来打印一下试试:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct S
{
  int a;
  char arr[5];
  int* p;
}s1 = { .p = NULL,.a = 100,.arr = "bit" };
struct S s2 = { .arr = "hello",.p = NULL,.a = 99 };
int main()
{
  struct S s3 = { .a = 1,.p = NULL,.arr = "abc" };
  printf("%d %s %p\n", s1.a, s1.arr, s1.p);
  printf("%d %s %p\n", s2.a, s2.arr, s2.p);
  printf("%d %s %p\n", s3.a, s3.arr, s3.p);
  return 0;
}

运行结果:

想要访问结构体成员,用结构体变量 . 结构体成员名就可以了。

我们也可来尝试对结构体类型 B 创建变量 sb ,初始化并打印一下:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct S
{
  int a;
  char arr[5];
  int* p;
}s1 = { .p = NULL,.a = 100,.arr = "bit" };
struct S s2 = { .arr = "hello",.p = NULL,.a = 99 };
struct B
{
  char ch[10];
  struct S s;
  double d;
};
int main()
{
  /*struct S s3 = { .a = 1,.p = NULL,.arr = "abc" };
  printf("%d %s %p\n", s1.a, s1.arr, s1.p);*/
  struct B sb = { "thanks",{20,"abcd",NULL},3.14 };
  printf("%s %d %s %p %lf", sb.ch, sb.s.a, sb.s.arr, sb.s.p, sb.d);
  return 0;
}

运行结果:

上述代码,因为在struct B 中嵌套了struct S s;所以我们在{}中初始化时要将对struct S s初始化的内容再用个{}包起来。在打印时用两次结构体成员访问操作符(例如:sb.s.a)

以上是点操作符的使用,还有->操作符是在接受一个地址(指针)时使用,

下面看一段代码:

struct Stu
{
  char name[20];
  int age;
};
void set_stu(struct Stu t)
{
  t.age = 20;
  t.name = "王小明";
}
int main()
{
  struct Stu s = { 0 };
  set_stu(s);
  return 0;
}

我们写一个函数set_stu(),对结构体变量s赋值,那上面的代码对吗?

当然不对。

注意这句赋值语句:t.name = "王小明";出现了错误,我们说name是数组名,那你能直接把“王小明”赋给数组名吗?不能,应该是赋给数组名name所在的空间才对!

以前我们学过字符串拷贝strcpy这个函数,这里就可以用了。

struct Stu
{
  char name[20];
  int age;
};
void set_stu(struct Stu t)
{
  t.age = 20;
  strcpy(t.name,"王小明");//字符串拷贝
}
int main()
{
  struct Stu s = { 0 };
  set_stu(s);
    printf("%s %d", s.name, s.age);
  return 0;
}

问题又来了,这样写就对了吗?

运行一下:

结果是0,为什么呢?

那是因为我们在上述代码中使用的是传值调用,前面我们讲过形参是实参的一份临时拷贝,对形参的改变不影响实参的值。

如果用传址调用就可以了:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct Stu
{
  char name[20];
  int age;
};
void set_stu(struct Stu* t)
{
  t->age = 20;
  strcpy(t->name,"王小明");
}
int main()
{
  struct Stu s = { 0 };
  set_stu(&s);
  printf("%s %d", s.name, s.age);
  return 0;
}

因为传址调用,传给形参的是地址,我们在访问结构体变量时就用到了->操作符

下面我们在看一段->操作符的使用示例:

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct Stu
{
  char name[20];
  int age;
  char sex[5];
};
void print(struct Stu* t)
{
  printf("%s %d %s\n", t->name, t->age, t->sex);
  printf("%s %d %s\n", (*t).name, (*t).age, (*t).sex);
}
int main()
{
  struct Stu s = { "翠花",25,"女" };
  print(&s);
  return 0;
}

注意:代码中,t->name (*t).name 的作用是相同的。

3.结构体传参

我们在上面的讲解中已经包含了结构体传参,实际上,结构体传参有两种方式,一种是传结构体,一种是传地址。

那我们直接上代码:

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;
}

按道理这两种方式都可以实现传参,但是print1和print2那个更好些呢?

答案是:首选print2函数。

大家想一下,我们在传参时,形参是实参的一份临时拷贝,而如果传过去的结构体特别大,我们需要开辟同样大小的空间,这是不是对空间的一种浪费?而拷贝也是需要时间的,对时间也是一种浪费。而传地址的话,我们只需要传递4/8个字节大小的地址,然后根据地址找到实参的空间就行。

所以结构体传参时,传结构体的地址是一种比较好的选择。

原因:

函数传参的时候,参数是需要压栈的。

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

今天就学到这里,未完待续。。。

目录
相关文章
|
11天前
|
存储 C语言
C语言程序设计核心详解 第九章 结构体与链表概要详解
本文档详细介绍了C语言中的结构体与链表。首先,讲解了结构体的定义、初始化及使用方法,并演示了如何通过不同方式定义结构体变量。接着,介绍了指向结构体的指针及其应用,包括结构体变量和结构体数组的指针操作。随后,概述了链表的概念与定义,解释了链表的基本操作如动态分配、插入和删除。最后,简述了共用体类型及其变量定义与引用方法。通过本文档,读者可以全面了解结构体与链表的基础知识及实际应用技巧。
|
16天前
|
C语言
c语言中的结构体
本文档详细介绍了C语言中结构体的使用方法,包括结构体的基本定义、变量声明与赋值、数组与指针的应用,以及结构体嵌套、与`typedef`结合使用等内容。通过示例代码展示了如何操作结构体成员,并解释了内存对齐的概念。
|
22天前
|
C语言
C语言结构体赋值的四种方式
本文总结了C语言结构体的四种赋值方式,并通过示例代码和编译运行结果展示了每种方式的特点和效果。
35 6
|
1月前
|
编译器 程序员 Linux
【C语言篇】结构体和位段详细介绍
跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。
|
1月前
|
存储 C语言
C语言------结构体和共用体
这篇文章是关于C语言中结构体和共用体的实训,通过示例代码演示了结构体的定义、赋值、使用,以及如何使用结构体变量进行数据的组织和操作,包括输入、排序、求平均分和查找学生信息等功能。
C语言------结构体和共用体
|
2月前
|
编译器 C语言
【C语言初阶】指针篇—下
【C语言初阶】指针篇—下
|
2月前
|
存储 C语言
【C语言初阶】指针篇—上
【C语言初阶】指针篇—上
|
3月前
|
网络协议 编译器 Linux
结构体(C语言)
结构体(C语言)
|
2月前
|
存储 编译器 定位技术
结构体数组在C语言中的应用与优化策略
结构体数组在C语言中的应用与优化策略
|
2月前
|
存储 编译器 数据库
结构体数组在C语言中的应用与优化技巧
结构体数组在C语言中的应用与优化技巧