目录

数组的理解

数组越界

数组作为函数参数


数组的理解

数组的含义

在C语言中,用于存储多个相同类型的元素。它可以被简单地定义为包含多个元素的容器。数组中每个元素都可以通过索引来访问,索引从零开始递增。 C语言中的数组可以包含任何基本数据类型,例如整数、字符、浮点数等。要定义一个数组,需要指定数组的类型和元素的数量。

数组的创建

type_t   arr_name   [const_n];

type_t 是指数组的元素类型

const_n 是一个常量表达式,用来指定数组的大小

例如:

int a[5];
char c[2];
double* ptr[10];

数组创建的其他几种实例情况

#include<stdio.h>
#define COUNT 10
enum Count {Count = 10,
};
int main()
{//写法一int count = 10;int arr[count];//写法二const int count = 10;int arr[count];//写法三int arr[COUNT];//写法四int arr[Count];return 0;
}

以上代码中包含了4种不同的数组定义方式,分别如下:

1. 使用变量定义数组大小:

```

int count = 10;

int arr[count];

```

此种方式定义数组的长度使用的是一个变量,变量count的值为10,因此定义了一个包含10个元素的数组。

但需要注意的是,在博主的vs编译器中,采用的是C90的标准(数组大小只能是常量表达式),并不支持C99的标准(引入变长数组的概念,使得数组在创建的时候可以使用变量,但是不可以被初始化),所以这样的变量长度的数组会被编译器判定错误。

而在gcc编译器环境下,它是支持C99标准的特性,就允许这样的写法。

2. 使用常量定义数组大小:

```

const int count = 10;

int arr[count];

```

此种写法只是将变量count改为了常量count,但是count还是常量,只不过它拥有了常量属性,本质还是变量,原因和第一种方法相同。

3. 使用宏定义定义数组大小:

```

#define COUNT 10

int arr[COUNT];

```

通过宏定义,我们将数组大小定义为一个常量,因此在整个程序中都可以使用宏定义的值。此种方式定义的数组大小,与使用常量定义的数组大小基本相同。

4. 使用枚举定义数组大小:

```

enum Count

{

Count = 10,

};

int arr[Count];

```

使用枚举定义数组大小与使用宏定义相似,只不过使用的是枚举类型。其中,我们定义了一个名为Count,值为10的枚举常量,然后通过Count来定义数组大小。最终,以上四种方式定义的数组类型都是相同的,都定义了一个包含10个元素的数组。

总结:数组创建,在C99标准之前,[ ]([ ]内的值必须是整型)中要给一个常量、宏定义或者枚举常量才可以,不能使用变量。在C99标准支持了变长数组的概念,数组的大小可以使用变量指定,但是数组不能初始化。

数组的初始化

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

int arr1[5] = {1,2,3,4,5};//完全初始化
int arr2[10] = {1,2,3};//不完全初始化,剩余的元素都是0
int arr3[] = {1,2,3,4};//省略数组的大小,数组会根据初始化的内容来确定
int arr4[3] = {0};//省略数组的内容,就必须要指定数组的大小//下面解释
char arr5[] = {'a','b','c'};
char arr6[] = "abc";

数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。

现在我们来看数组中字符数组在创建为什么有两种形式(char arr5[] 和 char arr6[ ] ),以及在内存是怎么分配的。

```

char arr5[] = {'a','b','c'};

```

这种方式就是简单的使用{ }进行初始化 。

```

char arr6[] = "abc";

```

这种创建char数组的方式叫做字符串字面值初始化,可以简化char数组的定义和初始化。

由两张图我们可以得出:

第二种写法等价于:

```

char arr6[] = {'a', 'b', 'b', '\0'};

```

注意:两种写法有区别,第二种写法比第一种写法多了'\0',我们以后创建使用的时候不能混淆了。

一维数组的使用

数组访问的操作符: [ ]

下标从'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;
}

总结:

1. 数组是使用下标来访问的,下标是从0开始。

2. 数组的大小可以sizeof关键字通过计算得到。

一维数组在内存中的存储

上面我们打印的是数组每个元素的内容,现在我们来打印一下数组每个元素的地址

//输出数组每个元素的地址for (i = 0; i < 10; ++i){printf("[%d]==%p\n", arr[i], &arr[i]);}

总结:数组在内存中是连续存储的。

二维数组的含义

二维数组是一种数组类型,它可以存储具有相同数据类型的元素值,

但是以二维网格的形式存储。

例如:

二维数组由行和列组成,其中每个元素可以通过其行和列的索引来访问。通常,我们使用两个下标来访问二维数组的元素,第一个下标表示要访问的行数,第二个下标表示要访问的列数。

二维数组的创建

int arr[3][4];

char arr[3][5];

double arr[2][4]

二维数组的初始化

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

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

int arr[][4] = {{2,3},{4,5}};//二维数组如果有初始化,行可以省略,列不能省略

二维数组的使用

二维数组的使用也是通过下标的方式,行下标和列下标都从0开始。

#include <stdio.h>
int main()
{int arr[3][4] = { 0 };int i = 0;for (i = 0; i < 3; i++)//行下标{int j = 0;for (j = 0; j < 4; j++)//列下标{arr[i][j] = i * 4 + j;}}for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 4; j++){printf("%d ", arr[i][j]);}}return 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]);}
}

二维数组初始化为什么可以省略行,不能省略列

二维数组是由多个一维数组按照行排列而成的,每一个一维数组被称为一行,它们在内存中是连续存放的,行与行之间是相互独立的。当我们初始化一个二维数组时,需要指定其行数和列数。

对于指定行数,我们可以采用以下两种方式:

1. 显示指定行数

```

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

```

这种方式明确指定了行数为2。

2. 隐式指定行数

```

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

```

这种方式没有指定行数,但是编译器会根据初始化中元素的个数来计算出行数,这里由于有两个一维数组,每个一维数组包含3个元素,所以行数为2。

对于指定列数,我们需要显式指定数组列数,因为在分配内存空间时,需要先知道每一行应该分配多少个元素。如果省略了列数,则编译器无法确定每一行应该分配多少个元素。因此,初始化二维数组时可以省略行数,但必须指定列数。

数组越界

        数组的下标是有范围限制的。 数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。 所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。 C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就 是正确的, 所以我们在写代码时,最好自己做越界的检查。

#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int i = 0;for(i=0; i<=10; i++){printf("%d\n", arr[i]);//当i等于10的时候,越界访问了}return 0;
}

这段代码定义了一个长度为10的整型数组,并将其初始化为1到10的连续数字。然后通过循环遍历数组,依次输出数组中的元素,但是需要注意的是,在循环中判断的条件是 `i<=10`,这意味着循环将会执行11次,而数组下标从0开始,最大只能取到9。

因此,在循环的最后一次迭代中,`i`等于10,此时访问了数组下标为10的元素,即超出了数组的范围,这个错误我们称之为数组越界访问

越界访问是一种常见的编程错误,它可能导致程序崩溃、产生不可预知的结果或安全隐患等问题。为了避免越界访问,我们需要在访问数组元素时,保证数组下标的合法性,即数组下标不能小于0,且不能大于等于数组长度。所以代码中应该把循环判断条件修改为 `i<10`。

数组作为函数参数

在C语言中,我们可以将数组作为函数的参数来传递。当我们将一个数组作为参数传递给函数时,实际上传递的是数组的地址。在函数中,我们可以通过传递进来的地址来访问数组中的元素,对数组进行读取、修改等操作。

数组名怎么理解

数组名通常情况下就是数组的首元素的地址。
但是有两个例外:
        1.sizeof(数组名),数组名单独放在sizeof()内部,这里的数组名表示整个数组,计算的是整个数组的大小。
        2.&数组名,这里的数组名表示整个数组,这里取出的是整个数组的地址。

我们来用函数作为参数写一个冒泡排序实现整形数组排序。

#include <stdio.h>
void bubble_sort(int arr[])
{int sz = sizeof(arr) / sizeof(arr[0]);//这样对吗?int i = 0;for (i = 0; i < sz - 1; i++)//比较的趟数{int j = 0;for (j = 0; j < sz - 1 - i; j++)//两两比较的次数{if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main()
{int arr[] = { 3,1,7,5,8,9,0,2,4,6 };bubble_sort(arr);//是否可以正常排序?int i = 0;for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}return 0;
}

在这段代码中,定义了一个名为 `bubble_sort` 的函数,它接受一个整型数组 `arr` 作为参数。在函数内部,使用双重循环实现了冒泡排序算法,将传入的数组进行排序。

在 `main` 函数中,定义并初始化了一个整型数组 `arr`,然后将其作为参数传递给 `bubble_sort` 函数进行排序。

然而,这段代码存在一个问题:在 `bubble_sort` 函数中,计算数组长度的方式不确。 在 C 语言中,数组作为函数参数时,我们只能传递数组的地址,也就是指向数组第一个元素的指针。因此,当 `bubble_sort` 函数被调用时,传递给它的是数组的地址,而不是数组本身的大小。因此,`sizeof(arr)` 将会返回数组指针的大小,而不是整个数组的大小。我们试着打印一下发现结果确实是4,并且程序也提示警告。

为了解决这个问题,我们可以在调用函数时将数组的长度一起传递给函数,或者在函数内部使用另一个参数来表示数组的长度。在这段代码中,我们可以在定义函数时加上一个表示数组长度的参数 `int len`,然后在调用函数时将数组的长度传递给它。

修改后的冒泡排序算法:

#include <stdio.h>
//void bubble_sort(int *arr,int sz)
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 - 1 - i; 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 sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr,sz);int i = 0;for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}return 0;
}

在代码中,用一个flag变量来表示当前的数组是否已经有序了。在每一轮比较之前,将flag值赋为1,表示当前的数组已经有序。如果在比较的过程中发现有元素需要进行交换,那么就将flag值改为0,表示当前的数组无序。 在每一轮比较结束之后,检查flag的值。如果flag值还是1,说明这一轮比较过程中没有进行任何元素的交换,也就是整个数组已经有序了。此时就可以直接退出排序循环,不再进行无用的比较,从而提高排序的效率。这个优化方法能够有效地减少排序循环的次数,尤其在处理大数据量时效果更为明显。因为如果数组已经有序,那么不必再进行多余的比较操作,这样可以大大减少算法的时间复杂度

C learning_11 (数组和在内存存储的理解、数组越界、数组作为形参)相关推荐

  1. python 申请内存空间、用于创建多维数组_python 申请内存空间,用于创建多维数组的实例...

    以三维数组为例 先申请1个一维数组空间: mat = [None]*d1 d1是第一维的长度. 再把mat中每个元素扩展为第二维的长度: for i in range(len(mat)): mat[i ...

  2. 数组的存储与初始化、对象数组、数组作为函数参数

    数组的存储 数组在内存中是一组连续的内存单元,也就是说数组元素是连续存储的.数组名是数组所占内存的首地址. 一维数组是按照下标的顺序存储的,而对多维数组就复杂些,以一定的约定顺序将多维数组存储在连续的 ...

  3. 二维数组及其二维数组的动态内存分配

    本文为大一时所写的文章(2017/4/9),文笔还很生疏,在很多问题上认识不深,算是在学校的微信公众号上的一个编程探究模块上的投稿,本人当时也参与了本模块的维护和管理.补档. 今天我们来聊聊二维数组及 ...

  4. 二维数组及其动态内存分配

    一.二维数组的基本概念 1.1 二维数组的内存映像 从内存角度看,二维数组和一维数组一样,在内存中都是连续分布的多个内存单元,并没有本质差别,只是内存的管理方式不一样,如下图所示 一维数组int a[ ...

  5. PHP如何生成大数组「考虑内存」

    PHP如何生成大数组「考虑内存」 PHP如何生成大数组「考虑内存」 1.我们先来看一下,如果要生成1000000个元素的数组,对内存的消耗: 2.使用生成器原理生成数组对象的话,如下: PHP如何生成 ...

  6. Golang——数组遍历、最大值、求和、多维数组

    数组: 数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致. 数组定义完成后,可以对数组进行赋值操作.数组是通过下标来进行操作的,下标的范围是从0开始到数组长度减1的位置. 特点: 数组是一 ...

  7. java 二维数组存储方式_JAVA-初步认识-第六章-二维数组-定义方式内存图解

    一. 接下讨论数组中特殊的一部分.数组是一个容器,用来存储数据的.现在数组中存储的不再是int,double..的类型了,而是存储的数组. 数组中的元素还是数组,我们把它称之为数组中的数组,也就做二维 ...

  8. 简要叙述matlab的含义,1,简述MATLAB组成部分? 2,说明使用M文件编辑/调试器的方法和优点? 3,存储在工作空间中的数组能编辑吗...

    匿名用户 1级 2012-05-17 回答 我也考这个....祝你好运 1,简述MATLAB组成部分? (1)开发环境(development Environment); (2)MATLAB数学函数库 ...

  9. java存储整数_关于数组:Java:存储大量整数的最佳数据类型是什么?

    我必须处理大量的小整数,最大为1700万(值始终在0-255之间),并将它们存储在某种类型的数组上,我目前使用的是普通的int数组,并且性能不是最好的( 符合预期). 每次执行时,程序都会访问该数组的 ...

最新文章

  1. 根据16S预测微生物群落功能最全攻略
  2. git查看某个文件的修改历史
  3. 如何生成随机不重复的11位数字
  4. 不要把a href=当作按钮用
  5. ArcEngine9.1结合VS2005开发技巧2则
  6. 01企业网络高级解决方案
  7. 全球及中国城市规划行业十四五建设方向与运营动态分析报告2022版
  8. JSCRIPT如何调试
  9. 苹果新专利针对骑自行车摔倒情况,苹果Find My使自行车免于丢失
  10. 一次精彩的皮卡车降噪试验过程
  11. SQL语句的基础教程(二)
  12. 财路网每日原创推送:轻信微信上的投资专家,男子亏损四十多万!
  13. 51单片机按键控制数码管显示0-9
  14. XMail 安装配置使用
  15. BCOP章鱼船长,6月22日晚上8点上线薄饼
  16. 微信小程序Canvas绘制主页保存到手机相册
  17. Vulnhub渗透测试 DR4G0N B4LL: 1
  18. EmailCamel+Mail-tester检测及优化邮件内容评分,防止进垃圾箱!
  19. List中remove()的用法
  20. 2018大公司面试分享(百度,京东,搜狗,小米等)

热门文章

  1. SQL Server 练习题(初学)
  2. java试题库管理系统源代码_Java试题库管理源代码
  3. maven项目打包报错Failed to execute goal org.apache.maven.plugins:maven-install-plugin:2.4:install (default
  4. iapp小钢琴程序代码
  5. WIFI系列协议--802.11v--无线网络管理
  6. ThinkPHP55.1验证码的使用及点击刷新
  7. OpenSSL-3.0.3编程—封装EVP摘要计算为C++类EvpDigest
  8. html页面改成jsp后IE和360浏览器不兼容问题
  9. 安卓 动画的深入分析
  10. JavaScript - 判断浏览器内的页面是在手机端还是电脑(PC)上打开的(判断用户访问设备是什么)