C语言基础篇--数组

一、数组的定义

在C语言中,可以使用以下方式定义数组:

  1. 1.定义数组并指定长度

 //定义一个长度为5的整数数组

int arr[5];

之后可逐个赋值:

arr[0]=1;

arr[1]=2;

```

arr[4]=5;

  1. 2.定义数组并初始化

int num[] = {1,2,3};

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

char str[] = "Hello";

  1. 3.指定数组长度但不完全初始化元素

//初始化部分元素,其余元素默认为0;

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

//定义字符数组时,可以用ASII码值代替字符:

char arr[5] = {'a',98};  ==  char arr[5] = {'a','b'}; 

  1. 4.定义多维数组

//定义一个二维整数数组

int mat[3][3];

//使用嵌套大括号

int array[3][5] = {

                   {1, 2, 3, 4, 5},

                   {6, 7, 8, 9, 10}, 

                   {11, 12, 13, 14, 15}   

                 };

  1. 5.动态分配内存

type *arrayName = malloc(sizeof(type) * elements);

- type 指数组元素的类型,如 int、char 等

- arrayName 是数组的指针名称

- malloc() 分配 sizeof(type) * elements 个字节的内存,elements 为元素个数

例如:

int *numbers = malloc(sizeof(int) * 5);  // 分配 5 个 int 型元素的内存

char *chars = malloc(sizeof(char) * 26); // 分配 26 个 char 型元素的内存

``` 

这实际上就是为数组分配内存,并用指针 arrayName 来访问数组元素。

如使用 numbers 访问数组:

numbers[0] = 1;

numbers[1] = 2;

...

numbers[4] = 5;

  • 使用完数组后需要使用 free()释放数组内存:

      free(numbers);

      free(chars);

  • 这个做法的优点是:

- 可在运行时指定数组大小 

- 不需要在编译时事先知道数组大小 

- 数组大小可动态调整

  • 缺点是:

- 需要手动 free() 释放内存,否则会内存泄漏

- 分配失败需要检查 malloc() 的返回值

总的来说,用malloc() 分配数组内存可以在运行时指定数组大小。需要谨慎释放内存,否则会内存泄漏。

定义数组时不能既不初始化值也不规定数组大小,必须至少满足以下两个条件之一:

  • 提供初始值来初始化数组,让编译器推断数组大小

       int arr[] = {1,2,3};  //数组大小推断为3

  • 直接指定数组大小

      int arr[5];  //数组大小为5

注意:

  1. 需要注意的是,数组的索引从0开始,因此访问数组元素时,索引范围应为0到数组长度减1。

    另外,C语言中的数组是一块连续的内存区域,用于存储相同类型的数据。可以通过索引访问和修改数组中的元素

二、数组的显隐式初始化

  • 隐式初始化

指在声明变量时,使用默认的初始化规则(赋值为0)为变量赋予初始值,而不显式地指定初始值。

  • 隐式初始化 

指在声明变量时明确指定初始值的操作。

 三、数组的存储

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

在C语言中,数组的内存分配取决于数组的声明方式。

以下举两个例子:

  1. char arr1[] = "abc";

编译器会根据字符串的长度为数组分配足够的内存空间,并在末尾自动添加一个空字符'\0',表示字符串的结束符。

因为字符串"abc"包含3个字符,所以编译器会为`arr1`分配4个字节的内存空间(3个字符+1个空字符)。内存布局如下所示:

+------+------+------+------+

| 'a'      |  'b'   |  'c'   |  '\0'  |

+------+------+------+------+

|  arr1[0] | arr1[1] | arr1[2] | arr1[3] |

+------+------+------+------+

  1.        2.char arr2[3] = {'a','b','c'};
  2. 我们明确指定了数组的大小为3。编译器会为`arr2`分配3个字节的内存空间,并将指定的字符分别存储在数组的对应位置。

    没有自动添加空字符'\0',因为我们使用了显式初始化。内存布局如下所示:

    +------+------+------+

    | 'a'     |  'b'   |  'c'  |

    +------+------+------+

    | arr2[0] | arr2[1] | arr2[2] |

    +------+------+------+

    需要注意的是,由于数组的索引从0开始,所以`arr1`和`arr2`的元素索引范围都是0到2。

  3. 那么,什么情况下,编译器会在数组末尾自动添加空字符呢?

  4. 1.定义字符串字面值初始化的字符数组

  5. char greeting[] = "Hello";

    // 等效于

    char greeting[] = {'H', 'e', 'l', 'l', 'o', '\0'};

  6. 2.使用scanf()读取字符串到字符数组

  7. char str[100];

    scanf("%s", str);   //%s—按字符串格式读取

  8. scanf()需要’\0‘作为字符串的结束标志,才知道停止读取输入

即使 str 数组有 100 个元素,使用 scanf("%s", str) 读取字符串时,实际最多只能读取 99 个字符

scanf()读取到第99个数组时,会自动添加一个\0作为字符串的结束。

3.使用strcpy()/strcat()将字符串复制到字符数组中

strcpy(str, "Hello");

strcpy() 会这样做:

  1. 复制字符串中的前 99 个字符到 str 数组中
  2. 再在第 100 个位置(即下标为 99)添加空字符 \0

对比:

  1. scanf() 是读入字符串的过程中动态地添加'\0'
  2. strcpy() 是复制完字符串后,再附加'\0' 加到数组末尾
  3. 都只能识别到前99个元素

4.将字符串作为函数参数传递给函数

void printStr(char str[]) {

}

printStr("Hello");

// 等效于

printStr("Hello\0");

```

传递字符串字面值时,编译器会自动添加'\0'

总结:

这些情况下,编译器自动在字符数组末尾添加空字符'\0',是为了表示字符串的 ending(结束)。

'\0' 使得字符串函数如strlen()、strcpy()等能正确处理字符串。

所以,只有定义存储字符串的数据时,编译器才会自动在末尾添加'\0'。

定义普通字符数组时,不会添加'\0':

如:char chars[] = {'h', 'e', 'l'};  // 正常字符数组,无'\0'

```

四、 二维数组

【二维数组的存放也是连续的】

为了便于理解,我们可以将二维数组看作一维数组。

比如:  int arr[3][4];    //三行四列

  • 二维数组的初始化

  • 规则:第一维可以省略,第二维不可以省略。(即行可以省略,但是列必须初始化)

 

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

此数组是三行四列,第五个元素会自动填补到第二行。 

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

当第一行的元素不够时,自动补0。

打印二维数组

【通过下标访问】

打印每一个元素的地址:

printf("&arr[%d][%d] = %p\n", i , j , &arr[i][j]);

五、数组作为函数参数

 往往写代码时,会将数组作为参数传给函数,比如:想实现一个冒泡排序函数将一个整型数组排序。

  • 冒泡排序

它通过多次迭代比较相邻的元素,并依次交换它们的位置,使得较大(或较小)的元素逐渐向数组的一端移动,从而达到排序的目的。

如果有10个元素相比较,则需要9趟冒泡排序。(n-1

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

void boubble_sort(int arr[],int sz)
{
	//确定冒泡排序的趟数
	int i = 0;

	//为了避免已经有序的数据重复排序浪费资源,使用flag优化程序
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;//假设这一趟排序的数据已经有序
		//每一趟冒泡排序
		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;
				flag = 0;//本次排序不完全有序
			}
		}
		if (flag == 1)
		{
			break;//flag 保持为1,说明整个数组已经有序
			//break总是跳出离它最近的内层循环。这里跳出的是外层i for循环
		}
	}
}

int main()
{
	int arr[] = { 9,8,7,12,40,32,3,2,1,0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//对arr经行排序,排成升序
	boubble_sort(arr,sz);//冒泡排序函数
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;

}

其中,使用了flag对程序进行优化,避免浪费资源。

六、数组名的含义和用法

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

例外:

  1. sizeof(数组名)—数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
  2. &数组名  ,数组名代表整个数组, &数组名取出的是整个数组的地址

    除了这两种情况,所有的数组名都表示数组首元素的地址。

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

      arr  --  首元素地址  

     &arr[0]  --  首元素地址

     &arr  --  整个数组的地址

    注意:   arr == &arr[0]

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值