掌握了这些,才算真正了解C语言数组

这篇具有很好参考价值的文章主要介绍了掌握了这些,才算真正了解C语言数组。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

掌握了这些,才算真正了解C语言数组
也许你认为,C语言中的数组非常好理解,就是把一组相同类型的元素存储在同一块空间里。但是你可能并没有真正理解数组的本质,不信的话请回答一下下面的几个小问题,如果你能非常清晰的回答这些问题,那么你对C语言中的数组的理解就入门了。

  1. 一维数组和二维数组在定义时,哪些大小可以省略,哪些不可以省略?如果可以省略,在什么时候是可以省略的呢?
  2. 一维数组和二维数组在内存中是如何存储的?
  3. 一维数组和二维数组的数组名分别表示什么意思?
  4. sizeof(数组名) 和 &数组名 分别表示什么?
  5. 一维数组和二维数组传参时,形参应该如何写?
  6. 数组和指针有什么联系?有什么区别?
  7. 如何使用指针来遍历一维数组和二维数组?
  8. 如何理解数组指针和指针数组?
  9. 什么是柔性数组?

如果你想了解这些内容,那就花点时间阅读下本篇博客吧,希望你能有所收获。

什么是数组

数组,指的是相同类型的元素的集合,比如,想要存储10个整数,除了定义10个int类型的变量,也可以定义一个数组来存储。

数组分为一维数组和二维数组,三维以上的数组就不常见了,所以本篇博客重点讨论一维数组和二维数组。

所谓一维数组,就是很正常的把一些元素放到一起。比如存储1~10,就这么存储:[1 2 3 4 5 6 7 8 9 10]。

而二维数组是有行和列的,比如,用一个3行5列的二维数组来存储1~15,可以这么理解:

掌握了这些,才算真正了解C语言数组

数组的定义和初始化

数组的定义和初始化是要按照指定的个数来的。

对于一维数组,只需要表示出数组名、数组容量和数组元素类型即可。比如,一个能存储10个int类型的数组就应该这么定义:int arr[10];

这样的数组如果是一个局部的数组,也就是在大括号内定义的话,默认会被初始化为随机值。如果你想要手动初始化,比如把1~10放进去,可以这么写:

int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

如果手动进行初始化,可以省略数组的元素个数,编译器会根据你初始化的元素个数来给数组开辟空间。

int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

以上代码中,虽然我把表示数组大小的方括号里的10给省略了,编译器也会开辟一个能够存储10个int数据的数组,因为我手动给这个数组初始化了。

当然,我也可以只给数组的部分元素初始化,比如:

int arr[10] = {1, 2, 3};

如果像上面这么写,这个数组就只会初始化前3个元素为1~3,剩下的7个元素会被默认初始化成0,所以数组实际存储的是:[1 2 3 0 0 0 0 0 0 0]。

一个很常见的写法是,把整个数组都初始化成全0。

int arr[10] = {0};

根据我们对一位数组不完全初始化的理解,如果像上面这么写,就只把第一个元素初始化为0,其余9个元素会被默认初始化为0,其实效果就是把整个数组的所有元素都初始化为0。

讲完了一维数组,那二维数组呢?

二维数组的定义需要指定行数和列数,比如一个3行5列的数组应该这么定义:int arr[3][5];

如果要对其进行初始化,比如初始化为:第一行1~5,第二行6~10,第三行11~15,就这么写:

int arr[3][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

效果可以理解成:
掌握了这些,才算真正了解C语言数组
也可以把每一行也用大括号括起来,这样更加清晰:

int arr[3][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15} };

建议再换个行:

int arr[3][5] = { {1, 2, 3, 4, 5}, 
                  {6, 7, 8, 9, 10}, 
                  {11, 12, 13, 14, 15} };

那能不能像一维数组那样省略大小呢?答案是:行可以省略,列不能省略!也就是说,可以省略行,写成这样:

int arr[][5] = { {1, 2, 3, 4, 5}, 
                 {6, 7, 8, 9, 10}, 
                 {11, 12, 13, 14, 15} };

编译器看到后,就会理解成,由于你放了3行,所以行数就是3。

同样,也可以进行不完全初始化,比如:

int arr[3][5] = { {1, 2, 3, 4, 5}, 
                  {6, 7, 8, 9, 10} };

由于只放了2行,第三行就会被默认初始化为0,也就是这样的布局:
掌握了这些,才算真正了解C语言数组
当然每一行也可以不完全初始化,比如:

int arr[3][5] = { {1, 2, 3}, 
                  {6, 7} };

效果就是,第一行前3个元素初始化为1~3,后2个元素初始化为0,第二行的前2个元素初始化为6和7,后3个元素初始化为0,第三行全部初始化为0。
掌握了这些,才算真正了解C语言数组
当然,如果内层的大括号省略掉,也有类似的效果,不过会一行一行放,放满一行再放下一行,比如:

int arr[3][5] = {1, 2, 3, 4, 5, 6, 7};

会先把第一行放满,第一行就是1~5,第二行前2个元素就是6和7,后3个元素初始化为0,第三行所有元素都是0。
掌握了这些,才算真正了解C语言数组
和一维数组一样,如果想把所有元素都初始化为0,就这么写:

int arr[3][5] = {0};

这样就只放了一个0,其余元素都被初始化为0,就相当于把整个数组的所有元素都初始化为0。

数组在内存中的存储

数组在内存中的存储满足以下2点:

  1. 数组在内存中是连续存储的。
  2. 随着数组下标的增长,地址是由低到高变化的。

先举一个一维数组的例子。写一个程序,把一维数组的每一项的地址都打印出来:

#include <stdio.h>

int main()
{
	int arr[5];
	for (int i = 0; i < 5; ++i)
	{
		printf("&arr[%d] = %p\n", i, &arr[i]);
	}

	return 0;
}

输出结果如下:

掌握了这些,才算真正了解C语言数组
观察到,数组的相邻2项之间的地址都相差了4个字节。为啥是4个字节呢?因为每个int是4个字节,由于数组在内存中是连续存储的,相邻两项的地址自然隔了4个字节。除此之外,随着数组下标从0增长到4,地址也是从低到高变化的。

再举一个二维数组的例子:

#include <stdio.h>

int main()
{
	int arr[3][5];
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 5; ++j)
		{
			printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
		}
	}

	return 0;
}

输出结果如下:
掌握了这些,才算真正了解C语言数组
相邻2项的地址仍然间隔4个字节,且随着下标的增长,地址也在增长,这说明数组的内存分布规律同样适用于二维数组。

神奇的数组名

先说结论。在C语言中,数组名表示数组首元素地址,但是有2个例外:

  1. sizeof(数组名),数组名直接放在sizeof()内部,此时数组名不表示首元素地址,而是表示整个数组,求的是整个数组的大小,单位是字节。
  2. &数组名,在&符号后面的数组名,此时数组名不表示首元素地址,而是表示整个数组,取出的是整个数组的地址。详见后面讲解的“数组指针”。

关于sizeof,大家应该很熟悉了,可以计算类型的大小。如果想要计算数组的大小,就sizeof(数组名)即可。比如:

int arr[10];
printf("%d\n", sizeof(arr)); // 40

以上代码中,由于数组有10个元素,每个元素是int,数组总大小是40个字节,所以会输出40。

下面来对比一下3个代码:

int arr[10];

printf("arr = %p\n", arr);
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr = %p\n", &arr);

其实3个玩意打印出来结果是一样的。但是我们要理解它们的本质:

  1. arr是数组名,数组名表示数组首元素地址,也就是arr[0]的地址。
  2. &arr[0],就是数组首元素的地址。
  3. &arr,数组名表示整个数组,取出的是整个数组的地址。

“整个数组的地址”和“数组首元素地址”在值上是一样的。但是如果+1,或者解引用,效果是不一样的,这就要牵扯对指针的理解了。指针类型决定了指针+1时跳过的步长。比如一个int*的指针+1会跳过4个字节,也就是跳过一个int,而一个char*的指针+1会跳过1个字节,也就是跳过了一个char。

在上面的例子中,arr和&arr[0]都表示数组首元素的地址,也就是一个int的地址,类型是int*,+1后会跳过一个int,也就是跳过4个字节。而&arr表示整个数组的地址,如果+1,会跳过整个数组。观察一下以下程序:

#include <stdio.h>

int main()
{
	int arr[10];

	printf("        arr = %p\n", arr);
	printf("    arr + 1 = %p\n", arr + 1);
	printf("    &arr[0] = %p\n", &arr[0]);
	printf("&arr[0] + 1 = %p\n", &arr[0] + 1);
	printf("       &arr = %p\n", &arr);
	printf("   &arr + 1 = %p\n", &arr + 1);

	return 0;
}

输出结果如下:
掌握了这些,才算真正了解C语言数组
观察到确实是这样的。arr+1和&arr[0]+1都跳过4个字节,而&arr+1跳过了16进制的0x28,也就是10进制的40个字节。

那二维数组的数组名表示什么呢?实际上,二维数组的数组名也表示首元素的地址,而二维数组的首元素是第一行,也就是说,二维数组的数组名表示第一行的地址。比如一个二维数组是int arr[3][5];,数组名arr表示的是第一行的地址,也就是一个容量是5个int的一维数组的地址,类型就是int (*)[5]。

数组和指针的区别和联系

数组和指针有什么区别呢?这个问题其实很奇怪,因为这2个玩意完全就不是一个东西。数组是一组相同类型的元素的集合,而指针是用来存储地址的,它们八竿子打不着。但是确实存在着一个关联,那就是前面讲解到的:数组名表示数组首元素的地址。根据这一点,就可以拿到一个指向数组首元素的指针。又由于数组在内存中是连续存放的,就可以通过这个指针来遍历这个数组。比如:

#include <stdio.h>

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	while (p < arr + 10)
	{
		printf("%d ", *p++);
	}

	return 0;
}

输出结果如下:
掌握了这些,才算真正了解C语言数组

数组传参

有些时候,我们要把数组作为实参,传递给函数,函数的形参应该如何写呢?

先看一维数组传参。假设有这样的场景:

int arr[10];
test(arr);

此时函数test的形参应该如何写呢?

  1. 我们传过去了一个数组,自然可以用一个数组来接收:void test(int arr[10]);
  2. 数组的大小可以省略,而且建议省略:void test(int arr[]);
  3. 由于数组名表示首元素地址,我们本质上是传过去了一个地址,所以应该用一个指针来接收:void test(int* arr);
  4. 既然传过去的是地址,那前2种写法中,数组的大小重要吗?一点都不重要,所以你哪怕乱写其实也不会出问题,比如:void test(int arr[1000]);

接着看二维数组传参:

int arr[3][5];
test(arr);

此时test的形参应该如何写呢?

  1. 我们传过去了一个数组,自然可以用一个数组来接收:void test(int arr[3][5]);
  2. 注意:二维数组的行可以省略,但是列不能省略!比如:void test(int arr[][5]);
  3. 数组名表示首元素地址,二维数组的数组名表示第一行的地址,也就是一个容量为5个int的数组的地址,类型是int (*)[5],形参可以这么写:void test(int (*arr)[5]);
  4. 既然传过去的是地址,那前2种写法中,数组的大小重要吗?一点都不重要,所以你哪怕乱写其实也不会出问题,但是在二维数组中,列是很重要的,必须写对,但是行随便。比如:void test(int arr[1000][5]);

指针数组和数组指针

指针数组,本质是数组,只不过存放的是指针。比如:int* arr[10];就是一个指针数组,能存储10个int*类型的指针。

数组指针,本质是指针,只不过是指向数组的指针,存放的是数组的地址。详见我之前写过的一篇博客,戳这里跳转。

柔性数组

柔性数组是结构体内的大小可以变化的数组,这个知识点和动态内存管理也有联系,这里展开讲解就太长了。还好我之前写了一篇博客来讲解这个知识点,戳这里跳转。

总结

现在我们可以回答开头的问题了。

  1. 一维数组和二维数组在定义时,哪些大小可以省略,哪些不可以省略?如果可以省略,在什么时候是可以省略的呢?一维数组如果初始化,可以不指定大小,编译器会根据初始化的情况来给数组开辟空间。二维数组如果初始化,行可以省略,但是列不能省略,编译器会根据初始化的情况来决定有几行。
  2. 一维数组和二维数组在内存中是如何存储的?数组在内存中是连续存放的,随着数组下标的增长,地址是由低到高变化的,这点对一维数组和二维数组都适用。
  3. 一维数组和二维数组的数组名分别表示什么意思?数组名表示首元素地址,但是有2个例外:sizeof(数组名),&数组名,数组名都表示整个数组。除此之外,数组名都表示首元素地址,其中二维数组的“首元素”指的是第一行。
  4. sizeof(数组名) 和 &数组名 分别表示什么?sizeof(数组名)求的是整个数组的大小,&数组名取出来的是整个数组的地址。
  5. 一维数组和二维数组传参时,形参应该如何写?可以用数组接收,一维数组的大小可以省略,二维数组的行可以省略,列不能省略。对于省略的大小,其实乱写也是符合语法的,但是不建议。除此之外,根据“数组名表示数组首元素地址”这个知识点,也可以用指针接收。
  6. 数组和指针有什么联系?有什么区别?数组是一组相同类型元素的集合,指针是存储地址的。它们之间被“数组名”这个桥梁关联起来,因为数组名表示数组首元素地址。
  7. 如何使用指针来遍历一维数组和二维数组?根据数组在内存中是连续存储的,只要拿到首元素地址,就能遍历整个数组。
  8. 如何理解数组指针和指针数组?数组指针是一个指针,存储的是数组的地址。指针数组是一个数组,存储的元素类型是指针。
  9. 什么是柔性数组?柔性数组是结构体内的最后一个成员数组,且大小可以变化(大小不确定)的。管理柔性数组,要使用动态内存管理的方式。

感谢大家的阅读!文章来源地址https://www.toymoban.com/news/detail-430450.html

到了这里,关于掌握了这些,才算真正了解C语言数组的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 大厂一线研发揭秘:怎么样才算全面了解了业务?

    刚做开发时,觉得自己要好好学习技术,对业务不上心,公司让我做什么就用自认为极其优雅的编码方式解决,但对为什么要做这个事,做了对业务有什么影响,做完了有没有达到业务目标,并没有很关心。后续观察了身边的一些同学,整体上业务与技术链接比较好的同学职

    2024年04月22日
    浏览(36)
  • 去哪里能找到真正的黑客?现在黑客的聚集地在这些地方!

    如果想找黑客办事,去哪里才能找到真正的黑客,不是抖音,而是这些黑客高手的聚集地,99%的人都不知道。 第一个是公开的漏洞平台,比如补天和漏洞盒子,每天都有大量的白帽黑客在上面用技术给企业挖漏洞,不过这里的黑客都是有着原则的,不然他们也不会甘心于挖漏

    2024年02月19日
    浏览(38)
  • 掌握它才说明你真正懂 Elasticsearch - Lucene (一)

    Lucene 简介 Lucene 是一种高性能、可伸缩的信息搜索(IR)库,在 2000 年开源,最初由鼎鼎大名的 Doug Cutting 开发,是基于 Java 实现的高性能的开源项目。 Lucene 采用了基于倒排表的设计原理,可以非常高效地实现文本查找,在底层采用了分段的存储模式,使它在读写时几乎完全

    2024年02月03日
    浏览(78)
  • 【C语言初阶】带你轻松掌握指针基础知识完结篇——野指针,指针运算,指针和数组,二级指针

    君兮_的个人主页 勤时当勉励 岁月不待人 C/C++ 游戏开发 Hello,这里是君兮_,今天继续给大家更新0基础入门C语言的内容,我们这次主要更新的依然是初阶指针的基础知识 废话不多说咱们直接开始吧!! 概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有

    2024年02月16日
    浏览(45)
  • 【C语言初阶】指针的运算or数组与指针的关系你了解吗?

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《快速入门C语言》《C语言初阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,前面给大大家介绍了指针的基础知识。那么这一章就来给大家介绍下一部分内容!    ⛳️ 指针的运算 以及 数组与指

    2024年02月16日
    浏览(34)
  • 让你真正了解免费ai绘画工具有哪些

    你有幻想过未来世界会变成什么样吗?浩瀚无际的宇宙究竟长是什么的?巨大的加菲猫与普通的加菲猫模样上又有何不同?以上问题ai智能技术都可以帮你解决,说到这,你想不想也拥有一款ai绘画软件来帮你将梦想照进现实?想的话,就跟我一起来看看免费ai绘画工具有哪些

    2024年02月15日
    浏览(31)
  • 你真正了解低代码么?(国内低代码平台状况分析)

    ■ 写在前面 都说技术是生产力,这话没错,但是我想说的是“生产力也是分等级的”。就拿当下爆火的人工智能 ChatGPT 来说,这种产品才算是真正降维到 C 端的生产力产品。也只有 C 端的生产力产品,才能在短短几天注册量突破 1 个亿! B 端:面向商家、企业级部门提供的

    2023年04月14日
    浏览(45)
  • 这些vue基本语法,你掌握了吗

    1. 单页面应用程序 单页面应用程序指的是一个应用程序中只有唯一的一个 HTML 页面,所有的功能与交互都在这唯一的一个页面内完成。 单页面应用程序将所有的功能局限在一个HTML页面中,仅在该HTML页面初始化时加载相应的资源(HTML、JavaScript、CSS)。一旦页面加载完成了,

    2024年02月01日
    浏览(78)
  • 掌握 Midjourney 这些关键词,秒变Ai绘画大神!

    套用公式 1.比如角色人物:  人物名(命名)+描述词(重要词和人物特征词)+设计风格+官方命令词 举例: 进击巨人(特定来源)贝尔托特(人物姓名)Q版形象(重要描述词),设计风格是迪士尼或者皮克斯,且小巧可爱(描述)。(官方 --ar 16:9(比例)) 整理成

    2024年02月09日
    浏览(64)
  • 掌握这些GitHub搜索技巧,你的开发效率将翻倍!

    作为开发it行业一员,学习借鉴他人项目是很有必要的,所以我们一般都会从 github 或者 Gitee 上面去参考借鉴他人的项目来学习增加自己的项目经验 但是github你真的用对了嘛,他的功能其实很强大!!! 在Github搜索栏中输入与您感兴趣的技术相关的,例如 “machine le

    2024年02月02日
    浏览(77)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包