【C/C++】系列文章目录
第三节【C/C++】初识C语言语句、函数、数组、运算符、关键字
文章目录
前言
本章节为前述章节数组部分的补充。涉及一维、二维数组内存存储,传参,sizeof,strlen对于计算字符数组的区别。数组名的含义及特殊使用场景。
提示:以下是本篇文章正文内容,下面案例可供参考
一、数组是什么?
数组是一组相同类型元素的集合,每个元素都有相同的数据类型,占有相同大小的存储单元。
二、一维数组
1.定义
类型说明符 数组名[常量表达式];
类型说明符指定数组元素的类型,常量表达式指定数组元素的个数,必须是一个整数。
int main()
{
int temp[10];//定义数组temp,有10个元素,元素类型是整型int
char str[20];//定义数组str,有20个元素,元素类型是字符型char
float arr[5];//定义数组arr,有5个元素,元素类型是浮点型float
return 0;
}
C99语法支持边长数组,VS2019不支持变长数组。 gcc编译器对c99支持的比较好。
字符数组定义时设置了数组大小为5,而初始化时只设置了两个字符,剩余的会自动填充为'\0'。
而从ch2也看出“”字符串末尾自动追加了'\0'。
ch3这种情况不自动追加'\0'。
2.strlen和sizeof计算字符串
注意区分strlen和sizeof。char类型一个字符大小为1个字节。
strlen遇到'\0'停止,计算字符串长度不计算'\0'。
sizeof数组设置了几个元素就按元素个数计算数组大小。
不设置大小的字符串数组,按初始化数据分配内存空间大小。ch2设置了三个字符’h','a','\0'。所以sizeof计算是3。
3.一维数组在内存中的存储
打印下字符串数组各个元素的地址。
可以看出 各元素是连续存放的。
下标从0开始,从低到高,随着下标的整张,地址是由低到高变化的。
三、二维数组
1.定义
类型说明符 数组名[常量表达式1][常量表达式2];
类型说明符指定数组元素的类型,常量表达式1指定数组行数,常量表达式2指定数组列数。
2.初始化
int main()
{
//完全初始化
int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
//不完全初始化
int arr2[3][4] = { 1,2,3,4,5 };
//不完全初始化
int arr3[3][4] = { {1,2},{3,4},{5} };
return 0;
}
注意初始化时各元素的分配。
3.二维数组在内存中的存储
%p打印地址(地址是十六进制的)。
可以看出二维数组在内存中各元素也是连续存放的。每一行内元素连续,行与行之间也是连续的。
对于arr来说,它只有两个元素arr[0]和arr[1],只不过它的每个元素又都是一个拥有三个元素的一维数组。
C语言中二维数组在内存中按行顺序存放。
对于不给定数组个数的二维数组初始化,由二维数组元素在内存中的连续存放方式得出,定义二维数组的时候,行数可省略,列数不可省略,以防计算机无法计算应分配的内存空间大小。
int arr[][4] = {1,2,3,4,5};
四、数组越界
数组下标是从0开始的,n个元素的数组,最大下标为n-1。
所以数组的下标如果小于0,或者大于n - 1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查。
越界访问了,VS2019没有报错。
二维数组的行和列也可能存在越界。
五、数组作为参数
数组的数组名就是数组首元素的地址。
但是之前学到过计算数组大小使用的是sizeof(arr) / sizeof(arr[0]),所以数组名是数组首元素的地址有个两个例外:
- sizeof(数组名)时,数组名表示的是整个数组,计算得出的也是整个数组的大小,单位是字节。
- &数组名,取地址数组名如&arr时,数组名表示的是整个数组,取出的是整个数组的地址。
其他情况数组名都是数组首元素的地址。所以函数传参数组,无法在数组内部计算数组的大小。因为形参接收的是首元素的地址,一个地址存储大小也就4个字节,无法据此计算数组长度。所以数组传参要在外边计算好数组长度,一起传入参数。如下冒泡排序:
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
int flag = 1;
for (j = 0; j < sz - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
int main()
{
int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
&arr取得是一整个数组的地址,说明示例:
int main()
{
int arr[5] = { 1,2,3,4,5 };
printf("%p\n", &arr); //00D3FE2C
printf("%p\n", &arr + 1);//00D3FE40
return 0;
}
十六进制地址:00D3FE40 - 00D3FE2C = 00000014
换算为十进制值为1*16^1+4*16^0 = 20,就是一个数组的大小。
结论:
数组作为函数参数传参本质是传的数组首元素的地址,形参的本质是指针。
数组形式,void test(int arr[5],int sz)或void test(int arr[], int sz)。
void test2(int arr[3][4],int row, int col)或 void test2(int arr[][4],int row, int col)
指针形式,void test (int*pa),二维数组的有待学习后记录。
总结
一维数组定义,初始化,内存存储形式。二维数组定义,初始化,不完全初始化,内存存储形式。
sizeof,strlen对于字符数组的计算区别。 数组名的含义以及在sizeof和&运算符中的区别。冒泡排序。