C语言:结构体

本文最后更新于:2023年3月21日 晚上

定义

1
2
3
4
5
6
struct point{
int x;
int y;
}p1,p2;

struct point p3;
  • struct 后跟的point称为结构标记,这种标识符在C语言中称为Tag
  • struct声明定义了一种数据类型,可以将自定义的标识符声明为指定类型的变量,这里p1,p2都是point类型
  • 如果在定义结构体类型的同时定义了变量,也可以不必写Tag,但这样就没有办法再次引用这个结构体类型(没有tag)

成员访问与引用

通过.进行结构体的引用

  • struct point maxpt = {320, 200};,那么maxpt.x可以访问maxpt的x成员,若有结构体内部的嵌套关系,也可以使用.访问被嵌套的成员
    通过->进行引用
  • struct point origin,*pp; 将 pp 定义为一个指向 struct point 类型对象的指针
  • pp = &origin,该指针指向origin结构体,然后就可以用(*pp).x, (*pp).y来访问结构体的变量
  • (*pp).x 中的圆括号是必需的,因为结构成员运算符.的优先级比*的优先级高。表达式*pp.x 的含义等价于*(pp.x),因为 x 不是指针,所以该表达式是非法的

C 语言提供了另一种简写方式,假定 p 是一个指向结构的指针,可以用p->结构成员这种形式引用相应的结构成员

假设有一个结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct list_ele {
/**
* @brief Pointer to a char array containing a string value.
*
* The memory for this string should be explicitly allocated and freed
* whenever an element is inserted and removed from the queue.
*/
char *value;

/**
* @brief Pointer to the next element in the linked list.
*/
struct list_ele *next;
} list_ele_t;
  • list_ele_t *newh = (list_ele_t *)malloc(sizeof(list_ele_t));分配了结构体的空间。但是这样不会给value分配空间

  • newh->value = (char *)malloc(sizeof(char) * (strlen(s) + 1));给结构体内的value分配了空间,这样之后才可以对value进行操作

赋值

  • 结构体类型之间用赋值运算符是允许的
  • 用一个结构体初始化另一个结构体也是允许的
  • 将结构体作为函数参数、函数返回值是允许的

一个函数调用分析

1
2
3
4
5
6
7
8
9
10
struct complex_struct add_complex(struct complex_struct z1, struct
complex_struct z2)
{
z1.x = z1.x + z2.x;
z1.y = z1.y + z2.y;
return z1;
}

struct complex_struct z = { 3.0, 4.0 };
z = add_complex(z, z);

变量z在main函数的栈帧中,参数z1和z2在add_complex函数的栈帧中,z的值分别赋给z1和z2

如果结构体中有char类型的数组,可以使用strcpy函数进行赋值

类型定义

typedef可以建立新的数据类型名,例如,typedef int Length;将 Length 定义为与 int 具有同等意义的名字

比较复杂的类型定义:

1
typedef int (*PFI)(char *, char *);

该语句定义了类型 PFI 是“一个指向函数的指针,该函数具有两个 char *类型的参数,返回值类型为 int”

typedef有助于分析函数声明,比如下面的声明,可以暴力分析:

1
void (*signal(int sig, void (*func)(int)))(int);
  • 简化声明
    • void (*signal(int sig, func))(int);
    • void (*)(int);
  • signal 是一个函数
    • 有两个参数,一个为int类型,一个为指向函数的指针
    • 返回值为指向函数的指针(参数为int,返回值为void)

也可以这样分析:

1
2
typedef void (*sighandler_t)(int); 
sighandler_t signal(int signum, sighandler_t handler);

结构体排序的几种方法

通过重载 < 操作符

1
2
3
4
5
6
7
8
9
struct node
{
int math;
int chinese;
bool operator < (const node &a) const
{
return math<a.math;//math从小到大进行排序
}
};

自定义sort的cmp函数

对于一个结构体而言,有多个关键字,在使用sort的时候可以针对不同关键字进行排序
比如下面这个结构体node存储数学和语文成绩

1
2
3
4
5
struct node
{
int math;
int chinese;
}node[N];

仅仅以math为关键字进行排序

1
2
3
4
5
boo cmp(node a,node b)
{
return a.math<b.math;//按照数学成绩从小到大进行排序
}
sort(a,a+n,cmp);

优先以math为关键字排序,当math相等时,以Chinese为关键字排序

1
2
3
4
5
6
boo cmp(node a,node b)
{
if(a.math!=b.math) return a.math<b.math;
if(a.chinese!=b.chinese) return a.chinese<b.chinese;
}
sort(a,a+n,cmp);

结构体对齐

  • 原则一:数据成员对齐规则:
    • 结构的数据成员,第一个数据成员放在offset为0的地方
    • 以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)
  • 原则二:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐
  • 原则三:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储
    • struct a里存有struct b,b里有char,int, double等元素, 那b应该从8的整数倍开始存储.
  • 原则四:对齐参数如果比结构体成员的sizeof值小,该成员的偏移量应该以此值为准。也就是说,结构体成员的偏移量应该取二者的最小值
    • 对齐参数就是#pragma pack(num)中的num值

image-20230313175216749


C语言:结构体
http://gls.show/p/8cf2e09a/
作者
郭佳明
发布于
2022年9月4日
许可协议