C语言单元小结(6)

本文详细介绍了C语言中的结构体,包括字节对齐规则、结构体嵌套、对齐指令#pragma pack的应用、位字段的使用限制、网络结构体的定义以及在函数中的应用。同时,还探讨了宏的常见用法,如__FILE__、__LINE__等,并提到了枚举在定义常量符号中的作用。

结构体

1.字节对齐

知识点:

对齐规则:1、我们的结构体变量在4字节对齐的位置。
2、第一个成员,从结构体开始的地址处存放。具体占多少字节,由紧挨着下个元素决定。
3、整个结构体是默认字节对齐的最小整数倍。
//结构体默认的字节对齐:成员变量最大的那个类型所占字节

举例:
#include<stdio.h>
struct data
{
    char a;//填充1
    short b;//填充4
    double c;
}A;
sizeof(A)=16 byte
注意:

结构体不允许此类定义

struct data
{
        int a;
        struct data s;   // 此时结构体类型(定义)不完整 
}s;

2.结构体嵌套

知识点:

1、不会因为有结构体成员,而影响你的基本类型,只决定默认对齐方式
2、里面的结构体对齐方式,已经在外面决定了(遍历整个完整结构体)

举例:
struct data1
{
        int a;
        short b;
        int c;
        double e;
};//24

struct data
{
        char a;//2
        short b;//2
        int c;//4
        struct data1 s;//24 
        char ch;//8
}//40

3.对齐指令

知识点:

#pragma pack(n) (1、2、4、8、…..)
#pragma pack()
表示一个区间,区间内的结构体有此设置
指定的对齐方式和结构体自身默认对齐方式取最小的。

作用:

1、充分利用内存空间,牺牲了速度,降低了访问效率。
2、提高效率、性能,牺牲了内存空间。

举例:
#include <stdio.h>
#pragma pack(2)
struct
{
    int a;
    char b;
}A;
#pragma pack()
sizeof(A)=6 byte

4.位字段

知识点:

1.专用于结构体,结构体成员的类型必须是:int || unsigned int
2.字段不可取地址
3.0字段,不可访问,只是占位整个字中剩下的位,全部写0
4.无名字段,不可访问,只是占位
5.字(word)=32bit
6.下一个字段不够在剩余的字存放时,必须另起一个字。字段不能跨字。
7.超出字段表示的数据范围,结果不可预期
8.若最后一个字没有填满则也将填满后返回

举例:
//写法
struct data
{
    unsigned a : 1;  // 1就是一个bit,范围:0~1
    int :0;     // 0字段,不可访问,只是占位  整个字剩下的位,全部写0
    unsigned b : 2;  // 2就是两个bit,范围:0~3
    int:31;     // 无名字段,不可访问,只是占位
}A;

sizeof(A)=3 word=12 byte
注意:
struct
{
    int a:1;//此时a只有一位且仅为符号位,取值范围为(-1~0)
};

5.网络结构体

知识点:

头文件路径:/usr/include/netinet/in.h

实例:

头文件内容:

/* Internet address.  */
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;    // 16的无符号short类型 
struct in_addr
  {
    in_addr_t s_addr;
  };

/* Structure describing an Internet socket address.  */
struct sockaddr_in
  {
    __SOCKADDR_COMMON (sin_);               /* AF_INET IPv4 Internet protocols  指定网络地址类型*/ 
    in_port_t sin_port;                         /* Port number. 端口号 16位无符号short */
    struct in_addr sin_addr;            /* Internet address.  具体的网址32位无符号int*/

    /* Pad to size of `struct sockaddr'.  */
    unsigned char sin_zero[sizeof (struct sockaddr) -
               __SOCKADDR_COMMON_SIZE -
               sizeof (in_port_t) -
               sizeof (struct in_addr)];
  };

利用以上内容定义sockaddr_in结构体并赋值

#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.254.3"

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

6.结构体与函数

知识点:

结构体中不能调用函数,需要声明函数指针

实现:
#include <stdio.h>
typedef int (*p_func)(int a,int b);//重命名函数类型为函数指针

int add(int a,int b)//函数本体,实现具体功能
{
        return a+b;
}

struct data
{
        int a;
        int b;
        p_func 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);
}

int main(void)
{
        struct data s;
        set_func(&s);

        int ret=ret_func(&s);
        printf("%d\n",ret);

        return 0;
}

7.柔性数组

知识点

标志是结构体的最后一个成员是一个没有定义大小的数组
需要malloc动态申请数组大小后使用

实现

1.int型数组

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
        int len;
        int arr[];
}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;
}

void set_arr(S* p)
{
        int i=0;
        for(i=0;i<(p->len);i++)
        {
                if(i<=1)
                        p->arr[i]=1;
                else if(i>=2)
                        p->arr[i]=p->arr[i-1]+p->arr[i-2];
        }
}

void show_arr(S* p)
{
        int i=0;
        for(i=0;i<(p->len);i++)
        {
                printf("%d ",p->arr[i]);
        }
}

int main(void)
{
        S* p = create_soft_arr(5);
        set_arr(p);
        show_arr(p);

        return 0;
}

2.char型数组

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct
{
        int len;
        char arr[];
}S;

int main(void)
{
        char a[]="abcd";
        S* p=(S*)malloc(sizeof(S)+sizeof(a));
        strcpy(p->arr,a);

        printf("%s",p->arr);

        return 0;
}

举例

1.
__FILE__:所在文件
__LINE__:所在行数

2.
#define handle_error(msg) (do{perror(msg); exit(EXIT_FAILURE);}while(0))
解释:
perror:检查系统错误的宏.一旦发生了,系统错误就会产生一个错误数字(errno),对应相应的错误字符串。
EXIT_SUCCESS 和 EXIT_FAILURE:C 标准定义了两个值,可以作为exit()的参数,来分别指示是否为成功退出。
exit():传递给的是父进程,或者shell终端

3.linux内核里的两个宏:在驱动应用中很广泛
off_set_of(type, member)计算结构体内元素的偏移量
containe_of(ptr, type, member)结构体的首地址,需传入该结构体的成员地址
原理:

#define off_set_of(type, member)    ((long)&((type*)0->member))

将0转换为结构体类型的指针,再指向其成员,将地址转换为数值接收即为偏移量

#define container_of(ptr, type, member) ({typeof ((type*)0->member) *mystr = ptr ; (type *)((long)mystr-off_set_of(type,member));})

将0转换为结构体类型指针,再指向其成员,调用typeof函数得到成员类型并申请一个该类型的指针,赋值为传入的成员地址str;该地址减去偏移量即结构体的首地址

补充:
typeof(),得到参数的类型。参数可以是变量名或者表达式。

int *p, a;
//变量名作参数
typeof(p) p_a = &a;
//表达式作参数
typeof(*p_a) b = 23;

枚举

知识点:

枚举:定义常量符号,就是宏定义常数的集合体。如四季,星期,意义相关的常数

举例:

实现密码锁,循环输入至出现正确密码

#include <stdio.h>

typedef enum//书写格式
{
        STATE1,//以逗号结尾
        STATE2,
        STATE3,
        STATE4,
}S;

int main(void)
{
        S c_state = STATE1;//声明一个变量的格式,以枚举值赋值
        printf("Input key:");
        int num=0;

        while(1)
        {
                scanf("%d",&num);
                switch(c_state)
                {
                        case STATE1:
                        if(num == 2)
                               c_state=STATE2;
                        else
                                c_state=STATE1;
                        break;
                        case STATE2:
                        if(num == 2)
                                c_state=STATE3;
                        else
                                c_state=STATE1;
                        break;
                        case STATE3:
                        if(num == 1)
                                c_state=STATE4;
                        else
                                c_state=STATE1;
                        break;
                        default:
                                c_state=STATE1;
                                break;
                }
                if(c_state==STATE4)
                {
                        printf("successful\n");
                        return 0;
                }
        }

        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值