C语言柔性数组详解:让你的程序更灵活

这篇具有很好参考价值的文章主要介绍了C语言柔性数组详解:让你的程序更灵活。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

仔细观察下面的代码,有没有看出哪里不对劲?

struct S
{
	int i;
	double d;
	char c;
	int arr[];
};

还有另外一种写法:

struct S
{
	int i;
	double d;
	char c;
	int arr[0];
};

你应该一眼就看到了,结构体的最后一个成员数组的写法是int arr[];或者是int arr[0],这两种写法是等价的,意思是这个数组的大小是不确定的、未知的、可以变化的

C99允许这种特殊的结构体存在。这样的结构体满足下面两个条件:

  1. 最后一个成员变量是一个大小可以变化的数组。
  2. 这个成员数组前面至少有另外一个成员变量。

我们称这个大小可以变化的成员数组为柔性数组

注意,柔性数组不能是结构体里唯一一个成员,下面的代码是不允许的:

struct S
{
	int arr[0];
};

这篇文章里,我将重点探讨柔性数组的用法、内存分布以及和优势。

二、柔性数组的用法

我不建议在栈上直接定义有柔性数组的结构体,也就是这么写:

struct S s;

因为柔性数组的大小是可以变化的,我建议在堆上申请空间,采取动态内存管理的方法,这样就能发挥出柔性数组大小可以改变的优势。

假设我们使用malloc()函数来开辟空间,一开始应该malloc出多大的空间呢?要回答这个问题,首先我们要知道sizeof(struct S)是多少。

事实上,sizeof(struct S)计算出来的结果是该结构体不考虑柔性数组的大小。如果我们想要给柔性数组开辟空间,malloc出来的大小应该是sizeof(struct S)加上柔性数组的大小。

假设这个柔性数组在结构体中的声明是int arr[0];,我想给这个数组的大小是40个字节,这样这个数组就能存储10个int,那么一开始malloc的大小就应该是sizeof(struct S)+10*sizeof(int),具体的例子如下:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	return 0;
}

该结构体中的i,d,c等变量可以正常使用。

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	return 0;
}

柔性数组也可以像正常的数组一样访问,比如把1~10放进去。注意此时这个数组的容量是10个int,不能越界访问。使用的例子如下:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	return 0;
}

我们还可以对柔性数组扩容,如果我们想让这个柔性数组的容量是20个int,整个结构体的新的大小就是sizeof(struct S)+20*sizeof(int),因为sizeof(struct S)是不考虑柔性数组的大小时计算的结构体大小。只需要对ps进行realloc就行了。实现代码如下:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps = tmp;
	}

	return 0;
}

扩容后的柔性数组的空间更大了,我们可以把11~20都放进去。实现代码如下:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps = tmp;
	}

	for (int i = 10; i < 20; i++)
	{
		ps->arr[i] = i + 1;
	}
	
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	return 0;
}

当然最后别忘了free掉ps,否则会导致内存泄漏。

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->i = 10;
	ps->d = 3.14;
	ps->c = 'F';

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	struct S* tmp = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps = tmp;
	}

	for (int i = 10; i < 20; i++)
	{
		ps->arr[i] = i + 1;
	}
	
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	free(ps);
	ps = NULL;

	return 0;
}

对于柔性数组的使用,在上面的例子中,可以总结出几个要点:

  1. malloc出来的大小是sizeof(struct S)加上柔性数组的大小,calloc同理。
  2. 扩容时realloc出来的新大小也是sizeof(struct S)加上柔性数组的新大小。
  3. 每次使用malloc和realloc等函数时,需要检查返回值,否则可能导致对NULL指针的解引用(这点是动态内存管理的常识了)。
  4. 一定要记得柔性数组的容量是多少,不要越界访问了,空间不够记得扩容。
  5. 记得free,防止内存泄漏。

三、柔性数组的内存分布

柔性数组是结构体的一个成员数组,在前面的例子中,整个结构体都是在堆上malloc出来的。此时,整个结构体都存储在堆上的一块连续的空间里,包括前面几个成员变量i,d,c和柔性数组arr。也就是这样:
C语言柔性数组详解:让你的程序更灵活,C语言,c语言,柔性数组,算法
只不过数组arr的大小是可以改变的,所以叫“柔性数组”。

有些朋友可能会说了,我不需要柔性数组也能实现类似这样的效果呀!我在结构体里存一个指针,指向一块malloc出来的空间,这块空间也是堆上的,可以动态管理。也就是说,像下面这样定义结构体:

struct S
{
	int i;
	double d;
	char c;
	int* arr;
};

这样似乎还简单一点,先malloc出一个struct S出来,malloc的大小就是sizeof(struct S),像这样:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	return 0;
}

然后再malloc出10个int的大小出来,用结构体中的arr指针来管理这块空间,像这样:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	return 0;
}

此时arr就可以当成一个数组来使用了,比如把1~10放进去。同样还是要注意不要越界访问。示例代码如下:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}
	
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	return 0;
}

你如果觉得空间不够,还可以扩容。比如,你可以把结构体中的arr进行realloc,新的大小能存放20个int。示例代码如下:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}
	
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	int* tmp = (int*)realloc(ps->arr, 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps->arr = tmp;
	}

	return 0;
}

此时,你就可以把11~20也放进去。实现代码如下:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	int* tmp = (int*)realloc(ps->arr, 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps->arr = tmp;
	}

	for (int i = 10; i < 20; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	return 0;
}

最后别忘了把arr和ps都free掉,而且顺序不能错了。如果你先free掉了ps,结构体就没了,里面的arr就成为了野指针,内存就泄露了。实现代码如下:

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

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		printf("malloc()->%s\n", strerror(errno));
		return 1;
	}

	ps->arr = (int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		printf("2: malloc()->%s\n", strerror(errno));
		return 1;
	}

	for (int i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	int* tmp = (int*)realloc(ps->arr, 20 * sizeof(int));
	if (tmp == NULL)
	{
		printf("realloc()->%s\n", strerror(errno));
		return 1;
	}
	else
	{
		ps->arr = tmp;
	}

	for (int i = 10; i < 20; i++)
	{
		ps->arr[i] = i + 1;
	}

	for (int i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}

	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;

	return 0;
}

那这种实现的内存分布是怎么样的呢?这个结构体是存储在堆上的,用ps来管理,结构体里的一个指针arr又指向了堆上的另一块空间,如下图:
C语言柔性数组详解:让你的程序更灵活,C语言,c语言,柔性数组,算法
这种实现方式和柔性数组的方式感觉差不多呀!都是在堆上有个结构体,结构体里有个大小可以变化的数组。那为什么非要搞出来个柔性数组的概念呢?那是因为,柔性数组有它独特的优势。

四、柔性数组的优势

前面我们先用柔性数组实现了一种效果,又不使用柔性数组实现了相似的效果,对比两种实现方式,我们可以做一些总结:

  1. 使用上:柔性数组malloc了一次,free了一次;不使用柔性数组要malloc两次,free两次。柔性数组的使用更加简单,不容易出错。如果不使用柔性数组,可能会忘记free掉结构体里的arr指针,导致内存泄漏。
  2. 效率上:柔性数组的存储空间是连续的,访问时效率更高。

所以,虽然有相似的效果,我更推荐使用柔性数组的方式。

五、总结

在这篇博客里,重点需要掌握以下几点:文章来源地址https://www.toymoban.com/news/detail-701073.html

  1. 如果结构体里最后一个成员变量是一个数组,并且大小可以变化,这个成员数组就叫做柔性数组。一个结构体里,除了柔性数组外必须至少有一个成员变量。
  2. 使用sizeof计算含有柔性数组的结构体大小时,只计算除柔性数组之外的空间大小。
  3. 使用柔性数组,比不使用柔性数组操作更加简单,不易出错,且效率更高。

到了这里,关于C语言柔性数组详解:让你的程序更灵活的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Animation Rigging 如何让你的Avatar人物更具灵活性

    Animation Rigging 是 Unity 官方发布的可以对 Avatar 人物骨骼进行约束的工具,已经有稳定的经过验证的 Vertified 包体,可以将其理解为一个 IK 工具,使用它可以让我们的人物动作表现更具灵活性。 Rig Builder 依赖 Animator 组件,所以将其与 Avatar 的 Animator 组件挂载于同一个物体上,

    2023年04月21日
    浏览(67)
  • 【设计模式】模板方法模式--让你的代码更具灵活性与可扩展性

    在软件开发中,设计模式是一种经过实践检验的、可复用的解决方案,它们可以帮助我们解决某一特定领域的典型问题。设计模式不仅能提高代码的可读性、可维护性,还能让我们的代码更加灵活和易于扩展。在这个不断发展的技术世界中,了解并掌握设计模式对于软件开发

    2024年02月04日
    浏览(36)
  • 【C语言】还有柔性数组?

    也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。C99中, 结构中的最后⼀个元素允许是未知⼤⼩的数组 ,这就叫做『柔性数组』成员。 欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 前言 柔性数组 柔性数组的特

    2024年03月10日
    浏览(38)
  • 柔性数组和C语言内存划分

    也许你从来没有听说过 柔性数组 (flexible array)这个概念,但是它确实是存在的。 C99 中,结构中的最后⼀个元素允许是未知大小的数组,这就叫做『柔性数组』成员。 例如: 有些编译器会报错⽆法编译可以改成: 1.1 柔性数组的特点: 结构中的柔性数组成员前面必须至少⼀

    2024年01月22日
    浏览(42)
  • C语言探索旅程之【柔性数组】

    目录 1. 柔性数组的定义 2. 柔性数组的使用 3. 柔性数组的注意事项 4. 柔性数组的优点 5. 柔性数组的应用场景 当我们谈到C语言中的柔性数组时,我们指的是一种特殊的数组,其大小在运行时动态确定,而不是在编译时确定。柔性数组是C语言中一种非常有用且灵活的特性,特

    2024年03月22日
    浏览(72)
  • 【C语言数组】一维数组,二维数组详解,数组传参,变长数组,这篇文章让你更全面的认识数组。

    前言: 大家好,我是 良辰丫 💞,今天带大家全面认识一下C语言里面的 数组 ,大家是不是满怀期待呢?嘿嘿嘿,别着急,我们往下看,感受C语言数组的魅力!!!💌💌💌 要么出众,要么出局。💝 乾坤未定,💟你我皆是黑马。 保存一组成绩的数据,数据多的时候难道要

    2024年01月19日
    浏览(51)
  • 【C语言进阶】智能管理:如何使用柔性数组实现内存优化

    目录 一、定义 二、用法 三、特点 四、注意事项 五、总结 在 C 语言中,柔性数组(Flexible Array)是一种特殊类型的数组,它允许程序员在运行时动态地分配数组的大小,从而实现更灵活的内存管理。本文将详细介绍柔性数组的定义、用法、特点及注意事项。 C 语言的数组是

    2023年04月24日
    浏览(58)
  • 椋鸟C语言笔记#31:结构体(联合体)嵌套、柔性数组

    萌新的学习笔记,写错了恳请斧正。 目录 结构体(联合体)嵌套 嵌套时内嵌结构体(联合体)不创建变量(匿名) 嵌套时内嵌结构体(联合体)创建变量(非匿名) 嵌套初始化 柔性数组 柔性数组的使用 结构体(联合体)嵌套 结构体、联合体可以嵌套,也就是说结构体(

    2024年02月02日
    浏览(44)
  • 【C语言】动态内存管理(malloc,free,calloc,realloc,柔性数组)

    本章重点 为什么存在动态内存管理 动态内存函数的介绍 malloc free calloc realloc 常见的动态内存错误 几个经典的笔试题 柔性数组 我们已经掌握的内存开辟方式有: int val = 20; //在栈空间上开辟四个字节 char arr[10] = {0}; 在栈空间上开辟十个字节的连续空间 但是上述开辟空间的方

    2024年02月03日
    浏览(48)
  • 【C进阶】分析 C/C++程序的内存开辟与柔性数组(内有干货)

            本文是对于动态内存管理知识后续的补充,以及加深对其的理解。对于动态内存管理涉及的大部分知识在这篇文章中 ---- 【C进阶】 动态内存管理_Dream_Chaser~的博客-CSDN博客         本文涉及的知识内容主要在两方面: 简单解析C/C++程序的内存开辟 分析柔性数组

    2024年02月09日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包