😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍C语言的数组🍭
😎金句分享😎:🍭🍭
本文未经允许,不得转发!!!
目录
🎄一、了解数组,清楚这五个方面
如果定义一个基本数据类型变量,我们可以了解到这三个方面的内容:1、变量所在的内存地址; 2、变量的值; 3、变量的类型;
例如:从语句
int i = 1;
,我们可以知道:
- 1、编译器分配一块4个字节(sizeof(int))的内存,地址是
&i
;- 2、这块内存里面存放了值为4的内容;
- 3、这个变量是
int
类型的。
如果定义的是数组,则我们需要清楚五个方面的内容:
- 1、数组所在的内存地址;
- 2、数组的内容;
- 3、数组的类型;
- 4、数组的元素类型;
- 5、数组的元素个数;
✨1.1 数组所在的内存地址
定义了一个任何类型变量,编译器都会为其分配一块内存来存放该变量,起始地址就是 &变量名
,大小就是该变量的类型的大小。
所以数组也是如此,定义一个数组后,编译器会为该数组分配一块内存。内存大小,就是数组类型的大小。例如:语句int a[5];
,编译器会分配一块内存,起始地址可以通过&a
来获取,内存大小是20个字节(sizeof(int [5])
)。
数组所在的内存地址(数组的地址),就是 &数组名
的值。
✨1.2 数组的内容
数组的内容是指定义数组时,分配的那块内存里存放的东西,与数组元素类型
、数组元素个数
有关。
数组的内容就是由若干个固定大小 的内存块,线性排列组成的一个内存块。这里的若干个
就是数组元素个数
,固定大小
就是每个元素的内存大小,即sizeof(数组元素类型)
。
✨1.3 数组的类型
针对基本数据类型变量 或 指针变量,大部分人都可以轻易指出该变量的类型是什么。
看例子1.3.1:
// array_type.c char c; // char 类型 int i; // int 类型 char *pc; // char* 类型 int *pi; // int* 类型
那么,数组的类型是什么?好像以前都没听过这个词汇!!!
仔细观察上面例子,变量的类型都是在定义语句中,把变量名
去掉,就得到该变量的类型。同样地,在数组定义语句中把数组名去掉就是数组的类型。
看例子1.3.2:
#include <stdio.h> int main() { char ca[10]; // 数组类型是 char [10] int ia[5]; // 数组类型是 int [5] char *pca[8]; // 数组类型是 char *[8] int *aapi[4][5];//数组类型是 int *[4][5] int (*apai[4])[5];//数组类型是 int (*[4])[5]) printf(" sizeof(aapi)=%lu, %lu\n", sizeof(aapi), sizeof(int *[4][5])); printf(" sizeof(apai)=%lu, %lu\n", sizeof(apai), sizeof(int (*[4])[5])); return 0; }
从这个例子,可以轻易指出数组的类型
。在数组定义语句中,把数组名去掉,剩下的就是数组的类型。
但是,在例子中,aapi
和 apai
变量的类型可能又把一部分人的CPU干烧了。
这里需要使用一个右左法则来阅读复杂类型:
右左法则:
- 1、从变量名(没变量名的,从最里层的圆括号)开始,先看右边,再看左边;
- 2、如果右边是
()
则是函数,如果是[]
则是数组。- 3、如果遇到
[]
,后面还是[]
,就先看完右边的[]
,再看左边。
例如:int *pi[4][5];
,p先跟[4][5]结合,再跟*
结合。
变量aapi
,先跟右边[4]
结合,说明是一个有4个元素数组;再跟[5]
结合,说明数组的每个元素都是带有5个元素数组;再跟左边*
结合,表示第二维数组的5个元素都是指针;再跟左边int
结合,表示指针指向int
类型数据。
变量apai
,先跟右边[4]
结合,说明是一个有4个元素数组;因为()
改变优先级,再跟*
结合,说明数组的每个元素都是指针;再跟右边[5]
结合,表示每个指针指向带有5个元素的数组;再跟左边int
结合,表示这些数组都是int类型的。
✨1.4 数组元素的类型
在数组定义语句中,把数组名和后面的[]去掉,剩下的就是数组元素的类型。
看例子1.4.1:
// array_unit_type.c #include <stdio.h> int main() { char ca[10]; // 数组元素类型是 char int ia[5]; // 数组元素类型是 int char *pca[8]; // 数组元素类型是 char * int *aapi[4][5];//数组元素类型是 int *[5] int (*apai[4])[5];//数组元素类型是 int (*)[5]) printf(" sizeof(aapi[0])=%lu, %lu\n", sizeof(aapi[0]), sizeof(int *[5])); printf(" sizeof(apai[0])=%lu, %lu\n", sizeof(apai[0]), sizeof(int (*)[5])); return 0; }
例子中,前三个类型都比较容易看明白,int *[5]
和 int (*)[5]
分别是什么类型?int *[5]
类型表示一个数组,每个数组元素都是int *
类型。int (*)[5]
类型表示一个指向包含5个int型元素的数组
的指针 。
✨1.5 数组元素的个数
C语言中,在定义数组时,都需要明确的给出数组元素个数。
在数组定义语句中,数组名后面的[]中的数字,就是数组元素的个数。
已int a[5];
为例子,数组个数可以用表达式:sizeof(a) / sizeof(a[0])
来获得。
🎄二、数组的几个地址—— a、&a、&a[0]
当我们定义一个数组 a 时,编译器根据指定的元素个数和元素的类型分配确定大小(元素类型大小*元素个数)的一块内存,并把这块内存的名字命名为 a。名字 a 一旦与这块内存匹配就不能被改变。
a[0]、a[1]等为 a 的元素,但并非元素的名字。数组的每一个元素都是没有名字的。
- 数组名
a
:数组名a
作为地址使用时,表示数组首个元素的地址(指针),那它所指向的类型就是数组元素类型,加减一个整数就相当于加减(整数*sizeof(元素类型))
。
数组名a
可以看成一个指针常量,它的值不能被修改,不能单独作为左值使用。 &a
:&a
表示对编译器分配好内存的变量a取地址,得到的是整个数组的地址(指针),其所指向的类型是数组的类型,加减一个整数就相当于加减(整数*sizeof(数组的类型))
。&a[0]
:a[0]
是数组的首个元素,&a[0]
是对数组首个元素取地址,得到的是数组首个元素的地址(指针),那它所指向的类型就是数组元素类型,加减一个整数就相当于加减(整数*sizeof(元素类型))
。
看例子2.1:
// array_addr.c #include <stdio.h> int main() { int a[5];// 数组类型:int [5] ; 数组元素类型:int printf("a=%p a+1=%p %lx\n",a, a+1, (unsigned long)a+sizeof(int)); printf("&a=%p &a+1=%p %lx\n",&a, &a+1, (unsigned long)&a+sizeof(int [5])); printf("&a[0]=%p &a[0]+1=%p %lx\n",&a[0], &a[0]+1, (unsigned long)&a[0]+sizeof(int)); printf("a=%lu &a=%lu &a[0]=%lu\n",sizeof(a), sizeof(&a), sizeof(&a[0])); return 0; }
打印结果如下:
从结果看,可以得出结论:
a、&a、&a[0]
作为地址(指针)使用时,三个值都是相等的;a、&a、&a[0]
的指针类型不一样,&a的指针类型是数组的类型,a、&a[0]的指针类型是数组元素类型,最后进行指针运算时,a、&a[0]的结果是一样的,与&a存在差别;a、&a、&a[0]
进行sizeof计算时,sizeof(a)是这个数组大小,其余两个是指针的大小(32位系统为4,64位系统为8)。
🎄三、不指定数组长度——int a[]={1,2};
在C语言中,可以使用不指定数组长度的方式来定义和初始化数组。这种情况下,编译器会根据提供的初始化值自动推断数组的长度。
例如,代码int a[]={1,2};
定义了一个整型数组a,并用初始值1和2进行了初始化。由于未指定数组长度,编译器会根据提供的初始化值计算数组的大小。
在这个例子中,由于提供了两个初始化值,编译器会推断数组长度为2,因此数组a将具有两个元素:a[0]和a[1]。其值分别为:
a[0] = 1 a[1] = 2
通过这种方式,可以方便地定义和初始化具有不同长度的数组,而无需显式指定数组的长度。但是请注意,这种隐式推断数组长度的方式只适用于在声明时进行初始化的静态和自动(非堆)数组。对于动态分配的数组,仍然需要显式指定数组的长度。
总结起来,使用不指定数组长度的方式定义和初始化数组是C语言中的一种常见用法,编译器会根据提供的初始化值来推断数组的长度。
🎄四、数组初始化
在C语言中,主要有3种常见的方式可以初始化数组。下面列举了其中的几种方式,并提供了相应的示例:
- 1、在定义数组时,给各个元素初始化。
int a[5] = {1, 2, 3, 4, 5};
- 2、部分初始化:只为数组的一部分元素提供初始值,剩余元素会被设置为默认值(0)。
int a[5] = {1, 2}; // a[0]和a[1]被初始化为1和2,a[2]、a[3]和a[4]被初始化为0
- 3、不指定数组长度的方式定义和初始化数组。这是C语言中的一种常见用法,编译器会根据提供的初始化值来推断数组的长度。
int a[]={1,2}; // 初始化结束后,数组a的长度为2 char str[] = "Hello"; // 字符数组str会被初始化为包含"Hello"字符串的字符序列
这些是C语言中常见的数组初始化方式,你可以根据自己的需求选择适合的方式来初始化数组。
看例子4.1:
// array_init.c #include <stdio.h> int main() { int i=0; printf("no init:\n"); int a[5]; for(i=0; i<(sizeof(a)/sizeof(a[0])); i++) { printf("%d, ",a[i]);// 没初始化,打印随机值 } printf("\n\n"); printf("init1: init all unit\n"); int a1[5] = {1,2,3,4,5}; for(i=0; i<(sizeof(a1)/sizeof(a1[0])); i++) { printf("%d, ",a1[i]); } printf("\n\n"); printf("init2: init first unit\n"); int a2[5] = {5}; for(i=0; i<(sizeof(a2)/sizeof(a2[0])); i++) { printf("%d, ",a2[i]); } printf("\n\n"); printf("init3: Do not specify length\n"); int a3[] = {1,2,3,4,5}; for(i=0; i<(sizeof(a3)/sizeof(a3[0])); i++) { printf("%d, ",a3[i]); } printf("\n\n"); printf("init4: Specify some unit\n"); int a4[5] = {[1]=2, [3]=4}; for(i=0; i<(sizeof(a4)/sizeof(a4[0])); i++) { printf("%d, ",a4[i]); } printf("\n\n"); printf("init5: Do not specify length, and specify the unit\n"); int a5[] = {[1]=2, [3]=4}; for(i=0; i<(sizeof(a5)/sizeof(a5[0])); i++) { printf("%d, ",a5[i]); } printf("\n"); return 0; }
运行结果:
🎄五、字符串
C语言中的字符串,本质为字符数组,编译器自动在结尾加上 ‘\0’ 字符。
字符串字面值可以用来初始化字符数组:char str[]="abc";
字符串字面值存储于程序的全局只读存诸区,内容不可以修改,地址可以看出常量指针,指针类型是const char * const
。
字符串字面值的长度可以用strlen
函数来获取。
// array_str.c #include <stdio.h> #include <string.h> int main() { // 1、字符串字面值的地址、空间大小、字符串长度 printf("str_addr=%p str_size=%lu str_len%lu\n", (char*)"str", sizeof("str"), strlen("str")); // 2、字符串字面值给字符数组初始化 unsigned char str[100] = "12345"; // 3、字符串字面值是 `const char* const` 指针 printf("str_1=[%c] str_end=[%c]\n", *("str"+1), *("str"+strlen("str"))); return 0; }
运行结果:
🎄六、总结
本文详细地介绍C语言的数组,数组本质上是一段连续的内存空间,了解数组5个重要内容:数组所在的内存地址、数组的内容、数组的类型、数组元素的类型、数组元素的个数;然后介绍数组的几个地址—— a、&a、&a[0];数组的初始化、字符串等。
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁