Skip to main content

C 结构体

1. 基本概念

  • 在 C 语言中可以使用结构体(structure)来组织多个相同或者不同类型的数据。
  • 结构体可以被声明为变量、指针或者数组,用以实现较复杂的数据结构。
  • 结构体是一些元素的集合,这些元素被称为结构体成员。
  • 结构体成员可以是任何一种的基本数据类型,也可以是另一个结构体。

2. 结构体声明与定义

struct tag { 
member-list
member-list
member-list
...
} variable-list ;
  • tag 是结构体标签。
  • member-list 是标准的变量定义,可以说有效的数据类型定义。
  • variable-list 结构变量,定义在结构体的末尾,最后一个分号之前,可以指定一个或多个变量结构。
  • 在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。
// 此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
// 同时又声明了结构体变量 stu1
// 这个结构体并没有标明其标签
struct
{
int a;
char b;
double c;
} stu1;

//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
int a;
char b;
double c;
};
//用SIMPLE标签的结构体,另外声明了结构体变量 temp1、temp2、temp3
struct SIMPLE temp1, temp2[20], *temp3;

//也可以用typedef创建新类型
typedef struct
{
int a;
char b;
double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;

//此结构体的声明包含了其他的结构体
struct COMPLEX
{
char string[100];
struct SIMPLE a;
};

//此结构体的声明包含了指向自己类型的指针
struct NODE
{
char string[100];
struct NODE *next_node;
};
  • 在第一个声明和第二个声明中即使它们的成员列表是一样的,它们也是不同的结构体类型。
  • 结构体声明只是进行一个框架的描绘,它并不会在内存空间中分配空间存储数据,直到我们去定义一个结构体类型的变量。

3 访问结构体成员

要访问结构体成员,我们需要使用成员访问运算符( . )。成员

stu1.a;  // 访问 stu1 结构体的 a 成员
stu1.b; // 访问 stu1 结构体的 b 成员

4. 定义结构体数组

结构体数组跟数组概念是一样的,只不过每个数组元素不再是简单的基础类型,而是一个结构体类型的数据。

  • 第一种方法是在声明的时候进行定义。
struct 结构体名称
{
结构体成员;
} 数组名[长度];
  • 第二种方法是已经声明了一个结构体类型,在此基础上定义一个结构体数组。
struct 结构体名称
{
结构体成员;
}; // 声明一个结构体

struct 结构体名称 数组名[长度]; // 定义一个结构体数组

5. 定义结构体指针

指向结构体变量的指针称为结构体指针。

struct SIMPLE *pt;

这里声明的就是一个指向 student 结构体类型的指针变量 pt。

我们知道数组名其实是指向这个数组第一个元素的地址,所以我们可以将数组名直接赋值给指针变量。

但结构体变量是不一样的,结构体变量并不是指向该结构体的地址,所以要查找结构体变量的地址,我们需要 &。

struct SIMPLE temp1;  // 先定义结构体变量

pt = &temp1;

通过结构体指针访问结构体成员有两种方法:

  • (*结构体指针).成员名
  • 结构体指针->成员名

第一种方法是因为( . )运算符的优先级比( * )运算符优先级要高,所以要使用小括号先对结构体指针进行解引用,让它先变成结构体变量,再用点运算符去访问成员。

第二种方法( -> )更加方便和直观,但两种方法是等价的,都可以访问到结构体成员。

注意:点号(.)只能用于结构体,而箭头(->)只能用于结构体指针。

6. 示例代码

#include <stdio.h>

struct A
{
int a;
char b;
};

int f(void);

//结构体内部的合法成员
struct member
{
//基本类型
int a;
float b;
char c;

//结构类型
int arr[5];
float brr[5];
char crr[5];
struct{
int a;
int b;
}in_s;

struct A Ac;
union{
int a;
double b;
}in_u;

enum {
ONE, TWO, THREE
}in_e;

//各种指针
int *pi;
float *pf;
char *pc;

int (*piarr)[5]; //数组指针
int (*pfunc)(void); //函数指针
struct A *pA; //其他结构体的指针
// f(); //函数不可以作为结构体成员

//特例
struct member *pmember; //可以定义自己这种类型的指针
// struct member a_s; //不可以当前结构体的类型在成员中定义变量
};

int func(void)
{
printf("%s---[%d]\n", __FUNCTION__, __LINE__);
}

int main(int argc, char *argv[])
{
int i = 100;
float f = 2.45;
char c = 'j';

int aa[5] = {1, 4, 6, 7, 8};

struct member ptmp =
{
.a = 100,
.b = 3.13,
.c = 'A'
};

struct member tmp =
{
.a = 100,
.b = 3.13,
.c = 'A',
.arr = {10, 20, 3, 4, 5},
.brr = {1.2, 3.4, 5.6},
.crr = {'A', 'B', 'c'},
.in_s.a = 20,
.in_s.b = 30,
.Ac.a = 100,
.Ac.b = 'v',
.in_u.b = 4.21,
.in_e = ONE,
// .pi = &tmp.a //可以指向自己内部的成员变量地址
.pi = &i,
.pf = &f,
.pc = &c,
.piarr = &aa,
.pfunc = &func,
.pmember = &ptmp
};

int k;
for(k=0; k<5; k++)
{
printf("%f\t", tmp.brr[k]);
}

for(k=0; k<5; k++)
{
printf("%d\t", (*tmp.piarr)[k]);
}

printf("tmp.in_s.a = %d, tmp.in_s.b = %d\n", tmp.in_s.a, tmp.in_s.b);
printf("*tmp.pi = %d, *tmp.pf = %f, *tmp.pc = %c\n", *tmp.pi, *tmp.pf, *tmp.pc);

tmp.pfunc(); //通过结构体成员来访问函数

printf("tmp.pmember->a = %d,tmp.pmember->b = %f,tmp.pmember->c = %c\n",
tmp.pmember->a, tmp.pmember->b, tmp.pmember->c);

printf("(*tmp.pmember).a = %d,(*tmp.pmember).b = %f,(*tmp.pmember).c = %c\n",
(*tmp.pmember).a, (*tmp.pmember).b, (*tmp.pmember).c);


return 0;
}