终于学会数组的使用啦~~~------C语言数组学习笔记详解

简介: 终于学会数组的使用啦~~~------C语言数组学习笔记详解

前言

一、一维数组的创建和初始化

1.一维数组的创建

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

数组的创建方式:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//数组的创建方式:
int main() {
  int arr[8];   //创建一个整型数组,里面可以放8个整型数据
  char ch[10];  //创建一个字符型数组,里面可以放10个数据
  return 0;
}

注意:

a.C99标准前,数组的大小必须是常量表达式指定

b.C99标准中,引入变长数组的概念,变长数组中允许数组的大小用变量来指定

c.本编译器不支持C99标准中的变长数组

d.变长数组不可以初始化

e.非变长数组是可以初始化的,在创建的时候就可以初始化

2.一维数组的初始化

int main() {
  int arr1[10] = {1,2,3,4,5,6,7,8,9,10};   //完全初始化:有几个元素就把所有元素初始化
  int arr2[10] = {1,2,3,4,5};              //不完全初始化:初始化其中的前几个元素,其余元素的初始值默认为0
  //字符串数组的初始化
  char ch1[5] = {'a','b',99};               //字符串数组的不完全初始化
  char ch2[10] = "abcdef";                  //相当于拿7个字符初始化这个数组(因为字符串的结尾是\0),剩余的三个位置相当于默认放的是0
  //如果数组初始化了,,可以不指定数组的大小,数组的大小会根据初始化的内容来确定
  char ch3[] = "abc";             //ch3里面放了4个字符,a,b,c,\0,但是这个数组的长度为3,\0不计入数组的长度
  char ch4[] = { 'a','b','c'};
  printf("%s\n", ch3);           // ch3中隐藏一个\0
  printf("%s\n", ch4);           // ch4里面放了abc,之后放的什么不确定,所以会随机打印 
                                 //字符串打印\0的时候就会停下来,但是对于ch4来说后面没有\0,所以打印完c会一直往后打印,打印结果的后面会出现随机值
  return 0;
}

3.一维数组的使用

注意:

// 数组是通过下标来访问的

// 访问数组元素时的[]叫下标引用操作符

//去掉变量名或者数组名,剩下的就是它的类型

//计算变量的大小就是计算它的类型的大小

//计算数组的大小就是计算数组类型的大小

int main() {
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };   //[]语法形式
  //        下标: 0 1 2 3 4 5 6 7 8 9 
  printf("%d\n", arr[9]);                 //[]下标引用操作符
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);  //计算数组的大小:40
  for (i = 0; i < 10; i++) {             //打印数组所有元素
    printf("%d ", arr[i]);               //下标引用操作符,访问数组时arr[i]可以使用
  }
//计算变量的大小就是计算它的类型的大小
//  int a = 10;
//  printf("%d\n", sizeof(a));            //计算a变量的大小:4
//  printf("%d\n", sizeof(int));      //计算a的类型的大小:4
//计算数组的大小就是计算数组类型的大小
// int arr[10]= { 1,2,3,4,5,6,7,8,9,10 }; 数组的类型是int [10]
// printf("%d\n",arr);               //计算arr数组的大小:40
// printf("%d\n",int [10]);          //计算arr数组类型的大小:40
  return 0;
}

4.一维数组在内存中的存储

打印数组每个元素的地址

int main() {
  int arr[10] = { 0 };
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);
  for (i = 0; i < sz; i++) {
    printf("&arr[%d]=%p\n", i, &arr[i]);
  }
}

发现每个数组元素地址相差4,刚好是一个整型长度.所以:

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

b.随着下标的增长地址是由低到高变化的

通过指针也可以遍历数组所有的元素

pc是什么类型的指针,pc+1就跳过一个什么类型的元素

int main()
{
  int arr[10] = {1,2,3,4,5,6,7,8,9,10};
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);
  int* p = &arr[0];                          //p是一个整型指针,指向arr[0]的地址
  for (i = 0; i < sz; i++) {
    printf("%p==%p\n", p + i, &arr[i]);   //p+1 指的是从p地址跳过一个整型长度
    printf("%d\n", *(p + i));                     //通过上个操作发现p+i是下标元素的地址
  }
  //char* pc;         //pc是char类型的指针
                      //pc是什么类型的指针,pc+1就跳过一个什么类型的元素
  return 0;
}

二、二维数组的创建和初始化

1.二维数组的创建

int arr[3][4];   //创建一个二维数组,数组有三行四列,每个元素都是int类型

2.二维数组的初始化

(1)完全初始化

int  arr1[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

(2)不完全初始化,第一行放入1,2;第二行放入3,4;第三行放入5,6,其余元素默认为0

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

(3)二维数组初始化的时候,行可以省略,但是列不可以省略

因为可以根据里面的大括号判断几行,但是必须表明有几列

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

3.二维数组的使用

(1)通过下标来访问

(2)行从0开始,列也从0开始

//           0列 1列 2列 3列 
    //   0行:      1  2  3  4           
    //   1行:      5  6  7  8 
    //   2行:      9 10 11 12
  int  arr5[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };

(3)如何定位每个元素?

printf("%d\n", arr5[1][2]);   //定位7在1行2列

(4)如何访问二维数组的每个元素?

//打印二维数组每个元素
  int i = 0;
  for (i = 0; i < 3; i++) {
    int j = 0;
    for (j = 0; j < 4; j++) {
      printf("%-2d ", arr5[i][j]);         //%2d  打印两位整数,右对齐,左边补位    
                                           //%-2d 打印两位整数,左对齐,右边补位
    }
    printf("\n");
  }

4.二维数组在内存中的存储

计算第i行,第j列元素的地址

int main() {
  int arr[3][4] = { 0 };
  int i = 0;
  int j = 0;
  for (i = 0; i < 3; i++) {
    for (j = 0;j < 4; j++) 
 {
      printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
    }
  }
}

发现:

(1)看似是多行多列,真正内存中的存储方式也是连续的,第一行放完,放第二行…

(2)数组的起始地址是编译器给的

(3)二维数组地址的行可以省略.列不可以省略,因为连续存储,必须知道列,也就是每行放几个数字,这样才能方程二维数组,而行的话能放几行就放几行

(4)如果把二维数组的每一行看做一个一维数组,那么每一行一维数组也有数组名

arr[0] 就是第一行的数组名

arr[1] 就是第二行的数组名

三、数组越界问题

无论是一维数组还是二维数组:

C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的.

int main() {
  int arr[10]={ 0 };
  int i = 0;
  for (i = 0; i < 10; i++) {            //注意不要越界,不可以等用于10,因为在一些编译器中可能不会提示报错,直接执行错误代码
    printf("%d ", arr[i]);
  }
}

四、数组作为函数参数—冒泡排序

注意:往往我们在写代码的时候,会将数组作为参数传个函数.冒泡排序----核心思想:两两相邻的元素进行比较

**题目:**将arr[] = {3,1,5,9,2,4,7,6,8,0}用冒泡排序那升序排序打印结果.

一套冒泡排序----把一个数字放在应该出现的位置上

10个元素要九趟冒泡排序

3,1,5,9,2,4,7,6,8,0 3,1比较之后交换

1,3,5,9,2,4,7,6,8,0 3,5比较之后不需要交换

1,3,5,9,2,4,7,6,8,0 5,9比较之后不需要交换

1,3,5,9,2,4,7,6,8,0 9,2比较之后交换

1,3,5,2,9,4,7,6,8,0 9,4比较之后交换

1,3,5,2,4,9,7,6,8,0 9,7比较之后交换

1,3,5,2,4,7,9,6,8,0 9,6比较之后交换

1,3,5,2,4,9,6,9,8,0 9,8比较之后交换

1,3,5,2,4,9,6,8,9,0 9,0比较之后交换

1,3,5,2,4,9,6,8,0,9

错误设计:

//错误设计:
void bubble_sort(int arr[]) {      //数组名是数组首元素的地址
  int i = 0;
  int sz = sizeof(arr) / sizeof(arr[0]);   //错误的点在这里!!!
  //sz-1:冒泡排序的趟数
  for (i = 0; i < sz - 1; i++) {
    //一趟冒泡排序的过程
    int j = 0;
    //一趟冒泡排序的过程:10个元素(有九对)比较9次,九个元素比较8次....
    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;
      }
    }
  }
}
//一套冒泡排序----把一个数字放在应该出现的位置上
// 10个元素要九趟冒泡排序
// 3,1,5,9,2,4,7,6,8,0       3,1比较之后交换
// 1,3,5,9,2,4,7,6,8,0       3,5比较之后不需要交换
// 1,3,5,9,2,4,7,6,8,0       5,9比较之后不需要交换 
// 1,3,5,9,2,4,7,6,8,0       9,2比较之后交换 
// 1,3,5,2,9,4,7,6,8,0       9,4比较之后交换
// 1,3,5,2,4,9,7,6,8,0       9,7比较之后交换
// 1,3,5,2,4,7,9,6,8,0       9,6比较之后交换
// 1,3,5,2,4,9,6,9,8,0       9,8比较之后交换
// 1,3,5,2,4,9,6,8,9,0       9,0比较之后交换
// 1,3,5,2,4,9,6,8,0,9
int main() {
  int arr[] = { 3,1,5,9,2,4,7,6,8,0 };
  //排序--升序
  //冒泡排序----核心思想:两两相邻的元素进行比较
  bubble_sort(arr);
  int i = 0;                //排完之后把数组打印一下
  for (i = 0; i < sz; i++) {
    printf("%d ", arr[i]);
  }
  return 0;
}

发现结果没有按要求排序:

为什么呢?

数组名是数组首元素的地址

但是有两个例外情况:

(1)sizeof(数组名),数组名如果单独放在sizeof内部,这里的数组名表示整个数组,计算的是整个数组的大小

(2)&数组名,这里的数组名表示整个数组,表示的是整个数组的地址

除此之外,遇到的所有的数组名都是数组首元素的地址

下面可以验证一下:

int main() {
  int arr[10] = { 0 };
  printf("%p\n", arr);       //数组名是数组首元素的地址
  printf("%p\n", arr+1);    //数组名的地址+1--------------->跳过数组的一个元素
  printf("%p\n", &arr[0]);  //数组首元素的地址+1----------->跳过数组的一个元素
  printf("%p\n", &arr[0]+1);
  printf("%p\n", &arr);     //取地址数组名     
  printf("%p\n", &arr+1);   // 数组的地址+1---------------->跳过整个数组
}

正确设计

(1)数组在函数写形式函数的时候可以写成数组也可以写成指针的形式

(2)数组在传参的时候,形参的部分不会创建数组,所以void bubble_sort(int arr[],int sz) 中的int arr[]不用写大小,不过写不写都可以

void bubble_sort(int arr[],int sz) {      //数组名是数组首元素的地址
  int i = 0;
  //int sz = sizeof(arr) / sizeof(arr[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;
      }
    }
  }
}
//一套冒泡排序----把一个数字放在应该出现的位置上
// 10个元素要九套冒泡排序
// 3,1,5,9,2,4,7,6,8,
// 1,3,5,9,2,4,7,6,8,       1.3比较之后交换
// 1,3,5,9,2,4,7,6,8,       3,5比较之后不需要交换
// 1,3,5,9,2,4,7,6,8,       5,9比较之后不需要交换
// 1,3,5,2,9,4,7,6,8        9,2比较之后交换
// 1,3,5,2,4,9,7,6,8        9,4比较之后交换
// 1,3,5,2,4,7,9,6,8
#include<string.h>
int main() {
  int arr[] = {3,1,5,9,2,4,7,6,8,0};
  int sz = sizeof(arr) / sizeof(arr[0]);
  //排序--升序
  //冒泡排序----核心思想:两两相邻的元素进行比较
  bubble_sort(arr,sz);     //数组首元素的地址,整形指针来接收一个整形的地址
                        //函数拿指针去接收,虽然写成数组的形式,但是实质上是指针int* arr
                        //
  int i = 0;                //排完之后把数组打印一下
  for (i = 0; i < sz; i++) {
    printf("%d ", arr[i]);
  }
  return 0;
}


总结

今天的内容你是否有收获呢小伙伴们?💕💕

如果哪里写的有问题,欢迎大家帮我指正.

最后,希望友友可以留下关注点赞收藏关注后续内容哦~🥰💕❤️

相关文章
|
24天前
|
传感器 算法 安全
【C语言】两个数组比较详解
比较两个数组在C语言中有多种实现方法,选择合适的方法取决于具体的应用场景和性能要求。从逐元素比较到使用`memcmp`函数,再到指针优化,每种方法都有其优点和适用范围。在嵌入式系统中,考虑性能和资源限制尤为重要。通过合理选择和优化,可以有效提高程序的运行效率和可靠性。
75 6
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
53 5
|
27天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
1月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
1月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
57 4
|
2月前
|
存储 编译器 C语言
【c语言】数组
本文介绍了数组的基本概念及一维和二维数组的创建、初始化、使用方法及其在内存中的存储形式。一维数组通过下标访问元素,支持初始化和动态输入输出。二维数组则通过行和列的下标访问元素,同样支持初始化和动态输入输出。此外,还简要介绍了C99标准中的变长数组,允许在运行时根据变量创建数组,但不能初始化。
59 6
|
2月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
2月前
|
存储 C语言
C语言:一维数组的不初始化、部分初始化、完全初始化的不同点
C语言中一维数组的初始化有三种情况:不初始化时,数组元素的值是随机的;部分初始化时,未指定的元素会被自动赋值为0;完全初始化时,所有元素都被赋予了初始值。
|
2月前
|
C语言
C语言数组
C语言数组
28 0