C语言---自定义类型:结构体(3)

简介: C语言---自定义类型:结构体

C语言---自定义类型:结构体(2)https://developer.aliyun.com/article/1544451

那么为什么会存在内存对齐的问题呢

  1. 平台原因 (移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定

类型的数据,否则抛出硬件异常。

  1. 性能原因:

数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

内存对齐可以提升读取的效率

所以内存对齐还是很有必要的

那么在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到?

让占用空间小的成员尽量集中在一起

//那我们只能眼睁睁的看着字节被浪费吗?
/*
我们是否有办法使内存少浪费一些
 
那么在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到?
 
让占用空间小的成员尽量集中在一起
 
 
*/
/*这个时候我们就可以拿出之前的S1和S2了
struct S1
 {
    char c1;
    int i;
    char c2;
 };
 
 struct S2
 {
    char c1;//对齐数是1
    char c2;//对齐数是1
    int i;
 };
 这两个类型成员是一样的,只是内部成员顺序不同
 S1占12个字节
 S2占8个字节
 
 不难发现,我们将占用空间较小的成员集中放在一起,
 原本要浪费的空间就被利用起来了
 
 
*/

修改默认对齐数

pragma这个预处理指令,可以改变编译器的默认对齐数

//vs上默认对齐数是8
 
#pragma pack(1)//将默认对齐数设置为1
struct S
{
    char c1;
    int n;
    char c2;
 
};
#pragma pack()//恢复默认对齐数,恢复默认对齐数为8
//如果想恢复默认对齐数,我们pack()括号内可以直接不写
int main()
{
    //printf("%zd\n", sizeof(struct S));//12个字节---默认对齐数为8的时候
    printf("%zd\n", sizeof(struct S));//6个字节-=--默认对齐数是1的时候
    return 0;
}

3.结构体传参

//结构体传参
struct S//我们必须将这个结构体类型的创建放在前面,不然会报错的
{
    int arr[1000];
    int n;
    char ch;
};
 
void print1(struct S tmp)//传过来的是一个结构体变量,那么我们就创建一个结构体变量进行接收
{
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", tmp.arr[i]);
    }
    printf("\n");
    printf("n=%d\n", tmp.n);
    printf("ch=%c\n", tmp.ch);
 
}
 
void print2(const struct S* ps)//传过来的是结构体变量的地址,那么我们就创建一个结构体指针进行接收
{//那么ps就指向了s
 
    for (int i = 0; i < 10; i++)
    {
        printf("%d ", ps->arr[i]);
    }
    printf("\n");
    printf("n=%d\n", ps->n);
    printf("ch=%c\n",ps->ch );
 
}
int main()
{
    struct S s = { {1,2,3,4,5,6,7,8,9,10},10,'w' };//创建结构体变量s并进行初始化
    //利用函数将结构体内数据打印出来
    print1(s);//直接将结构体变量s传过去-----传值调用
    print2(&s);//直接将结构体变量s的地址传过去
    return 0;
}
 
 
//分析:那种更好一些
/*
在print1中,我们传过去的是结构体变量,
相当与传值调用
在调用的时候,我们还要创建一个结构体变量tmp用来接收传过来的数据
在这个调用的时候,我们还要额外的空间进行结构体变量的创建
 
假如传过来的变量占用空间很大,我们又要创建一个一模一样的空间进行存储
 
我认为这么很浪费空间,还很消耗时间
 
*/
 
/*
但是我们利用print2传的仅仅只是地址,地址的大小是4个字节或者8个字节
 
我们在创建形参的时候仅仅只需要创建一个指针ps进行接收,
指针ps就能找到这个结构体变量内的数据
这样也不需要创建结构体指针变量进行数据的拷贝,我们直接利用地址进行数据的调用和访问
*/
 
//传值能做到的,传地址一定能做到
//传地址能做到的,传值不一定能做到
 
//所以我们在结构体传参的时候,要传结构体的地址
 
//如果不想因为地址传过去的原因,地址指向的数据被改变了,我们直接加上const

在我们进行结构体传参的时候,我们传地址就行了

4.结构体实现位段

结构体讲完就得讲讲结构体实现位段的能力

位段的声明和结构是类似的,有两个不同:


1.位段的成员必须是int 、unsigned int 或signed int,在C99中位段成员的类型也可以选择其他类型

2.位段的成员名后边有一个冒号和一个数字


位段中的位指的是二进制中的位

//二进制中一位就是一个比特位
//一个字节是8个比特位
struct S
{
    /*int _a:2;*///_a占2个比特位
    /*
    假设我们在设定a的值的时候,我们只想要a是0 1 2 3 这4个数字
    那么二进制位表达出来就是
    0---00
    1---01
    2---10
    3---11
 
    这四个数刚好满足两个比特位的要求
    如果我们给a32个比特位的话就有点浪费空间了
    */
 
 
 
    int _a : 2;
    int _b:5;//_b占5个比特位
    int _c:10;//_c占10个比特位
    int _d : 30;//_d占30个比特位
    //int _e : 50;//这种写法是错的,对于一个整型来说,最大也就32个比特位,没有50
};
 
int main()
{
    printf("%zd\n", sizeof(struct S));//8
    /*
    如果不进行位段的调整,将是16个字节
 
    */
    return 0;
}
//这种设计相当于限制了大小,可以减小空间,避免空间浪费
//8个比特位等于1个字节

通过位段的使用,可以减少空间的占有,减少空间浪费

限制数据的长度、大小

节省空间

C语言---自定义类型:结构体(4)https://developer.aliyun.com/article/1544455

相关文章
|
23天前
|
存储 C语言
如何在 C 语言中实现结构体的深拷贝
在C语言中实现结构体的深拷贝,需要手动分配内存并逐个复制成员变量,确保新结构体与原结构体完全独立,避免浅拷贝导致的数据共享问题。具体方法包括使用 `malloc` 分配内存和 `memcpy` 或手动赋值。
30 10
|
22天前
|
安全 编译器 Linux
【c语言】轻松拿捏自定义类型
本文介绍了C语言中的三种自定义类型:结构体、联合体和枚举类型。结构体可以包含多个不同类型的成员,支持自引用和内存对齐。联合体的所有成员共享同一块内存,适用于判断机器的大小端。枚举类型用于列举固定值,增加代码的可读性和安全性。文中详细讲解了每种类型的声明、特点和使用方法,并提供了示例代码。
21 3
|
22天前
|
存储 大数据 编译器
C语言:结构体对齐规则
C语言中,结构体对齐规则是指编译器为了提高数据访问效率,会根据成员变量的类型对结构体中的成员进行内存对齐。通常遵循编译器默认的对齐方式或使用特定的对齐指令来优化结构体布局,以减少内存浪费并提升性能。
|
27天前
|
编译器 C语言
共用体和结构体在 C 语言中的优先级是怎样的
在C语言中,共用体(union)和结构体(struct)的优先级相同,它们都是用户自定义的数据类型,用于组合不同类型的数据。但是,共用体中的所有成员共享同一段内存,而结构体中的成员各自占用独立的内存空间。
|
27天前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
1月前
|
编译器 C语言 C++
C语言结构体
C语言结构体
25 5
|
1月前
|
编译器 Linux C语言
C语言 之 结构体超详细总结
C语言 之 结构体超详细总结
19 0
|
1月前
|
存储 编译器 Linux
深入C语言:探索结构体的奥秘
深入C语言:探索结构体的奥秘
|
1月前
|
存储 编译器 C语言
c语言回顾-结构体(2)(下)
c语言回顾-结构体(2)(下)
28 0
|
1月前
|
存储 编译器 程序员
c语言回顾-结构体(2)(上)
c语言回顾-结构体(2)(上)
28 0