C基本知识

C基本数据类型

C基本的数据类型说明:

C declaration Bytes
Signed Unsigned 32-bits 64-bits
[signed]char unsigned char 1 1
short unsigned short 2 2
int unsigned 4 4
long unsigned long 4 8
int32_t uint32_t 4 4
int64_t uint64_t 8 8
char * 4 4
float 4 4
double 8 8

字节序

字节序_1

测试代码:

#include <stdio.h>

typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, int len)
{
    int i;

    for (i = 0; i < len; i++) {
        printf(" %.2x", start[i]);
    }
    printf("\n");
}

int main(void)
{
    int x = 0x01234567;

    show_bytes((byte_pointer)&x, sizeof(x));

    return 0;
}

编译运行测试:

$ gcc endian.c
$ ./a.out
 67 45 23 01

可以看出,当前我运行的机器是小端序。

结构体

参考:C语言技术天花板【结构体】一节课掌握_哔哩哔哩_bilibili

结构体中成员排列的规则:

  1. 结构体成员的内部偏移量(内部地址),要被这个成员的数据类型的大小整除。

    struct test {
        int   a; /* [Byte][Byte][Byte][Byte] */
        char  c; /* [Byte][0x00] */
        short s; /* [Byte][Byte] */
    };
    
    /* 结构体的内存大小 */
    sizeof(struct test) = 8;
    
    /* 结构体中的内存分布,可看出成员c后面填充了1Byte的0x00 */
    struct test test1 = {0};
    test1.a = 0x12345678;
    test1.c = 0xbb;
    test1.s = 0xccdd;
    
    (gdb) x /8bx &test1
    0x7fffffffe110: 0x78    0x56    0x34    0x12    0xbb    0x00    0xdd    0xcc
    
  2. 整个结构体的大小,必须是最大成员的size的整数倍,否则就需要在末尾填充空白字节。

    struct test {
        int   a; /* [Byte][Byte][Byte][Byte] */
        char  c; /* [Byte] */
    };
    
    /* 结构体的内存大小 */
    sizeof(struct test) = 8;
    
    /* 结构体中的内存分布,可看出成员c后面填充了3Byte的0x00 */
    (gdb) x /8bx &test1
    0x7fffffffe110: 0x78    0x56    0x34    0x12    0xbb    0x00    0x00    0x00
    
  3. 对于结构体中的结构体,按照结构体展开之后的内存对齐来处理。

    struct test1 {
        int a;   /* [Byte][Byte][Byte][Byte] */
        short s; /* [Byte][Byte][0x00][0x00] */
    };
    
    struct test {
        int   a; /* [Byte][Byte][Byte][Byte] */
        char  c; /* [Byte][0x00][0x00][0x00]*/
        struct test1 s;
    };
    
    /* 结构体的内存大小为16,根据规则1,成员c后面补充3Bytes的0x0,根据规则2,s.s成员后面补充2Bytes的0x0 */
    sizeof(struct test) = 16;
    
    /* 结构体中的内存分布 */
    struct test test1 = {0};
    test1.a = 0x12345678;
    test1.c = 0xff;
    test1.s.a = 0xaabbccdd;
    test1.s.s = 0x9988;
    (gdb) x /16bx &test1
    0x7fffffffe100: 0x78    0x56    0x34    0x12    0xff    0x00    0x00    0x00
    0x7fffffffe108: 0xdd    0xcc    0xbb    0xaa    0x88    0x99    0x00    0x00
    
  4. 认为指定特殊的对齐规则,使用#pragma pack(n)指定每个成员的其实地址,按照n来对齐,覆盖第一条规则。如果整个n比第一条规则对齐还要大,那么就取小的。

    #pragma pack(2)
    struct test1 {
        int a;   /* [Byte][Byte][Byte][Byte] */
        short s; /* [Byte][Byte] */
    };
    
    struct test {
        int   a; /* [Byte][Byte][Byte][Byte] */
        char  c; /* [Byte][0x00] */
        struct test1 s;
    };
    
    /* 结构体的内存大小为12,根据规则4,成员c后面补充1Bytes的0x0 */
    sizeof(struct test) = 12;
    
    /* 结构体中的内存分布 */
    struct test test1 = {0};
    test1.a = 0x12345678;
    test1.c = 0xff;
    test1.s.a = 0xaabbccdd;
    test1.s.s = 0x9988;
    (gdb) x /12bx &test1
    0x7fffffffe110: 0x78    0x56    0x34    0x12    0xff    0x00    0xdd    0xcc
    0x7fffffffe118: 0xbb    0xaa    0x88    0x99
    
    #pragma pack(8)
    struct test1 {
        int a;   /* [Byte][Byte][Byte][Byte] */
        short s; /* [Byte][Byte] */
    };
    
    struct test {
        int   a; /* [Byte][Byte][Byte][Byte] */
        char  c; /* [Byte][0x00] */
        struct test1 s;
    };
    
    /* 规则1生效 */
    sizeof(struct test) = 16;
    
  5. 不使用任何对齐,直接存放数据的方式,使用#pragma pack(1)

    #pragma pack(1)
    struct test1 {
        int a;   /* [Byte][Byte][Byte][Byte] */
        short s; /* [Byte][Byte] */
    };
    
    struct test {
        int   a; /* [Byte][Byte][Byte][Byte] */
        char  c; /* [Byte] */
        struct test1 s;
    };
    
    /* 结构体的内存大小为11,各个成员之间不会填充0x0 */
    sizeof(struct test) = 11;
    
    /* 结构体中的内存分布 */
    struct test test1 = {0};
    test1.a = 0x12345678;
    test1.c = 0xff;
    test1.s.a = 0xaabbccdd;
    test1.s.s = 0x9988;
    (gdb) x /11bx &test1
    0x7fffffffe110: 0x78    0x56    0x34    0x12    0xff    0xdd    0xcc    0xbb
    0x7fffffffe118: 0xaa    0x88    0x99
    

一个有趣的函数offsetof,可以用来计算结构体成员在结构体中的偏移位置,比如:

参考:Yanyan's Wiki (jyywiki.cn)

#include <stdio.h>
#include <stddef.h>

struct foo {
    char common[64];

    // This is anonymous.
    union {
        struct {
            char x;
            int y;
            int z;
        };
        struct {
            char u;
            char v;
            char w[0];
        };
    };
};

#define display_offset(member) \
    printf("foo." #member " is at %d\n", \
        offsetof(struct foo, member) \
    )

int main() {
    display_offset(common);
    display_offset(x);
    display_offset(y);
    display_offset(z);
    display_offset(u);
    display_offset(v);
    display_offset(w);
}

运行结果:

$ ./offsetof 
foo.common is at 0
foo.x is at 64
foo.y is at 68
foo.z is at 72
foo.u is at 64
foo.v is at 65
foo.w is at 66
posted @ 2022-07-31 21:39  Mrlayfolk  阅读(96)  评论(0)    收藏  举报
回到顶部