那些年你还没学会的C语言数组小细节(和bug郭一起学C系列)

简介: 那些年你还没学会的C语言数组小细节(和bug郭一起学C系列)

一维数组

什么是数组呢?

我们之前学过数组是一组数据,方便记录数据,那到底什么才是数组呢,今天我们就来详细介绍一下!

数组的定义


数组是一组相同类型元素的集合。


相同类型元素的集合

我们回忆一下学过那些类型的数据

整型,浮点型,字符型

所以应该这些相同类型的元素的的集合就是数组


int short long long char double float

一维数组的创建和初始化

数组的创建


//数组的创建
int arr[2];
char arr1[5];
double arr2[5];

总结:数据类型+数组名[常量表达式];

数据类型:指定数组类型

数组名:就像我们的变量名一样

常量表达式:指定元素个数,必须是常量表达式


image.png

数组的初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。 看代码:


//数组的创建和初始化
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'};

image.png

可以看到数字初始化成字符串的形式,数组长度比,字符数组的形式多了一个\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;
}

image.png

注:


数组的第一个元素的下标并不是arr[1],而是arr[0],数组的下标是从0开始访问的,如果有n个元素,那么最后一个元素的下标应该是arr[n-1]

初始化的[]代表元素个数,而访问时[]代表的是下标。

一维数组在内存中的存储

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

可以看到数组arr在内存中的存储如下:

image.png

总结:


数组元素之间的地址是连续的且相差数组类型个字节(int 类型相差4个字节,char类型1个字节)

数组的第一个元素的地址就是数组的地址

数组在内存中是连续存放的

image.png

二维数组

二维数组本质上是以数组作为数组元素的数组,即“数组的数组”,类型说明符 数组名[常量表达式][常量表达式]。


二维数组的创建和初始化

二维数组的创建


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;
}

image.png


二维数组在内存中的储存

二维数组像一维数组一样,这里我们尝试打印二维数组的每个元素!

#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;
}

image.png

通过结果我们可以分析到,其实二维数组在内存中也是连续存储的。

image.png


数组传参

一维数组作为函数参数

往往我们在写代码的时候,会将数组作为参数传个函数,比如:我要实现一个冒泡排序函数,将一个整形数组排序


//方法一
#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]);
  }
}

image.png

方法1,出问题,那我们找一下问题,调试之后可以看到bubble_sort 函数内部的sz ,是1。

难道数组作为函数参数的时候,不是把整个数组的传递过去?

image.png

可以看到形参arr并不是数组,而是只有一个元素,sz=1,难道arr是地址?

image.png

可以看到在64位平台,sz=2验证了我们的猜测,arr就是地址,虽然形参是以arr[]数组的形式接收的,但还是地址,地址就只有4/8个字节。

所以数组长度只能在函数外部计算好传参进去!

数组名是什么

image.png


数组名是数组首元素的地址。(有两个例外)



如果数组名是首元素地址,那么:为什么输出的结果是: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]);
  }
}

image.png


我们已经知道,数组传参,数组名就是数组地址也是首元素地址,而我们用数组名接收或者数组接收得到的都是数组的地址;



         

既然我们用指针接收,那么我们就可以用指针的形式解引用!


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)!


二维数组传参

二维数组,传参的方式和一维数组一样,但是数组名就不是,首元素地址了,而是首行地址,我们来验证一下。

image.png

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);
}

image.png

总结


像二维数组的初始化一样,二维数组的形参行数不能省略。

二维数组的数组名是第一行的行指针。

arr[i][j]等价于*( *(arr+i)+j),*(arr+i)解引用找到行指针存放的一行元素(一维数组)的地址,*( *(arr+i)+j)解引用找到该元素!

目录
相关文章
|
1月前
|
C语言
在C语言中数组作为函数参数的应用与示例
在C语言中数组作为函数参数的应用与示例
15 0
|
1月前
|
C语言
【进阶C语言】数组笔试题解析
【进阶C语言】数组笔试题解析
17 0
|
1月前
|
存储 C语言 索引
C语言数组
C语言数组
14 0
|
1月前
|
存储 算法 数据挖掘
C语言中如何快速找出数组最大值下标
C语言中如何快速找出数组最大值下标
|
1月前
|
C语言 开发者
C语言中如何精确实现数组元素的插入
C语言中如何精确实现数组元素的插入
|
1天前
|
存储 C语言
C语言中字符串的引用与数组元素操作
C语言中字符串的引用与数组元素操作
10 0
|
21天前
|
编译器 程序员 C语言
【C语言】变长数组,二分查找和数组之间自动替换的实现
【C语言】变长数组,二分查找和数组之间自动替换的实现
|
21天前
|
存储 C语言
【C语言数组】创建、初始化、以及使用2
【C语言数组】创建、初始化、以及使用
|
1月前
|
存储 程序员 C语言
C语言中的结构体数组
C语言中的结构体数组
9 0
|
1月前
|
存储 C语言 索引
C语言一维数组
C语言一维数组
13 1