(一).结构体是一种自定义类型
1.结构体是一种类型
2.结构体是用户自定义的类型
原因:
1.系统提供的数据类型均是单一的数据类型
2.现在的数据类型无法复合存储多个数据
(二).结构体的定义
1.结构体类型是一个复合类型;
2.结构体类型由成员构成
3.定义
struct 结构体成员名 类型名
{
数据类型1 成员1
数据类型2 成员2
。。。
}
例:
struct node
{
int x;
int y;
char ch;
}
(1)行为:定义了一种类型
类型名称: struct node
大小: 不仅是各个成员变量之和,还要字节对齐
(2)结构体类型成员
①表示一个结构体由哪些成员组成
②系统不为每个成员分配单元
意义:struct node 这种数据类型有三个成员组成
③一个结构体类型的定义即该结构体类型成员的定义
(三).结构体类型变量
1.系统不会为结构体类型分配存储单元
(1)只有用户先定义的数据类型,然后通过结构体类型定义变量
(2)系统会按照结构体大小为结构体类型变量分配单元
2.结构体类型变量的定义
struct 结构体名 变量名;
(结构体类型名)
例:
struct node
{
int x;
float y;
char c;
};
//通过结构体类型定义变量
struct node s;
(1)系统通过struct node类型为变量s分配单元
(2)变量s由三个成员构成
(3)结构体变量成员的引用
结构体变量名.成员名
① . 成员运算符(双目)
优先级最高
②功能:引用一个结构体变量单元的分量子单元
struct node s, t;
s: 中有一组{int x; float y; char c;}
t:中有一组{int x; float y; char c;}
注意:s.x与t.x没有一毛钱关系
分别给变量s的x, y, c 分量成员赋值
s.x = 15;
s.y = 3.1;
s.c = 'j';
将结构体变量s各个分量成员的值存入变量单元
方法1:
s.x = t.x;
s.y = t.y;
s.z = t.z;
方法2:
t = s;
两个同类型的结构体变量单元之间可直接赋值
3.结构体数组的定义
struct 结构体类型名 数组名[个数];
例:
struct node s[3];
s[0].x = 12;
s[0].y = 1.3;
s[0].c = '4';
通过键盘给s[2]的成员赋值
scanf("%d",&s[2].x);
scanf("%f",&s[2].y);
scanf("%c",&s[2].c);
(四)类型重命名
1.对已存在的类型名进行重命名:
typedef 原类型名 重命名
例:
typedef int elemint;
2.结构体的重命名:
1)先定义结构体类型再重命名
struct node
{
int x;
float y;
char c;
};
typedef struct node elems; //重命名
struct node s;//定义结构体变量s
elems t;//定义结构体变量t
2)定义结构体类型同时重命名
(1)方法1:
typedef struct node
{
int x;
float y;
char c;
}elems;
(2)方法2:
typedef struct
{
int x;
float y;
char c;
}elems;
(五)结构体的存储方式(字节对齐)
1.对于char类型数据自身的对齐值为1字节,对于short型为2字节,int、float类型为4字节,double、long类型为8字节。
2.结构体自身对齐值(默认):其所有基本成员中那个对齐值最大的值。
3.指定对齐值:#pragma pack (vale),其中对齐值为vale。
4.有效对齐值:自身对齐值和指定对齐值中小的那个值。
5.当结构体中含有空数组时,空数组所占字节数为0.
(六)柔性数组
1.柔性数组的标志是结构体的最后一个成员是一个没有定义大小的数组,需要malloc动态申请数组大小后使用(需要加入头文件#include <stdlib.h>)
(1)int型数组
typedef struct
{
int len; //常用来存储数组长度
int arr[];//数组名为结构体末尾地址,数组所占字节数为0
}S;
S* create_soft_arr(int len)
{
S* p=(S*)malloc(sizeof(S)+sizeof(int)*len);
if(NULL==p)
printf("malloc error");
p->len=len;
return p;
}
(2)char型数组(一般用于操作字符串,加入头文件#include <string.h>)
typedef struct
{
int len;
char arr[];
}S;
int main(void)
{
char a[]="1234664987";
S* p=(S*)malloc(sizeof(S)+sizeof(a));
strcpy(p->arr,a);
printf("%s",p->arr);
return 0;
}
2.柔性数组其实就是对“空数组所占空间为零”和“指针可以越界”的一种综合应用。
(七)结构体与函数
typedef int (*pfun)(int a, int b);//重命名函数类型为函数指针
int add(int a, int b)
{
return a + b;
}
struct data
{
int a;
int b;
pfun p;//函数指针(由于结构体中不能调用函数,需要声明函数指针)
}s;
void set_func (struct data *s)//结构体赋值
{
s->a=1;
s->b=2;
s->p=add; //结构体调用函数
}
int ret_func (struct data *s)//使用,在主函数调用该函数即可
{
return s->p(s->a,s->b);
}
(八)网络结构体
#include <stdio.h>
#include <features.h>
#include <stdint.h>
#include <sys/socket.h>
#include <bits/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 1234
#define ADDR "172.25.10.10"
void set_struct (struct sockaddr_in *p)
{
p->sin_family = AF_INET;
p->sin_port = htons(PORT);
p->sin_addr.s_addr = inet_addr(ADDR);
}
int main(void)
{
struct sockaddr_in s;
set_struct(&s);
return 0;
}
(九)枚举
1.定义常量符号,就是宏定义常数的集合体。如四季,星期,意义相关的常数
例子:
typedef enum
{
STATE1,//用逗号分隔
STATE2,
STATE3,
STATE4,
STATE5,
}S;
void main(void)
{
S c_state = STATE1;
}
注意:枚举类型定义的变量,只能赋已经定义过的值
(十)linux内核里的两个宏:在驱动应用中很广泛
1.off_set_of(type, member) 计算元素在结构体内的偏移量
偏移量 = 当前成员的地址 - 成员所在结构体的首地址
如果结构体的首地址为0时
偏移量 = 当前成员的地址
宏定义为:#define off_set_of(type, member) ((long)&((type*)0->member))
2.containe_of(ptr, type, member) 计算成员变量所在结构体的首地址,
成员所在结构体的首地址 = 当前成员的地址 - 偏移量
注意:由于地址的加减与地址类型有关,所以要进行强制类型转换
宏定义为:#define container_of(ptr, type, member) ({typeof ((type*)0->member) *mystr = ptr ; (type *)((char *)mystr-off_set_of(type,member));})
1.结构体是一种类型
2.结构体是用户自定义的类型
原因:
1.系统提供的数据类型均是单一的数据类型
2.现在的数据类型无法复合存储多个数据
(二).结构体的定义
1.结构体类型是一个复合类型;
2.结构体类型由成员构成
3.定义
struct 结构体成员名 类型名
{
数据类型1 成员1
数据类型2 成员2
。。。
}
例:
struct node
{
int x;
int y;
char ch;
}
(1)行为:定义了一种类型
类型名称: struct node
大小: 不仅是各个成员变量之和,还要字节对齐
(2)结构体类型成员
①表示一个结构体由哪些成员组成
②系统不为每个成员分配单元
意义:struct node 这种数据类型有三个成员组成
③一个结构体类型的定义即该结构体类型成员的定义
(三).结构体类型变量
1.系统不会为结构体类型分配存储单元
(1)只有用户先定义的数据类型,然后通过结构体类型定义变量
(2)系统会按照结构体大小为结构体类型变量分配单元
2.结构体类型变量的定义
struct 结构体名 变量名;
(结构体类型名)
例:
struct node
{
int x;
float y;
char c;
};
//通过结构体类型定义变量
struct node s;
(1)系统通过struct node类型为变量s分配单元
(2)变量s由三个成员构成
(3)结构体变量成员的引用
结构体变量名.成员名
① . 成员运算符(双目)
优先级最高
②功能:引用一个结构体变量单元的分量子单元
struct node s, t;
s: 中有一组{int x; float y; char c;}
t:中有一组{int x; float y; char c;}
注意:s.x与t.x没有一毛钱关系
分别给变量s的x, y, c 分量成员赋值
s.x = 15;
s.y = 3.1;
s.c = 'j';
将结构体变量s各个分量成员的值存入变量单元
方法1:
s.x = t.x;
s.y = t.y;
s.z = t.z;
方法2:
t = s;
两个同类型的结构体变量单元之间可直接赋值
3.结构体数组的定义
struct 结构体类型名 数组名[个数];
例:
struct node s[3];
s[0].x = 12;
s[0].y = 1.3;
s[0].c = '4';
通过键盘给s[2]的成员赋值
scanf("%d",&s[2].x);
scanf("%f",&s[2].y);
scanf("%c",&s[2].c);
(四)类型重命名
1.对已存在的类型名进行重命名:
typedef 原类型名 重命名
例:
typedef int elemint;
2.结构体的重命名:
1)先定义结构体类型再重命名
struct node
{
int x;
float y;
char c;
};
typedef struct node elems; //重命名
struct node s;//定义结构体变量s
elems t;//定义结构体变量t
2)定义结构体类型同时重命名
(1)方法1:
typedef struct node
{
int x;
float y;
char c;
}elems;
(2)方法2:
typedef struct
{
int x;
float y;
char c;
}elems;
(五)结构体的存储方式(字节对齐)
1.对于char类型数据自身的对齐值为1字节,对于short型为2字节,int、float类型为4字节,double、long类型为8字节。
2.结构体自身对齐值(默认):其所有基本成员中那个对齐值最大的值。
3.指定对齐值:#pragma pack (vale),其中对齐值为vale。
4.有效对齐值:自身对齐值和指定对齐值中小的那个值。
5.当结构体中含有空数组时,空数组所占字节数为0.
(六)柔性数组
1.柔性数组的标志是结构体的最后一个成员是一个没有定义大小的数组,需要malloc动态申请数组大小后使用(需要加入头文件#include <stdlib.h>)
(1)int型数组
typedef struct
{
int len; //常用来存储数组长度
int arr[];//数组名为结构体末尾地址,数组所占字节数为0
}S;
S* create_soft_arr(int len)
{
S* p=(S*)malloc(sizeof(S)+sizeof(int)*len);
if(NULL==p)
printf("malloc error");
p->len=len;
return p;
}
(2)char型数组(一般用于操作字符串,加入头文件#include <string.h>)
typedef struct
{
int len;
char arr[];
}S;
int main(void)
{
char a[]="1234664987";
S* p=(S*)malloc(sizeof(S)+sizeof(a));
strcpy(p->arr,a);
printf("%s",p->arr);
return 0;
}
2.柔性数组其实就是对“空数组所占空间为零”和“指针可以越界”的一种综合应用。
(七)结构体与函数
typedef int (*pfun)(int a, int b);//重命名函数类型为函数指针
int add(int a, int b)
{
return a + b;
}
struct data
{
int a;
int b;
pfun p;//函数指针(由于结构体中不能调用函数,需要声明函数指针)
}s;
void set_func (struct data *s)//结构体赋值
{
s->a=1;
s->b=2;
s->p=add; //结构体调用函数
}
int ret_func (struct data *s)//使用,在主函数调用该函数即可
{
return s->p(s->a,s->b);
}
(八)网络结构体
#include <stdio.h>
#include <features.h>
#include <stdint.h>
#include <sys/socket.h>
#include <bits/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 1234
#define ADDR "172.25.10.10"
void set_struct (struct sockaddr_in *p)
{
p->sin_family = AF_INET;
p->sin_port = htons(PORT);
p->sin_addr.s_addr = inet_addr(ADDR);
}
int main(void)
{
struct sockaddr_in s;
set_struct(&s);
return 0;
}
(九)枚举
1.定义常量符号,就是宏定义常数的集合体。如四季,星期,意义相关的常数
例子:
typedef enum
{
STATE1,//用逗号分隔
STATE2,
STATE3,
STATE4,
STATE5,
}S;
void main(void)
{
S c_state = STATE1;
}
注意:枚举类型定义的变量,只能赋已经定义过的值
(十)linux内核里的两个宏:在驱动应用中很广泛
1.off_set_of(type, member) 计算元素在结构体内的偏移量
偏移量 = 当前成员的地址 - 成员所在结构体的首地址
如果结构体的首地址为0时
偏移量 = 当前成员的地址
宏定义为:#define off_set_of(type, member) ((long)&((type*)0->member))
2.containe_of(ptr, type, member) 计算成员变量所在结构体的首地址,
成员所在结构体的首地址 = 当前成员的地址 - 偏移量
注意:由于地址的加减与地址类型有关,所以要进行强制类型转换
宏定义为:#define container_of(ptr, type, member) ({typeof ((type*)0->member) *mystr = ptr ; (type *)((char *)mystr-off_set_of(type,member));})