当前位置: 首页 > news >正文

C语言初阶--数组

1.一维数组的创建和初始化

1.1数组的创建

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

数组的创建方式:

type_t   arr_name  [const_n];
//type_t 数组的元素类型
//const_n 常量表达式,指定数组的大小
#include <stdio.h>
int main()
{int arr[10]; //数组的创建char ch[4+1];//下面的代码只能在支持C99标准的编译器上编译,此处VS不行int n = 10;scanf("%d", &n);int arr2[n]; //这种数组不能初始化//在C99标准之前,数组的大小必须是常量或常量表达式;在C99标准之后,为了支持变长数组,数组的大小可以是变量return 0;
}

1.2数组的初始化

数组的初始化:在创建数组的同时给数组的内容一些合理的初始值。

int main()
{int arr[10] = {1,2,3}; //不完全初始化,剩余的元素默认初始化为0int arr1[10] = {1,2,3,4,5,6,7,8,9,0}; int arr2[] = {1,2,3};char ch1[10] = {'a', 'b', 'c'}; // a b cchar ch2[10] = "abc"; // a b c \0char ch3[] = {'a', 'b', 'c'}; char ch4[] ="abc";return 0;
}

可以自行查看数组内的数据:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3一维数组的使用

数组的使用之前介绍了一种操作符:[],下标引用操作符。它其实就是数组访问的操作符。
在这里插入图片描述

#include <stdio.h>
int main()
{int arr[] = {1,2,3,4,5,6,7,8,9,10};//[] 下标引用操作符printf("%d\n", arr[4]); //5return 0;
}
#include <stdio.h>
int main()
{int arr[] = {1,2,3,4,5,6,7,8,9,10};int i = 0;int sz = sizeof(arr) / sizeof(arr[0]); //10//for (i = 0; i < sz; i++)//{//    printf("%d ", arr[i]); //1 2 3 4 5 6 7 8 9 10//}for (i = sz-1; i >= 0; i--){printf("%d ", arr[i]); //10 9 8 7 6 5 4 3 2 1}return 0;
}

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

数组的大小可以通过计算得到。

1.4一维数组在内存中的存储

#include <stdio.h>
int main()
{int arr[] = {1,2,3,4,5,6,7,8,9,10};int i = 0;int sz = sizeof(arr) / sizeof(arr[0]); //10//打印数组每个元素的地址for (i = 0; i < sz; i++){printf("&arr[%d] = %p\n", i, &arr[i]); }return 0;
}

在这里插入图片描述
从运行结果看得出(或者自己查看内存),随着数组下标的增长,元素的地址,也在有规律的递增。得出结论:数组在内存中是连续存放的
在这里插入图片描述

2.二维数组的创建和初始化

2.1二维数组的创建

// 1 2 3 4
// 2 3 4 5
// 3 4 5 6
int main()
{int arr1[3][4];char arr2[5][10];return 0;
}

2.2二维数组的初始化

int main()
{int arr1[3][4] = {1,2,3,4,2,3,4,5,3,4,5,6}; //完全初始化return 0;
}

在这里插入图片描述

int main()
{int arr1[3][4] = { {1,2}, {3,4}, {5,6} }; //不完全初始化return 0;
}

在这里插入图片描述

int main()
{int arr2[][4] = { {1,2,3,4}, {2,3} }; //二维数组如果有初始化,行可以省略,列不能省略int arr3[][4] = {  1,2,3,4,2,3 };return 0;
}

2.3二维数组的使用

二维数组的使用也是通过下标的方式。

#include <stdio.h>
int main()
{int arr[3][4] = {1,2,3,4,2,3,4,5,3,4,5,6}; int i = 0;//printf("%d\n", arr[2][0]); //3for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 4; j++){printf("%d ", arr[i][j]);}printf("\n");}return 0;
}

在这里插入图片描述

//替换原数组的元素
#include <stdio.h>
int main()
{int arr[3][4] = {1,2,3,4,2,3,4,5,3,4,5,6}; int i = 0;//printf("%d\n", arr[2][0]); //3for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 4; j++){scanf("%d ", &arr[i][j]);}}for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 4; j++){printf("%d ", arr[i][j]);}printf("\n");}return 0;
}

在这里插入图片描述

2.4二维数组在内存中的存储

#include <stdio.h>
int main()
{int arr[3][4] = {1,2,3,4,2,3,4,5,3,4,5,6}; 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;
}

在这里插入图片描述
由上可知:二维数组在内存中也是连续存储。
在这里插入图片描述

3.数组越界

数组的下标是有范围限制的。

数组的下标规定是从0开始。如果数组有n个元素,最后一个元素的下标是n-1。

所以数组的下标如果小于0或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。

#include <stdio.h>
int main()
{int arr[] = {1,2,3,4,5,6}; //0-5int i = 0;for (i = 0; i < 10; i++)  //0-9,越界{printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序是正确的。所以程序员写代码时,最好自己做越界的检查。

//error
#include <stdio.h>
int main()
{int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j <= 4; j++) //越界{printf("%d ", arr[i][j]);}}return 0;
}

在这里插入图片描述
二维数组的行和列也可能存在越界。

4.数组作为函数参数

冒泡排序的核心思想:两个相邻的元素进行比较。

分析:
在这里插入图片描述
一趟冒泡排序让一个数据来到它最终应该出现的位置上!第1趟——10个元素——比较9次;第二趟——9个元素——比较8次···

10个元素——9趟

n个元素——n-1趟

4.1冒泡排序函数的错误设计

#include <stdio.h>
//数组传参时,形参有2种写法:
//1.数组
//2.指针
void bubble_sort(int arr[]) //形参是数组的形式。地址应该使用指针来接收,所以arr这里看似是数组,本质是指针变量
{//趟数int sz = sizeof(arr) / sizeof(arr[0]); //所以4/4=1int 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[] = {9,8,7,6,5,4,3,2,1,0}; //数组,把数组的元素排成升序//0 1 2 3 4 5 6 7 8 9//冒泡排序的算法,对数组进行排序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr); //数组名本质上是数组首元素的地址int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]); //error,未到达预期效果,9 8 7 6 5 4 3 2 1 0}return 0;
}

修正:

#include <stdio.h>
//数组传参时,形参有2种写法:
//1.数组
//2.指针 void bubble_sort(int* arr, int sz)
void bubble_sort(int arr[], int sz) //形参是数组的形式。地址应该使用指针来接收,所以arr这里看似是数组,本质是指针变量
{//趟数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[] = {9,8,7,6,5,4,3,2,1,0}; //数组,把数组的元素排成升序//0 1 2 3 4 5 6 7 8 9//冒泡排序的算法,对数组进行排序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz); //数组名本质上是数组首元素的地址int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]); //0 1 2 3 4 5 6 7 8 9}return 0;
}

4.2数组名是什么?

#include <stdio.h>
int main()
{int arr[10];printf("%p\n", arr); printf("%p\n", &arr[0]);return 0;
}

在这里插入图片描述

#include <stdio.h>
int main()
{int arr[10];int n = sizeof(arr);printf("%d\n", n); //40return 0;
}

纠正前面的说法:

数组名确实能表示首元素的地址,但是有2个例外:

1、sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。

2、&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

#include <stdio.h>
int main()
{int arr[10] = {0};printf("%p\n", arr);  //arr就是首元素的地址printf("%p\n", arr+1); printf("------------------\n");printf("%p\n", &arr[0]); //首元素的地址printf("%p\n", &arr[0]+1); //下一个元素的地址printf("------------------\n");printf("%p\n", &arr); //数组的地址printf("%p\n", &arr+1);return 0;
}

二维数组的数组名理解:

#include <stdio.h>
int main()
{int arr[3][4];//int sz = sizeof(arr);//printf("%d\n", sz); //48printf("%p\n", arr); //二维数组的数组名也表示数组首元素的地址printf("%p\n", arr+1);//005FFCA4//005FFCB4return 0;
}

在这里插入图片描述

#include <stdio.h>
int main()
{int arr[3][4] = {0};printf("%d\n", sizeof(arr) / sizeof(arr[0])); // 48/16=3,计算行printf("%d\n", sizeof(arr[0]) / sizeof(arr[0][0])); // 16/4=4,计算列
}

5.数组的应用实例

5.1实例1:三子棋

拆分模块:

test.c 测试游戏的逻辑

game.c 游戏代码的实现

game.h 游戏代码的声明(函数声明、符号定义)

//test.c
#define _CRT_SECURE_NO_WARNINGS #include "game.h"
void menu()
{printf("********************\n");printf("***1.play  0.exit***\n");printf("********************\n"); 
}
void game()
{char ret = 0;char board[ROW][COL] = {0};//初始化棋盘的函数InitBoard(board, ROW, COL);DisplayBoard(board, ROW, COL);//下棋while (1){PlayerMove(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}DisplayBoard(board, ROW, COL);ComputerMove(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}        DisplayBoard(board, ROW, COL);}if (ret == '*'){printf("玩家赢\n");}else if (ret == '#'){printf("电脑赢\n");}else{printf("平局\n");}DisplayBoard(board, ROW, COL);}int main()
{srand((unsigned int)time(NULL)); //设置随机数的生成起点int input = 0;do{menu(); //打印菜单printf("请选择:>");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default:printf("选择错误!\n");break;}}while (input);return 0;
}
//game.h
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 3
#define COL 3//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋:找没有下棋的地方随机下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C'
char IsWin(char board[ROW][COL], int row, int col);
//game.c
#define _CRT_SECURE_NO_WARNINGS #include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){board[i][j] = ' ';}}
}//第一个版本
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//    int i = 0;
//    for (i = 0; i < row; i++)
//    {
//        //打印数据
//        printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//        //打印分割行
//        if (i < row - 1)
//            printf("---|---|---\n");
//    }
//}
void DisplayBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){//打印数据//printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);int j = 0;for (j = 0; j< col; j++){printf(" %c ", board[i][j]);if (j < col-1)printf("|");}printf("\n");//打印分割行if (i < row-1){int j = 0;for (j = 0; j < col; j++){printf("---");if (j < col-1)printf("|");}printf("\n");}}
}void PlayerMove(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("玩家下棋:>\n");while (1){printf("请输入坐标:>");scanf("%d %d", &x, &y);//坐标范围合法性判断if (x >= 1 && x <= row && y >= 1 && y <= col){if (board[x-1][y-1] == ' '){board[x-1][y-1] = '*';break;}else{printf("坐标被占用,不能下棋,请选择其他位置\n");}}else{printf("坐标非法,请重新输入\n");}}
}void ComputerMove(char board[ROW][COL], int row, int col)
{printf("电脑下棋:>\n");int x = 0;int y = 0;while (1){x = rand() % row; //0-2y = rand() % col; //0-2if (board[x][y] == ' '){board[x][y] = '#';break;}}
}//满了返回1
//不满返回0
int IsFull(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (board[i][j] == ' '){return 0;}}}return 1;
}
char IsWin(char board[ROW][COL], int row, int col)
{//判断行int i = 0;for (i = 0; i < row; i++){if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' '){return board[i][1];}}//判断列int j = 0;for (j = 0; j < col; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' '){return board[1][j];}} //判断对角线if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' '){return board[1][1];}if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' '){return board[1][1];}//还没有人赢,则判断平局if (IsFull(board, row, col)){return 'Q';}//游戏继续return 'C';
}

5.2实例2:扫雷游戏(直接贴代码,实现了基本功能)

还可添加标记功能+展开一大片功能(感兴趣可以自己实现,递归思路)

//test.c
#define _CRT_SECURE_NO_WARNINGS#include "game.h"void menu()
{printf("******************\n");printf("******1. play*****\n");printf("******0. exit*****\n");printf("******************\n");
}void game()
{char mine[ROWS][COLS] = {0}; //存放布置好的雷的信息char show[ROWS][COLS] = {0}; //存放排查出的雷的信息//初始化数组的内容为指定的内容//mine 数组在没有布置雷的时候,都是'0'InitBoard(mine, ROWS, COLS, '0');//show 数组在没有排查雷的时候,都是'*'InitBoard(show, ROWS, COLS, '*');//DisplayBoard(mine, ROW, COL);//设置雷SetMine(mine, ROW, COL);//DisplayBoard(mine, ROW, COL);DisplayBoard(show, ROW, COL);//排查雷FindMine(mine, show, ROW, COL);}
int main()
{int input = 0;//设置随机数的生成起点srand((unsigned int)time(NULL));do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}
//game.h
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define EASY_COUNT 10void InitBoard(char board[ROWS][COLS], int rows, int cols, int set);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void SetMine(char board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//game.c
#define _CRT_SECURE_NO_WARNINGS #include "game.h"void InitBoard(char board[ROWS][COLS], int rows, int cols, int set)
{int i = 0;int j = 0;for (i = 0; i <= rows; i++){for (j = 0; j <= cols; j++){board[i][j] = set;}}
}void DisplayBoard(char board[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;printf("---------扫雷游戏----------\n");for (j = 0; j <= col; j++)   //打印列号{printf("%d ", j);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);       //打印行号for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}printf("---------扫雷游戏----------\n");
}void SetMine(char board[ROWS][COLS], int row, int col)
{int count = EASY_COUNT;//行和列的坐标范围1-9while (count){int x = rand() % row + 1;int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';count--;}}
}int get_mine_count(char board[ROWS][COLS], int x, int y)
{return (board[x - 1][y - 1] +board[x - 1][y] +board[x - 1][y + 1] +board[x][y - 1] +board[x][y + 1] +board[x + 1][y - 1] +board[x + 1][y] +board[x + 1][y + 1] - 8*'0');
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int win = 0; //找到非雷的个数while (win < row*col-EASY_COUNT){printf("请输入要排查的坐标:>");scanf("%d%d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (show[x][y] != '*'){printf("该坐标被排查过了,不能重复排查\n");}else{if (mine[x][y] == '1')  //如果是雷{printf("很遗憾,你被炸死了\n");DisplayBoard(mine, ROW, COL);break;}else  //如果不是雷{win++;//统计mine数组中x,y坐标周围有几个雷int count = get_mine_count(mine, x, y);show[x][y] = count + '0'; //转换为数字字符DisplayBoard(show, ROW, COL);}}}else{printf("输入的坐标非法,请重新输入\n");}}if (win == row * col - EASY_COUNT){printf("恭喜你,排雷成功\n");DisplayBoard(mine, ROW, COL);}
}

在这里插入图片描述

6.实现字符串逆序

字符串逆序分析:
在这里插入图片描述

//1、字符串逆序(非递归实现,循环),reverse_string(char * string)
#include <stdio.h>
int main()
{char arr[] = "abcdef"; //[a b c d e f \0]int sz = sizeof(arr) / sizeof(arr[0]); //sz=7int left = 0; //下标int right = sz -2; //所以减去2while (left<right){char tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;left++;right--;}printf("%s\n", arr); //fedcbareturn 0;
}
//1、字符串逆序(非递归实现,循环),reverse_string(char * string)
#include <stdio.h>
#include <string.h>int main()
{char arr[] = "abcdef"; //[a b c d e f \0]//int sz = sizeof(arr) / sizeof(arr[0]);int left = 0; //下标//int right = sz -2; int right = strlen(arr)-1;while (left<right){char tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;left++;right--;}printf("%s\n", arr); //fedcbareturn 0;
}
//2、字符串逆序(非递归实现,使用函数),reverse_string(char * string)
#include <stdio.h>
#include <string.h>void reverse(char arr[])
{int left = 0; //下标int right = strlen(arr)-1;while (left<right){char tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;left++;right--;}
}int main()
{char arr[] = "abcdef"; //[a b c d e f \0]reverse(arr);printf("%s\n", arr); //fedcbareturn 0;
}

分析:(递归实现,较复杂)
在这里插入图片描述

//字符串逆序(递归实现,不使用库函数),reverse_string(char * string)
#include <stdio.h>int my_strlen(char* str)
{int count = 0;while (*str != '\0'){count++;str++;}return count;
}
void reverse(char* str)
{//注释的数字对应流程图中数字char tmp = *str; //1int len = my_strlen(str);*str = *(str+len-1); //2*(str+len-1) = '\0'; //3if (my_strlen(str+1)>=2)reverse(str+1); //4*(str+len-1) = tmp; //5
}int main()
{char arr[] = "abcdef"; //[a b c d e f \0]reverse(arr);printf("%s\n", arr); //fedcbareturn 0;
}
//字符串逆序(递归实现,不使用库函数)
#include <stdio.h>int my_strlen(char* str)
{int count = 0;while (*str != '\0'){count++;str++;}return count;
}//这个代码存在一定潜在问题,如果字符串长度是偶数时,会出现问题
//void reverse(char arr[], int left, int right)
//{
//    char tmp = arr[left];
//    arr[left] = arr[right];
//    arr[right] = tmp;
//    if (left < right)
//        reverse(arr, left + 1, right - 1);
//}
//修正
void reverse(char arr[], int left, int right)
{if (left < right)  //满足这个条件才交换和递归{char tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;reverse(arr, left+1, right-1);    }
}int main()
{char arr[] = "abcdef"; //[a b c d e f \0]int left = 0;int right = my_strlen(arr)-1;reverse(arr, left, right);printf("%s\n", arr); //fedcbareturn 0;
}

总结

今天就暂且更新至此吧,期待下周再会。如有错误还请不吝赐教。希望对您学习有所帮助,翻页前留下你的支持,以防下次失踪了嗷。

作者更新不易,免费关注别手软。

相关文章:

  • 005 权限的理解
  • leetcode刷题日记——随机链表的复制
  • Prompt(提示词)工程师,“跟AI聊天”
  • 约瑟夫josephu问题
  • 2025-05-07 Unity 网络基础8——UDP同步异步通信
  • 【EasyPan】saveShare代码分析
  • 企业智能化第一步:用「Deepseek+自动化」打造企业资源管理的智能中枢
  • DSENT (Design Space Exploration of Networks Tool) 配合gem5
  • day 14 SHAP可视化
  • C++:买房子
  • Vue——前端vue3项目使用汉字转拼音
  • #黑马点评#(一)登录功能
  • LangChain第三讲:大模型的输出如何格式化成字符串?
  • 阿里云服务器-宝塔面板安装【保姆级教程】
  • HarmonyOS NEXT深度解析:自研框架ArkUI-X的技术革命与跨平台实践
  • 本地部署 MySQL + Qwen3-1.5B + Flask + Dify 工作流
  • 动态规划-91.解码方法-力扣(LeetCode)
  • SPSS系统发育分析中的聚类相关part
  • 端口安全讲解
  • 《Python星球日记》 第44天: 线性回归与逻辑回归
  • 富家罹盗与财富迷思:《西游记》与《蜃楼志》中的强盗案
  • 当我们提起拉动消费时,应该拉动什么消费?
  • 重庆大学通报本科生发14篇SCI论文:涉事学生及其父亲被处理
  • 中国象棋协会坚决支持司法机关依法打击涉象棋行业的违法行为
  • 19岁女生注射头孢离世后续:院方道歉,医生停职,监管介入
  • 陕西澄城樱桃在上海推介,向长三角消费者发出“甜蜜之邀”