哦!数组还能这么用,学到了!

来源:公众号【编程珠玑】

作者:守望先生

ID:shouwangxiansheng

这个问题源于读者在阅读redis源码时的一个疑问。

先看下面的代码,对于包含动态字符串成员的两个结构体Test0和Test1占用空间分别是多少呢?

//来源:公众号【编程珠玑】
//作者:守望先生
#include<stdio.h>
struct Test0
{int a;int b;char *c;
};
struct Test1
{int a;int b;char c[];
};
int main(void)
{printf("sizeof(struct Test0) = %zd\n",sizeof(struct Test0));printf("sizeof(struct Test1) = %zd\n",sizeof(struct Test1));return 0;
}

很多读者一眼就能看出来,在64位系统上,编译为64位程序,其输出结果为:

16
8

对于Test0的结果是16,通常没有什么疑问,毕竟4(int)+4(int)+8(指针)= 16,但是对于后者的结构体占用空间为8字节,有的读者可能会有疑问。(关于字节对齐,参考《字节对齐,看这篇就懂了》)

柔性数组(flexible array)

实际上这是在C99中引入的柔性数组的特性。即结构体的最后一个成员,可以不完整类型(一种缺乏足够的信息去描述一个完整对象的类型)的数组,但它使得整个结构体的大小就像没有这个成员一样。但是呢,当用结构体通过这个名字访问这个成员时,就像访问一个普通数组成员一样。

如果数组最终一个元素都没有的话,那么访问这个数组将会是未定义行为了。

正如我们前面所看到的:

struct Test1
{int a;int b;char c[];
};

成员c是一个数组,但是并没有指定大小,使用sizeof计算Test1,其占用空间也仅仅是8字节。

有什么好处?

那么使用柔性数组有什么好处呢?

内存申请和释放

假设分别使用两种类型的结构体,存储16字节的字符数据,需要申请内存。对于struct Test0:

strcut Test0 *t0 = malloc(sizeof(struct Test0));//为结构体申请内存
t0->c = malloc(sizeof(char) * 16);//为成员指向的数据申请内存

而对于struct Test1:

strcut Test1 *t1 = malloc(sizeof(struct Test1) + sizeof(char) * 16);

看出区别了吗?前者需要两次内存申请,而后者只需要一次。前者地址不连续(两次malloc),后者地址连续。而你访问成员c的时候,只需要下面这样就可以:

t1->c,和普通成员无异。

要判断它们的地址是否连续也非常简单,只需要分别打印b和c的地址就可以了。

和内存释放类似,前面需要单独释放成员c申请的内存,而后者可以一起释放。

数据拷贝

正由于前面的差别,导致数据拷贝时,更有区别。
对于struct Test0:

//memcpy(t0copy,t0,sizeof(struct Test0));//不可,这样直接t0copy的c和t0的c指向同一片内存区域。t0copy.a = t0.a;
t0copy.b = t0.b;
memcpy(t0copy.c,t0.c,sizeof(char)*16);

这里无法一次拷贝,因为它的成员c是一个指针类型,我们需要的是一份完整拷贝,因此必须拷贝它指向的内存。(参考《结构体成员赋值到底是深拷贝还是浅拷贝?》)

但是对于struct Test1:

memcpy(t0copy,t0,sizeof(strcut Test1) + sizeof(char) * 16);

在这里,由于柔性数组的内存,它的数据内容和结构体数据成员的地址是连续的,因此可以直接拷贝。

减少内存碎片

由于结构体的柔性数组和结构体成员的地址是连续的,即可一同申请内存,因此更大程度地避免了内存碎片。另外由于该成员本身不占结构体空间,因此,整体而言,比普通的数组成员占用空间要会稍微小点。

零长数组

与柔性数组功能类似,还有一个0长数组,不过它并不是标准中的,但是它可以实现类似的功能,使用方式如下:

struct Test1
{int a;int b;char c[0];
};

差别在于使得数组长度为0。但是由于它并非C标准中的,因此从可移植性考虑,不建议使用这种方式,除非你还无法使用C99。

总结

柔性数组的使用:

  • 位于结构体最后一个位置

  • 不完整数组类型

  • 不是唯一成员

最后,放张图,看差别:

普通和柔性数组

 

推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

嵌入式Linux

微信扫描二维码,关注我的公众号 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/467447.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推荐开源代码2004/12/17

严正声明&#xff1a;本博客中的任何随笔、文章、图片等内容都不能私自转载&#xff0c;必须书面征得作者同意才能转载&#xff0c;并不能随意篡改&#xff0c;如要作出任何改动&#xff0c;必须书面征得作者同意方可&#xff0c;作者拥有一切权利并保留一切追究权利&#xff0…

广东,就是这么横?

昨晚的稿 今天发一下 应该有好久好久没有写篮球相关的文章了&#xff0c;因为之前写了被骂了&#xff0c;不过&#xff0c;今天不一样&#xff0c;毕竟方超巨打得这么好&#xff0c;不吹一下&#xff0c;总感觉今天不完整&#xff0c;骂就骂了吧&#xff0c;反正也不差这一次了…

在ASP.NET中利JavaScript实现控件的聚焦

在Windows应用程序中很容易控制控件的聚焦&#xff0c;但是在ASP.NET中并没有提供这样的功能&#xff0c;但是我们同样可以实现这样的功能&#xff0c;这篇文章就讲述了通过JaveScript实现在页面上某一特定控件获得焦点的功能。 下面是用到的JavaScript代码。 <script langu…

电厂各类设备原理动图,绝对让你看花眼!

▲ 火力发电流程原理▲ 核能发电流程原理▲ 水力发电流程原理▲ 光热发电原理▲ 垃圾发电原理▲ 蒸汽吸收式制冷原理▲ 尿素热解脱硝流程原理▲ 湿法脱硫工艺原理▲ 钢球磨煤机内煤的破碎原理▲ 碎煤机工作原理▲ 螺旋输送机&#xff08;绞龙&#xff09;原理▲ 多管电除尘器▲…

也谈MMU管理机制

1&#xff0c;结构&#xff1a; MMU存储器系统的结构允许对存储器系统的精细控制。大部分的控制细节由存在存储器中的转换表提供。这些表的入口定义了从1KB 到1MB 的各种存储器区域的属性。这些属性包括&#xff1a; 虚拟地址到物理地址映射 ARM 处理器产生的地址叫虚拟…

__ATTRIBUTE__ 你知多少?

_ATTRIBUTE__ 你知多少&#xff1f; 1 #include "stdio.h"2 3 /* 地址参考基准 */4 5 char r1;6 short r2;int refer;7 8 struct p9 { 10 int a; 11 12 char b; 13 14 short c; 15 16 }__attribute__((aligned(4))) pp; 17 /* 4字节对齐&#xff0c;a…

跟几位大佬共进晚餐

这是一篇几个程序员大佬聚会的聚后感文章这次聚会比较唐突&#xff0c;连总从广州专门开车来深圳看望我们&#xff0c;我们约在了某个地铁站的八合里牛肉火锅店&#xff0c;这是一个周五的下午&#xff0c;理论上是非常简单的一个周五&#xff0c;但是因为这些男人女人的存在&a…

vue.js框架搭建

安装脚手架 前提条件&#xff1a;已安装node&#xff08;4.0版本以上&#xff09;&#xff0c;npm a、全局安装 vue-cli npm install -g vue-cli 安装成功后可以通过命令行查看版本号&#xff0c;如图 b、初始化项目 新建一个文件夹命名为01vue&#xff0c;准备在此文件夹下存放…

oracle数据库imp导入,imp 导入 没有数据库

IMP-00009: 导出文件异常结束今天准备从生产库向测试库进行数据导入&#xff0c;结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误&#xff0c;google一下&#xff0c;发现可能有如下原因导致imp的数据太大&#xff0c;没有写buffer和commit两个数据库字符集不同从…

MIK C语言面试两题

这是一个读者朋友在知识星球上提到的两个笔试题&#xff0c;第一个题目比较简单&#xff0c;关键在第二个题目「编程题」&#xff0c;我文章中写的解题思路应该不是最好的&#xff0c;希望大神读者们给出更好的答案&#xff0c;让这个充满乐趣的程序世界再增添一些乐趣吧&#…

看看大疆的C语言面试题

惯例&#xff0c;这笔试题也是一个读者朋友发给我的&#xff0c;简单看了下&#xff0c;并不觉得这是一个非常困难的题目&#xff0c;最近是校招准备的时候&#xff0c;很多人给我说发面试题对大家有帮助。这个题目面试官强调了这个跑在64位系统下。代码如下:#define mal(x,y) …

RocketMQ实战(一)

阿里巴巴有2大核心的分布式技术&#xff0c;一个是OceanBase&#xff0c;另一个就是RocketMQ。在实际项目中已经领教过RocketMQ的强大&#xff0c;本人计划写一个RocketMQ实战系列&#xff0c;将涵盖RocketMQ的简介&#xff0c;环境搭建&#xff0c;初步使用、API详解、架构分析…

C面试总结文档

最近很多人有参加面试&#xff0c;面试就避免不了笔试&#xff0c;嵌入式面试的话&#xff0c;避免不了C语言&#xff0c;所以给大家准备了两份pdf C语言面试总结的文档。在本公众号回复 「C面试」获取pdf下载链接推荐阅读&#xff1a;专辑|Linux文章汇总专辑|程序人生专辑|C语…

程序员到底怎么了

我们是这样的一群人&#xff1a;每天都在“努力”的工作着&#xff0c;每天都和计算机打交道&#xff0c;泡在网上&#xff0c;打游戏&#xff0c;查资料&#xff0c;发微博。可是有一天&#xff0c;突然意识到&#xff0c;我们的未来在哪里&#xff0c;每个月那点可怜的工资&a…

来看看比尔盖茨当年写的BASIC解释器源代码吧,你就知道泰勒级数有什么用了...

几年前当我刚上大学那会&#xff0c;我曾经问过我一位学计算机同学的一个问题&#xff1a;计算机是如何计算诸如 或者 这种运算的&#xff1f;当初这个问题曾经困扰了我好长时间&#xff0c;这个问题并非是我当年在微积分课堂上解决的&#xff0c;而是直到我后来接触编程后才彻…

手机可以连接多少个蓝牙设备?

这是一个课题研究&#xff0c;蓝牙的东西我们可以先不说&#xff0c;我们讨论一个产品需求。我经常遇到一个场景&#xff0c;就是我用我的手机连上TWS蓝牙耳机听音乐&#xff0c;然后呢&#xff0c;我没有开车&#xff0c;我下地铁的时候&#xff0c;我需要打开我的手机&#x…

Spring简洁总结

Spring简洁总结 要的对象不是自己建的&#xff0c;而是IOC容器&#xff08;XML文件&#xff09;给的&#xff0c;我们通过getbean来调用。 依赖注入的话就是对象&#xff08;bean&#xff09;的成员的赋值不是我们手动完成&#xff0c;而是容器&#xff08;XML文件&#xff09;…

抽象工厂的应用

抽象工厂的应用本文是描述了自己对设计模式的工厂的了解.肯定有错误和不足的地方,希望大家能给予支持和建议. 1&#xff0e;问题的引出在前面的Post中,我描述了.NET的反射在软件设计中的应用.当这篇Post发表之后&#xff0c;有人认为用工厂来实现更合理一些。在这篇Post里&…

一口气搞懂「文件系统」,就靠这 25 张图了

前言不多 BB&#xff0c;直接上「硬菜」。正文文件系统的基本组成 文件系统是操作系统中负责管理持久数据的子系统&#xff0c;说简单点&#xff0c;就是负责把用户的文件存到磁盘硬件中&#xff0c;因为即使计算机断电了&#xff0c;磁盘里的数据并不会丢失&#xff0c;所以可…

Samba远程代码执行漏洞(CVE-2017-7494)复现

简要记录一下Samba远程代码执行漏洞(CVE-2017-7494)环境搭建和利用的过程&#xff0c;献给那些想自己动手搭建环境的朋友。(虽然已过多时) 快捷通道&#xff1a;Docker ~ Samba远程代码执行漏洞(CVE-2017-7494) 演 示&#xff1a;服务器版“永恒之蓝”高危预警 &#xff0…