那些年你还没学会的C语言数组小细节(和bug郭一起学C系列)
失踪人口回归!互访,互粉,互相支持,谢谢大佬的指点!
@TOC
本章重点
- 一维数组的创建和初始化
- 一维数组的使用
- 一维数组在内存中的存储
- 二维数组的创建和初始化
- 二维数组的使用
- 二维数组在内存中的存储
- 数组作为函数参数
一维数组
什么是数组呢?
我们之前学过数组是一组数据,方便记录数据,那到底什么才是数组呢,今天我们就来详细介绍一下!
数组的定义
数组是一组相同类型元素的集合。
相同类型元素的集合
我们回忆一下学过那些类型的数据
整型,浮点型,字符型
所以应该这些相同类型的元素的的集合就是数组
int short long long char double float
一维数组的创建和初始化
数组的创建
//数组的创建
int arr[2];
char arr1[5];
double arr2[5];
总结:数据类型+数组名[常量表达式];
数据类型:指定数组类型
数组名:就像我们的变量名一样
常量表达式:指定元素个数,必须是常量表达式
数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。 看代码:
//数组的创建和初始化
int arr1[2]={1,3};
int arr2[] = {1,2,3,4}; //未定义数组个数由初始化元素个数确定
int arr3[5] = {1,2,3,4,5};
char arr4[3] = {'a',98, 'c'};
char arr5[5]={'a','c','d','e','f'};
float arr5[3]={3.14,1.3,6.7};
总结:
- 数组在创建的时候如果想不指定数组的确定的大小就得初始化。
- 数组的元素个数根据初始化的内容来确定。
但是对于下面的代码要区分,内存中如何分配?
//字符数组
char arr1[]="abcdef"; //字符串含'\0';
char arr2[]={'a','b','c','d','e','f'};
可以看到数字初始化成字符串的形式,数组长度比,字符数组的形式多了一个\0
的长度!
一维数组的使用
对于数组的使用我们之前介绍了一个操作符:[]
,下标引用操作符。它其实就数组访问的操作符。 我们来看代码:
#include <stdio.h>
int main()
{
int arr[10] = {0};//数组的不完全初始化
//计算数组的元素个数
int sz = sizeof(arr)/sizeof(arr[0]);
//对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以:
int i = 0;//做下标
for(i=0; i<10; i++)//这里写10,好不好?
{
arr[i] = i;
}
//输出数组的内容
for(i=0; i<10; ++i)
{
printf("%d ", arr[i]);
}
return 0;
}
注:
- 数组的第一个元素的下标并不是
arr[1]
,而是arr[0]
,数组的下标是从0
开始访问的,如果有n
个元素,那么最后一个元素的下标应该是arr[n-1]
- 初始化的
[]
代表元素个数,而访问时[]
代表的是下标。
一维数组在内存中的存储
int arr[10]={0,1,2,3,4,5,6,7,8,9};
可以看到数组arr
在内存中的存储如下:
总结:
- 数组元素之间的地址是连续的且相差数组类型个字节(
int
类型相差4
个字节,char
类型1
个字节) - 数组的第一个元素的地址就是数组的地址
- 数组在内存中是连续存放的
二维数组
二维数组本质上是
以数组作为数组元素的数组
,即“数组的数组”,类型说明符 数组名[常量表达式][常量表达式]
。
二维数组的创建和初始化
二维数组的创建
int arr1[5][5];
char arr2[3][5];
double arr[2][3];
二维数组的初始化
//从第一个元素开始初始化,未初始化的元素默认初始为0
int arr1[2][3]={1,2,3,4};
//按照每行初始化,每行未初始化的元素默认初始为0,
int arr2[2][3]={{1,2},{3,4}};
//根据初始化的行数确定数组的行数
int arr3[][3]={{1,2},{3,4}};
注:初始化时,行号可以省略,列号不可省略
二维数组的使用
#include<stdio.h>
int main(void)
{
int i = 0, j = 0;
//初始化
int arr[4][4] = { {1,2,3},{4,5,6},{7,8,9} };
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
//换行
printf("\n");
}
return 0;
}
二维数组在内存中的储存
二维数组像一维数组一样,这里我们尝试打印二维数组的每个元素!
#include <stdio.h>
int main()
{
int arr[3][4];
int i = 0;
for(i=0; i<3; i++)
{
int j = 0;
for(j=0; j<4; j++)
{
printf("&arr[%d][%d] = %p\n", i, j,&arr[i][j]);
}
}
return 0;
}
通过结果我们可以分析到,其实二维数组在内存中也是连续存储的。
数组传参
一维数组作为函数参数
往往我们在写代码的时候,会将数组作为参数传个函数,比如:我要实现一个冒泡排序函数,将一个整形数组排序
//方法一
#include<stdio.h>
bubble_sort(int arr[])
{
int i=0,j=0;
int sz=sizeof(arr)/sizeof(arr[0]);
int flag=0;
for(i=0;i<sz-1;i++)
{
for(j=0;j<sz-1-i;j++)
{
if(arr[j]<arr[j+1])//降序
{
flag=1;
int tmp=0;
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
if(flag==0)
break;
}
}
}
int main()
{
int arr[]={1,3,5,7,2,6,4,8,10,9};
bubble_sort(arr);//是否可以正常排序?
int i=0;
for(i=0;i<10;i++)
{
printf("%d ",arr[i]);
}
}
方法1,出问题,那我们找一下问题,调试之后可以看到bubble_sort
函数内部的sz
,是1
。
难道数组作为函数参数的时候,不是把整个数组的传递过去?
可以看到形参arr
并不是数组,而是只有一个元素,sz=1
,难道arr
是地址?
可以看到在64
位平台,sz=2
验证了我们的猜测,arr
就是地址,虽然形参是以arr[]
数组的形式接收的,但还是地址,地址就只有4/8
个字节。
所以数组长度只能在函数外部计算好传参进去!
数组名是什么
结论:
数组名是数组首元素的地址。(有两个例外)
如果数组名是首元素地址,那么:为什么输出的结果是:40
?
补充:
sizeof
(数组名),计算整个数组的大小,sizeof
内部单独放一个数组名,数组名表示整个数组。
2.&
数组名,取出的是数组的地址。&
数组名,数组名表示整个数组。
除此1,2
两种情况之外,所有的数组名都表示数组首元素的地址。
//改进后
#include<stdio.h>
void bubble_sort(int arr[],int sz)
{
int i=0,j=0;
int flag=0;
for(i=0;i<sz-1;i++)
{
for(j=0;j<sz-1-i;j++)
{
if(arr[j]<arr[j+1])//降序
{
flag=1;
int tmp=0;
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
if(flag==0)
break;
}
}
}
int main()
{
int arr[]={1,3,5,7,2,6,4,8,10,9};
int sz=sizeof(arr)/sizeof(arr[0]);
bubble_sort(ar,sz);//是否可以正常排序?
int i=0;
for(i=0;i<sz;i++)
{
printf("%d ",arr[i]);
}
}
我们已经知道,数组传参,数组名就是数组地址也是首元素地址,而我们用数组名接收或者数组接收得到的都是数组的地址;
//函数调用
bubble_sort(arr,sz);
//以数组接收1
bubble_sort(int arr[],int sz);
//以指针接收2
bubble_sort(int* arr,int sz);
既然我们用指针接收,那么我们就可以用指针的形式解引用!
void bubble_sort(int* arr,int sz)
{
int i=0,j=0;
int flag=0;
for(i=0;i<sz-1;i++)
{
for(j=0;j<sz-1-i;j++)
{
// if(arr[j]<arr[j+1])//降序
if(*(arr+j)<*(arr+j+1))
{
flag=1;
int tmp=0;
tmp=*(arr+j);
*(arr+j)=*(arr+j+1);
*(arr+j+1)=tmp;
}
if(flag==0)
break;
}
}
}
我们可以知道arr[1]
等价于*(arr+1)
!
二维数组传参
二维数组,传参的方式和一维数组一样,但是数组名就不是,首元素地址了,而是首行地址,我们来验证一下。
arr+1
后地址向后走了16
个字节,所以当我们二维数组以数组名传参后得到的是行指针。像一维数组一样,而我们肯定还要将二维数组的行列数传参才能控制二维数组。
//实现二维数组的打印
print(int arr[][3],int x,int y)
{
int i=0,j=0;
for(i=0;i<x;i++)
{
for(j=0;j<y;j++)
{
printf("%d ",arr[i][j]);
//printf("%d ",*(*(arr+i)+j)); //地址的形式
}
printf("\n");
}
}
int main()
{
int arr[4][3]={1,3,4,5,6,7,5,7,2,7,1,0};
print(arr,4,3);
}
总结
- 像二维数组的初始化一样,二维数组的形参行数不能省略。
- 二维数组的数组名是第一行的行指针。
arr[i][j]
等价于*( *(arr+i)+j)
,*(arr+i)
解引用找到行指针存放的一行元素(一维数组)的地址,*( *(arr+i)+j)
解引用找到该元素!
- 点赞
- 收藏
- 关注作者
评论(0)