目录
1.数组的概念
2.一维数组
2.1一维数组创建与初始化
2.2使用一维数组
2.3一维数组的存储
3.二维数组
3.1二维数组的创建
3.2二维数组初始化
3.3二维数组的使用
3.4二维数组的存储
4.函数
4.1自定义函数
4.2return语句
4.3数组作为函数参数
4.4函数的嵌套使用
4.5函数声明
5.static与extern
5.1static修饰局部变量
5.2static修饰全局变量
5.3static修饰函数
1.数组的概念
数组是一组相同类型数据的集合,注意是数据而不是数字的集合
数组中可以存放1或多个数据,元素个数不可为0
数组中所有数据的类型是相同的
数组分为一维数组和多维数组,二维数组比较常见
2.一维数组
2.1一维数组创建与初始化
一维数组的创建方式如下,数据类型根据内部存放的数据来决定,数组名可以根据实际需要或者使用方便取,[ ]内部是数组的元素个数即数组的大小

#include<stdio.h>
int main()
{
int arr1[10];//存放整型,大小为10
char arr2[5];//存放字符类型,大小为5
return 0;
}
一维数组与变量一样需要进行初始化,不然使用时容易出现问题
下面是数组的几种常用初始化方法,借此讲一下数组的类型
数组的类型其实就是除了数组名以外的部分
例如arr_1的类型就是int [5],arr_6的类型就是int [ ]
#include<stdio.h>
int main()
{
int arr_1[5] = { 1,2,3,4,5 };//完全初始化,将每个元素都写出来
int arr_2[5] = { 1,2,3 };//不完全初始化,只写出一部分元素,其余默认为0
int arr_3[5] = { 0 };//将全部元素初始化为0
int arr_4[5] = { 1 };//第一个元素为1,其余为0
int arr_6[] = { 1,2 };//如果元素全部写出,[]内部的常量可以不写
return 0;
}
2.2使用一维数组
当我们想使用数组内的数据时,可以通过下标来访问,第一个元素下表0,后续以此类推
如图将数组第二个元素7赋值给r,这就是数组的引用
#include<stdio.h>
int main()
{
int arr_1[5] = { 5,7,3,4,1 };
//下标 0 1 2 3 4
int r = arr_1[1];//7
printf("%d\\n", r);
return 0;
}
而有时需要使用数组来存储数据,但是数据需要运行时输入,而数组数据的输入和输入一个变量没有多大区别,如图,我们依次输入五个元素然后将其输出
#define _CRT_SECURE_NO_WARNINGS 1//在vs2022中使用scanf函数需要这一段代码
#include<stdio.h>
int main()
{
int arr[5] = { 0 };
for (int i = 0; i < 5; i++) {
scanf("%d", &arr[i]);//取出第i+1个元素的地址然后将输入的值赋给它
}
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);//输出数组
}
return 0;
}
而对于一个没有写长度数组我们可以使用sizeof函数快速获取其长度
#include<stdio.h>
int main()
{
int arr[] = { 1,542,5,2,54,4,8,12,134,3 };
int len = sizeof(arr) / sizeof(arr[0]);//数组大小除以一个元素大小
printf("%d", len);
return 0;
}
2.3一维数组的存储
在一维数组中,所有元素是连续存储的,我们可以通过依次输出每个元素的地址来观察
地址的存储采用16进制,将最后两位化为10进制可以明显看到每个元素之间差了4个字节,正好是int类型的大小

3.二维数组
3.1二维数组的创建
将一维数组作为该数组的每个元素,那么该数组称为二维数组,下面是定义
#include<stdio.h>
int main()
{
int arr[5][3];
//和一维数组的定义基本相同,int表示内部存储的数据是整型
//arr是数组名可以根据需要取
//5表示5行,3表示三列,即每个一维数组的长度为3共有5个一维数组
return 0;
}
3.2二维数组初始化
#include<stdio.h>
int main()
{
int arr1[5][3] = { 1,2 };//不完全初始化,只初始化了第一行前两个元素,其余默认为0
int arr2[5][3] = { 0 };//不完全初始化,全部元素默认为0
int arr3[2][3] = { 1,2,3,4,5,6 };//完全初始化
int arr4[3][3] = { {1,2,3},{4,5,6},{7,8,9} };//按照行初始化
int arr5[][5] = { 1,2,3,4,5,6,7 };
//二维数组初始化可以不定义行,但是一定要知道有几列
// 1 2 3 4 5
// 6 7 0 0 0
//知道列大小根据元素个数可以补充完整数组,但是不知道列大小无法补充
int arr6[][5] = { {1,2},{2,3} };
// 1 2 0 0 0
// 2 3 0 0 0
//按照行初始化时每个数组也可以是不完全初始化
return 0;
}
3.3二维数组的使用
在使用二维数组时同样要使用下标来访问元素,在理解上我们可以当成行列的坐标
#include<stdio.h>
int main()
{
int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
// 1 2 3 4
// 5 6 7 8
// 9 10 11 12
//例如要取出11这个元素,那就是三行三列arr[2][2]
printf("%d\\n", arr[2][2]);
return 0;
}
类比一维数组,二维数组的输入输入也是类似的,不过多解释
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int arr[3][4] = { 0 };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
scanf("%d", &arr[i][j]);
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d", arr[i][j]);
}
}
return 0;
}
3.4二维数组的存储
实际上二维数组中的元素并没有真的分为几行几列,所有数组都存在同一行
所以和一维数组一样,二维数组中的元素也是连续存储的,每两个元素之间相差四个字节

4.函数
C语言中的函数时完成某项特定任务的一小段代码,也被称为子程序
一般分为两类,库函数与自定义函数,库函数是C语言自带的只要引入相应头文件即可使用
在该网站可以详细了解不再赘述(C library – C++ Reference (cplusplus.com))
4.1自定义函数
当库函数不能满足我们的需要时,就得创建一个自定义函数来实现目的
一个自定义函数的定义方式如下,需要有返回类型,函数名以及传入参数,如果用不到任何参数形式参数部分可以不写

一般的函数进行的是传值调用,即在调用函数时将实际参数传入,用函数内部临时创建的临时变量来接收,函数运行完成后临时参数内存被释放而且不改变实际参数
由运行结果可知a和b的值并没有被修改,如果希望值被修改,则需要用指针传址调用,可以在我的指针部分的文章学习
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void test(int x,int y) {
x = 10;
y = 20;
printf("x=%d y=%d\\n", x, y);
}
int main()
{
int a = 1;
int b = 2;
test(a, b);
printf("a=%d b=%d\\n", a, b);
return 0;
}

4.2return语句
在自定义函数使用过程中,经常需要使用return来返回函数的结果
1.当返回类型为void,可以写return ;
2.当return执行之后,后续程序不再执行
3.return的值如果和返回类型不同。会转换为函数返回类型
4.分支语句如果写了return,则每个分支都要有return返回
5.不写返回类型默认是int
6.写了返回类型但是没有使用return,则返回值未知
4.3数组作为函数参数
定义一个数组,然后使得每个元素加10,前后输出数组元素发现,数组的元素真实的被修改了
这就是一个传址调用,当传入数组的时候,其实传入的是数组的地址,直接通过地址修改数组元素是可以的,所以在函数内部求用sizeof求数组长度是不可以的,因为函数内部其实使用的是指针进行操作,得不到正确的结果
注意以下几个数组传参的要点
1.函数形参与实参个数要匹配
2.一维数组传参时,大小可以省略不写
3.二维数组传参时不能省略列大小,但是可以省略行大小
4.数组传参进去后,任何对数组的操作都是对实参数组本身进行的,不会创建一个临时数组
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void dis(int arr[], int len) {
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\\n");
}
void test(int arr[],int len) {
for (int i = 0; i < len; i++) {
arr[i] += 10;
}
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
int len = sizeof(arr) / sizeof(arr[0]);
dis(arr, len);
test(arr, len);
dis(arr, len);
return 0;
}

4.4函数的嵌套使用
在一个函数中调用一个函数是可以的,因为函数本质就是一段实现特定功能的子程序段,相当于是将代码省略成一个函数替代,所以可以嵌套使用,但是不可以嵌套定义函数,每个函数都是独立定义的
#include<stdio.h>
void dis(int arr[], int len) {
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\\n");
}
void test(int arr[],int len) {
for (int i = 0; i < len; i++) {
arr[i] += 10;
}
dis(arr, len);//在函数内部使用另一个函数
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
int len = sizeof(arr) / sizeof(arr[0]);
dis(arr, len);
test(arr, len);
return 0;
}
下面再讲一个printf函数的嵌套使用
#include<stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d",43)));
//1 2 3
//首先我们要知道printf函数的返回值是多少
//printf函数的返回值是打印的字符个数
//所以第三个printf先打印43,返回2
//第二个printf函数打印2,返回1
//第一个printf函数打印1
//结果是4321
printf("\\n");
printf("%d\\n", printf("%d\\n", printf("%d\\n", 43)));
//注意\\n等转义字符也要计算在内
//输出为 43
// 3
// 2
return 0;
}
4.5函数声明
函数在使用前一定要先声明再调用
注意是声明而不是定义
int add(int x, int y);
只要写这么一行就算声明了函数,定义可以在调用之后的区域写,是没问题的,因为编译器从上往下扫描时,如果没有扫描到函数声明就使用了该函数,就会报错
在实际写代码时,我们可能会拆分成几个页面来写,以保持可读性与简洁性
因为库函数是包含在头文件里的,所以我们可以新建一个头文件,将需要的函数在其中声明
需要使用的时候包含头文件即可

5.static与extern
static可以修饰变量,extern可以引入变量
要解释这两个操作,先讲解一下生命周期与作用域
作用域就是一个变量可以合法使用的范围,局部变量的作用域在其所在的代码块,出了代码块就不起作用了,全局变量的作用域是整个程序
生命周期就是当一个变量被创建出来时,开始了它的生命周期,当它离开了它的作用域之后它的生命周期结束了,所以局部变量从被创建之后生命周期开始,离开所在的代码块之后生命周期结束,全局变量的生命周期从创建开始,到整个程序结束
5.1static修饰局部变量
static修改了局部变量的生命周期,当离开局部的作用域后它并没有被销毁,生命周期还在继续,直到整个程序的结束
观察下列代码,每次调用test函数的时候i被创建出来为0,然后加1输出1,离开函数后i被销毁
所以每次都输出1

那如果将i用static修饰呢,每次i都在加1并输出,所以依次输出1~5
因为static修改了i的生命周期,创建出来之后,只要对i进行操作,都是原来的i
相当于static int i=0只用了一次,负责将i创建并初始化,后续只剩下每次的i++

5.2static修饰全局变量
全局变量具有外链接性,意思是在很多个源文件中,我如果在其中一个源文件中定义了一个全局变量,加以正确的声明,它是可以被所有文件使用的
这就设计到extern的作用了,可以从外部引入变量或者函数
如下所示,当在test1.c中创建了一个全局变量a,在test.c中用extern声明就可以调用

而当用static修饰全局变量a时
程序就出现了错误,所以static修饰全局变量改变了其外链接性,使得它只能被本文件调用

5.3static修饰函数
同样的static修饰函数也是一样,函数也具有外链接性,所以当static修饰函数时,它也只能被本文件使用





