结构体内成员对齐规则:1.结构体变量本身就是四字节对齐的位置。2.第一个成员,就从结构体开始的地址处,存放这个元素,具体占多少字节,由紧挨着的下个元素决定。3.整个成员变量自身都对齐了,还没有结束。4.整个结构体还要是默认对齐的最小整数倍。//结构体默认的字节对齐:成员变量最大的那个类型所占字节。
#include <stdio.h>
struct data
{
int a; //4
char b; //1+1
short c;//2
}; //8
typedef struct data1
{
int a; //4+4
double b; //8
}d; //16
struct
{
char ch;
}c; //1
struct
{
char ch[2];
}array; //2
//结构体成员变量的问题
struct data
{
int a;
struct data*p; //64位机器指针永远占8字节
}S;
struct data1
{
int a;
struct data1s; //此时结构体定义不完整
}s;
int main(void)
{
unsignedshort a=0x1234;
unsigned char*p1=(unsigned char *)&a;
int i=8;
//*p1<<i,就产生了临时变量,内存中不知道地址,但是有一个默认类型(int)
printf(“0x%x.\n”,*p1<<i); //0x3400
printf(“0x%x.\n”,*(p1+1));//0x12
}
对齐指令:#pragma pack(n)(n=1,2,3,4···)
#pragma pack()
1、两个配合使用表示一个区间,只有这个区间内的结构体享受这个待遇
2、设置对齐
3、设置为一就是不对齐
作用:1、充分利用内存空间,牺牲了速度,降低了访问效率
2、提高效率、性能,牺牲了内存空间。
#pragma pack(1)
{
int a; //4
char b; //1
double c; //8
}s;(1)->13 (2)->14 (16)->16
总结:指定的对齐方式和结构体自身默认对齐方式,两者取最小
##############################################################################################
1.检查系统错误的宏.一旦发生了,系统错误就会产生一个错误数字(errno),对应相应的错误字符串。
2.标准定义了两个值 EXIT_SUCCESS 和 EXIT_FAILURE,可以作为exit()的参数,来分别指示是否为成功退出。exit(参数)传递给的是父进程,或者shell终端
#define handle_error(msg) do{perror(msg); exit(EXIT_FAILURE);}while(0)
#define PRIN (printf("1.\n");printf("2.\n");)
void func(int a)
{
printf("a= %d.\n", a);
}
#define FUNC printf("所在文件%s所在行数%d.\n", __FILE__,__LINE__)
int main(void)
{
//文件描述符,没有负数
int fd =-1;
int ret =close(fd);
if (-1 ==ret)
{
handle_error("fileerror!");
}
int *p =NULL;
if (NULL== p)
{
handle_error("mallocerror!");
}
int flag= 1;
if (flag)
handle_error("action!"); //printf("1.\n"); printf("2.\n");
else
{
printf("noaction.\n");
}
printf("hello");
//typeof()关键字,专门用于获得类型,()里可以放变量名,或者表达式
FUNC;
func(NUM);
int a =3;
intarr[a] = {};
intarr_sizeof[sizeof(a)] = {};
printf("sizeof(s)= %d.\n", sizeof(s)); // 1字节对齐13
printf("sizeof(s)= %d.\n", sizeof(s)); // 2字节对齐14
printf("sizeof(s1)= %d.\n", sizeof(s1)); // 2字节对齐14
return 0;
}
linux内核里的两个宏:在驱动应用中很广泛。
//off_set_of(type, member)计算结构体内元素的偏移量
//containe_of(ptr, type, member),ptr是结构体里成员的指针,通过调用这个宏计算出结构体的首地址.包含两句代码(表达式),必须要加{}.这两个宏:内核双链表。
#define off_set_of(type, member) ((long)&(((type *)0)->member))
//分析:1、(type *)0指向结构体零地址2、((type *)0)->member得到了结构体某个成元变量名3、给这成员变量名,取地址(相对于零地址),此时&(((type *)0)->member)表示是指针类型4、强制类型转换成(long)。
#include <stdio.h>
#define off_set_of(type,member) ((long)&(((type*)0)->member))
#define get_of(ptr,type,member) \
({typeof(((type *)0)->member) *_mptr=ptr;\
(type *)((char *)_mptr-off_set_of(type,member));})
分析:1、得到结构体成员变量的类型 2、指针赋值(得到真实成员变量的地址值)3、减去偏移量得到一个数字,该数字和结构体本身首地址在数值上一样4、最后强制类型转换为结构体指针类型
struct data
{
char a;//1+1
short b;//2
int c;//4
float d;//4+4
double e;//8
}s={1,2,3,3.14,1.14};
int main(void)
{
intoff=off_set_of(struct data,e);
printf("off=%d.\n",off);
printf("&s=%p\n",&s);
struct data*p=get_of(&s.a,struct data,a);
printf("&s=%p\n",p);
return 0;
}
//typeof()//参数可以是变量名或表达式
例如:
1. typeof(int)p;
p=1;
2.
int *p,a;
typeof(p)p_a=&a;
struct da
{
int a;
short b;
}; //8
struct data
{
char a; //1+1
short b; //2
int c; //4
struct da s; //8
}s={1,3,10,3.14,1.14};
int main(void)
{
struct da*p=container_of(&s.s.b,struct da,b);
}
总结;
1.不会因为有结构体成员而影响你的基本类型决定基本类型
2.里面的结构体对齐方式,已经在结构体外面决定了(遍历完整个结构体)
#include <stdio.h>
位字段:专用于结构体,结构体成员的类型必须是:int || unsigned int
有时侯,结构体成员表示的数据很小,就用几个位来表示。
struct data
{
unsigneda : 1; // 1就是一个bit,范围:0~1
unsignedb : 2; // 2就是er个bit,范围:0~3
unsignedc : 28; // 28位
}s, *p = &s;
//word 32位 就是一个int
struct data1
{
unsigneda : 1; //1就是一个bit,范围:0~1
int : 31; // 无名字段,不可访问,只是占位
unsignedb : 2; //2就是er个bit,范围:0~3
unsignedc : 2; // 28位
}s1;
struct data2
{
unsigneda : 1; //1就是一个bit,范围:0~1
int : 0; // 0字段,不可访问,只是占位 整个字剩下的位,全部写0
unsignedb : 30; // 2就是er个bit,范围:0~3
int : 0;
unsignedc : 2; // 28位
}s2;
//位字段:下一个字段不够在剩余的bit存放时,必须另起一个字。字段不能跨字。
struct data3
{
int a :2;
int b :32;
}s3;
int main(void)
{
s3.a = 2;
printf("s3.a= %d.\n", s3.a);
printf("szieof(s3)= %d.\n", sizeof(s3));
/*
printf("szieof(s)= %d.\n", sizeof(s));
// 超出字段表示的数据范围,结果不可预期
s.b = 2;
printf("s.b= %u.\n", s.b);
// printf("s.a = %u.\n", s.a = 3);
// 字段不可取地址
// unsigned*p_b = &s.b;
*/
return 0;
}
#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>
#if 0 // 网络结构体
/* Internet address. */
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t; // 16的无符号short类型
struct in_addr
{
in_addr_ts_addr;
};
/* Structure describing an Internet socketaddress. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_); /*AF_INET IPv4 Internet protocols 指定网络地址类型*/
in_port_tsin_port; /* Portnumber. 端口号 16位无符号short */
structin_addr sin_addr; /* Internet address. 具体的网址32位无符号int*/
/* Pad tosize of `struct sockaddr'. */
unsignedchar sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
#endif
/*
网络结构体:主流还是ipv4
*/
#define PORT 0x1234
#define ADDR "19.16.1.22"
//把本地ip地址转换成网络字节序地址
//in_addr_t inet_addr(const char *cp);
//输出型参数干的事儿
void set_net_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)
{
structsockaddr_in s;
//赋值
set_net_struct(&s);
printf("网络字节序地址:0x%x.\n", s.sin_addr.s_addr);
#if 0 // 演示网络结构体赋值
/* // 网络结构体填充
set_net_struct(structsockaddr_in *p);
*/
//1、structsockaddr_in定义这种类型的结构体变量
structsockaddr_in s;
//2、填充你这个结构体各成员
s.sin_family= AF_INET;
// s.sin_ =AF_INET; (错误)
//但是已经错了,你的发送数据包,肯定找不到对方。
//传送过去的IP/PORT都是网络字节序,大端模式:低位(字节)要放到高地址。
s.sin_port= htons(PORT);
// printf("s.sin_port= 0x%x.\n", s.sin_port);
s.sin_addr.s_addr= inet_addr(ADDR);
//0x16011013
// printf("网络字节序地址:0x%x.\n", s.sin_addr.s_addr);
#endif
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
柔性数组:是在结构体里,有一个数组,必须是结构体最后的一个元素,而且由特定形式[]或[0];
整体的结构体至少有两个成员变量。
*/
//不好之处:1、p(字符串)压根就没用,2、每次要用字符串时,很麻烦(char *)(p+1)。
typedef struct data
{
int a;
char*p; // 结构体里存储的字符串, 和你结构体时分离的。
}D;
//柔性数组,最后一个数组name[],不占空间,只是一个符号(常量地址)
typedef struct
{
int len;
intarr[];
}S;
// 看一下,遍历
void show_infor(S *p)
{
int i =0;
// printf("p->arr[%d]= %d.\n", p->len-1, p->arr[p->len-1]);
//遍历所有元素
for (i=0;i<p->len; i++)
{
printf("p->arr[%d]= %d.\n", i, p->arr[i]);
}
}
//生成柔性数组
void create_soft_arr(int len)
{
S *p = (S*)malloc(sizeof(S) + sizeof(int)*len);
//让a成员控制我数组个数
p->len= len;
int i =0;
for (i=0;i<len; i++)
{
if(i <= 1)
p->arr[i]= 1;
elseif (i >= 2)
p->arr[i]= p->arr[i-1] + p->arr[i-2];
}
//看一下 用func
show_infor(p);
free(p);
}
int main(void)
{
create_soft_arr(10);
#if 0
int len =10;
printf("sizeof(S)= %d.\n", sizeof(S)); // 4
//动态数组生成
//.符号:结构体变量访问各成员.
S *p = (S*)malloc(sizeof(S) + sizeof(int)*len);
//判断空间申请情况,和清0操作,省略
//数组赋值
int i =0;
for (i=0;i<len; i++)
{
//p->arr得到的是数组名
p->arr[i]= i+1;
// (*p).arr[i]= i+1;
}
//看一下
for (i=0;i<len; i++)
{
printf("p->arr[%d]= %d.\n", i, p->arr[i]);
}
/*
int *p_a= (int *)malloc(10);
charbuf[] = "2017_12_24";
//把字符串放到我的结构体里
D *p = (D*)malloc(sizeof(D) + strlen(buf) + 1);
printf("sizeof(structdata) = %d.\n", sizeof(struct data));
printf("(char*)(p+1) = [%s].\n", (char *)(p+1));
strcpy((char*)(p+1), buf);
printf("(char*)(p+1) = [%s].\n", (char *)(p+1));
*/
#endif
return 0;
}
#include <stdio.h>
typedef enum STATE
{
STATE1,
STATE2,
STATE3,
STATE4,
}S;
int main(void)
{
int num;
Scurrent_state=STATE1;
printf("请输入秘密\n");
int i=4;
while(i--)
{
scanf("%d",&num);
printf("num=%d\n",num);
switch(current_state)
{
caseSTATE1:
if(num==4)
{
current_state=STATE2;
}
else
{
current_state=STATE1;
}
break;
caseSTATE2:
if(num==3)
{
current_state=STATE3;
}
else
{
current_state=STATE1;
}
break;
caseSTATE3:
if(num==4)
{
current_state=STATE4;
}
else
{
current_state=STATE1;
}
break;
caseSTATE4:
if(num==2)
{
current_state=STATE4;
}
else
{
current_state=STATE1;
}
break;
default:
current_state=STATE1;
break;
}
}
if(current_state==STATE4)
{
printf("密码正确\n");
}
else
printf("密码错误\n");
}