优质的南昌网站建设,wordpress交友,中文绿色环保网站模板,宁波在线网文章目录前言一、数组的定义1.一维数组①.如何定义②.声明规则③.内存分布④.初始化方法2.二维数组3.高维数组二、访问数组元素相关问题1.访问越界2.数组的传递三、Scanf与字符数组1.字符数组初始化2.scanf读取字符四、字符数组相关函数前言
今天的C语言训练营没有安排高维数组…
文章目录前言一、数组的定义1.一维数组①.如何定义②.声明规则③.内存分布④.初始化方法2.二维数组3.高维数组二、访问数组元素相关问题1.访问越界2.数组的传递三、Scanf与字符数组1.字符数组初始化2.scanf读取字符四、字符数组相关函数前言
今天的C语言训练营没有安排高维数组的讲解因为考试中常考的是一维数组老师根据一维数组分析了数组越界、数组在内存中的分布、缓冲区与字符数组等相关问题。
一、数组的定义
先看一个案例 为了存放鞋子假设你把衣柜最下面的一层分成了 10 个连续的格子。此时让他人帮你拿鞋子就会很方便例如你可直接告诉他拿衣柜最下面一层第三个格子中的鞋子。同样假设现在我们有 10 个整数存储在内存中为方便存取我们可以借助 C 语言提供的数组通过一个符号来访问多个元素。 也就是在我们使用的时候如果需要用到多个类型相同的变量我们大可以选数组来简化我们的操作。
1.一维数组
①.如何定义
一维数组的定义格式为
类型说明符 数组名 [常量表达式]例如:定义一个整型数组数组名为 a它有 10 个元素
int a[10];②.声明规则
注意声明数组时要遵循以下规则
1数组名的命名规则和变量名的相同即遵循标识符命名规则。2在定义数组时需要指定数组中元素的个数方括号中的常量表达式用来表示元素的个数即数组长度。3常量表达式中可以包含常量和符号常量但不能包含变量。也就是说C 语言不允许对数组的大小做动态定义即数组的大小不依赖于程序运行过程中变量的值。能不用高维数组就不用高维数组多一个维度多一点复杂除非自己特别了解数组
以下是错误的声明示例最新的 C 标准支持但是最好不要这么写 int n; scanf(“%d”, n); /* 在程序中临时输入数组的大小 */ int a[n]; 数组声明的其他常见错误如下 ① float a[0]; /* 数组大小为 0 没有意义 / ② int b(2)(3); / 不能使用圆括号 / ③ int k3, a[k]; / 不能用变量说明数组大小*/ ③.内存分布
数组元素在内存中是连续分布的并且遵循小下标位于低地址的原则。数组中每一个元素占有的空间跟数组类型有关。
④.初始化方法
1在定义数组时对数组元素赋初值。例如 int a[10]{0,1,2,3,4,5,6,7,8,9}; 不能写成 int a[10];a[10]{0,1,2,3,4,5,6,7,8,9} 2可以只给一部分元素赋值。例如 int a[10]{0,1,2,3,4}; 定义 a 数组有 10 个元素但花括号内只提供 5 个初值这表示只给前 5 个元素赋初值后 5 个 元素的值为 0。 3如果要使一个数组中全部元素的值为 0那么可以写为 int a[10]{0,0,0,0,0,0,0,0,0,0}; 或 int a[10]{0}; 4在对全部数组元素赋初值时由于数据的个数已经确定因此可以不指定数组的长度。 例如 int a[]{1,2,3,4,5}; 2.二维数组
除了使用一维数组还可以使用二维数组二维数组定义方法与一维数组大致相同。
类型说明符 数组名 [常量表达式][常量表达式]例如:定义一个整型数组数组名为 a它有 100 个元素
int a[10][10];3.高维数组
C语言支持3维及以上维度的数组不过我们一般用不到所以不必深究只需了解即可。使用数组的时候一定要避免数组越界这个问题我会在下面介绍到。 先看看高维数组的使用
//
// Created by Zhu Shichong on 2023/1/9.
//
#includestdio.h
main()
{int a[1][1][1];int b[1][1][1][1];int c[2][2][2][2][2];a[0][0][0]1;b[0][0][0][0]2;c[1][1][1][1][1]3;printf(%d %d %d,a[0][0][0],b[0][0][0][0],c[1][1][1][1][1]);return 0;
}数组元素的个数定义时各维度下标大小连乘
二、访问数组元素相关问题
1.访问越界
前面介绍过数组定义之后在内存中是连续分布的并且100个元素的数组最大数组下标为99那么我们访问下标为100的元素会有什么问题呢大致有以下两种情况
数组内存中接下来的空间被其余变量占用会直接导致程序异常。数组内存中接下来的空间没有被其余变量占用会读出内存中的脏值也就是乱码。内存中有什么将会读什么最终亦会导致程序异常。
由此可见数组越界对我们的程序影响之大。
可能文字不如图片给的感觉强烈下面给出一个案例大家可以自行尝试
//
// Created by Zhu Shichong on 2023/1/9.
//
#include stdio.h
//数组越界
int main()
{int a[5]{1,2,3,4,5}; //定义数组时数组长度必须固定int j20;int i10;a[5]6; //越界访问a[6]7; //越界访问会造成数据异常printf(i%d\n,i); //i 发生改变return 0;
}有这种情况的原因是 编译器并不检查程序对数组下标的引用是否在数组的合法范围内。这种不加检查的行为有好处也有坏处好处是不需要浪费时间对有些已知正确的数组下标进行检查坏处是这样做将无法检测出无效的下标引用。 一个良好的经验法则是 如果下标值是通过那些已知正确的值计算得来的那么就无须检查如果下标值是由用户输入的数据产生的那么在使用它们之前就必须进行检查以确保它们位于有效范围内。
2.数组的传递
先看一个案例
//
// Created by Zhu Shichong on 2023/1/9.
//
#include stdio.h
#includestring.h
//一维数组的传递数组长度无法传递给子函数
//C 语言的函数调用方式是值传递(将地址的值传递过去)
void print(int b[],int len)
{int i;for(i0;ilen;i){printf(%3d,b[i]);}b[4]20; //在子函数中修改数组元素printf(\n);
}
//数组越界
//一维数组的传递
#define N 5
int main()
{int a[5]{1,2,3,4,5}; //定义数组时数组长度必须固定print(a,5);printf(a[4]%d\n,a[4]); //a[4]发生改变return 0;
}如果在print数组时不传递len过去将会导致无法获取数组的长度从而无法访问到数组中所有的元素想要在函数中访问数组中的所有元素必要的一点就是将数组的长度通过参数的方式传进去。 原因如下: 这是因为一维数组在传递时其长度是传递不过去的所以我们通过 len来传递数组中的元素个数。实际数组名中存储的是数组的首地址在调用函数传递时是将数组的首地址给了变量 b其实变量 b 是指针类型具体原理会在指针节讲解在 b[]的方括号中填写任何数字都是没有意义的。这时我们在 print 函数内修改元素 b[4]20可以看到数组 b 的起始地址和 main 函数中数组 a 的起始地址相同即二者在内存中位于同一位置当函数执行结束时数组 a 中的元素 a[4]就得到了修改。
三、Scanf与字符数组
1.字符数组初始化
字符数组的定义方法与前面介绍的一维数组类似。例如 char c[10]; 字符数组的初始化可以采用以下方式。 1对每个字符单独赋值进行初始化。例如 c[0]‘I’;c[1]’ ‘;c[2]‘a’;c[3]‘m’;c[4]’;c[5]‘h’;c[6]‘a’;c[7]‘p’;c[8]‘p’;c[9]‘y’; 2对整个数组进行初始化。例如 char c[10]{‘I’,‘a’,‘m’,‘h’,‘a’,‘p’,‘p’,‘y’} 工作中一般不用以上两种初始化方式因为字符数组一般用来存取字符串。通常采用的初始化方式是 char c[10] “hello”。因为 C 语言规定字符串的结束标志为’\0’而系统会对字符串常量自动加一个’\0’为了保证处理方法一致一般会人为地在字符数组中添加’\0’所以字符数 组存储的字符串长度必须比字符数组少 1 字节。例如char c[10]最长存储 9 个字符剩余的 1 个字符用来存储’\0’。如果末尾没有存储\0将会可能导致程序出现以下错误一直沿着字符数组打印直到内存中存储\0为止。
2.scanf读取字符
最明显的就是scanf碰见空白字符会直接阻断导致一句话有空格时没办法直接用scanf把那句话读进字符数组。可以通过对下面一个小程序输入 how are you 体会体会。
//
// Created by Zhu Shichong on 2023/1/9.
//
#include stdio.h
//scanf 读取字符串时使用%s
int main()
{char c[10];char d[10];scanf(%s,c);printf(%s\n,c);scanf(%s%s,c,d);printf(c%s,d%s\n,c,d);return 0;
}最好的解决方法就是使用gets,puts函数
gets 函数类似于 scanf 函数用于读取标准输入。前面我们已经知道 scanf 函数在读取字符串时遇到空格就认为读取结束所以当输入的字符串存在空格时我们需要使用 gets 函数进行读取。 gets 函数的格式如下 char *gets(char *str); gets 函数从 STDIN标准输入读取字符并把它们加载到 str字符串中直到遇到换行符\n。如下例所示执行后我们输入how are you共 11 个字符可以看到 gets 会读取空格同时可以看到我们并未给数组进行初始化赋值但是最后有’\0’这是因为 gets 遇 到\n 后不会存储\n而是将其翻译为空字符’\0’。 puts 函数类似于 printf 函数用于输出标准输出。puts 函数的格式如下 int puts(char *str); 函数 puts 把 str字符串写入 STDOU标准输出。puts 会将数组 c 中存储的how are you字符串打印到屏幕上同时打印换行相对于 printf 函数puts 只能用于输出字符串同时多打印一个换行符等价于 printf(“%s\n”,c)
四、字符数组相关函数
str 系列字符串操作函数主要包括 strlen、strcpy、strcmp、strcat 等。strlen 函数用于统计字符串长度strcpy 函数用于将某个字符串复制到字符数组中strcmp 函数用于比较两个字符串的大小strcat 函数用于将两个字符串连接到一起。 函数原型如下 头文件 #include string.h size_t strlen(char *str); char *strcpy(char *to, const char *from); int strcmp(const char *str1, const char *str2); char *strcat(char *str1, const char *str2); 对于传参类型 char*直接放入字符数组的数组名即可。
注意以下几点
strlen 函数的计算原理是通过判断结束符来确定字符串的长度。strcpy 函数用来将字符串中的字符逐个地赋值给目标字符数组。例中我们将 c 复制给 d就是将 c 中的每个字符依次赋值给 d也会将结束符赋值给 d。注意目标数组一定要大于字符串大小即 sizeof(d)strlen(c)否则会造成访问越界strcmp 函数用来比较两个字符串的大小首先比较首字母ASCII值第一个相同比较第二个依次类推由于字符数组 c 中的字符串与 d 相等所以这里的返回值为 0。如果 c 中的字符串大于 d那么返回值为c与d中不同位字符相减即 c-d正值如果 c 中的字符串小于 d那么返回值依旧为c-d负值。如何比较两个字符串的大小呢具体操作是从头开始比较相同位置字符的 ASCII码值若发现不相等则直接返回否则接着往后比较。例如strcmp(“hello”,“how”)的返回值是−1即hello小于how因为第一个字符 h 相等接着比较第二个位置的字符e 的 ASCII 码值小于 o 的然后返回e-o。不同的标准中有可能返回值不同但一定是两字符串相同返回0前面小后面大返回负值否则返回正值。strcat 函数用来将一个字符串接到另外一个字符串的末尾。例中字符数组 c 中存储的是hello我们将 d 中的world与 c 拼接最终结果为helloworld。注意目标数组必须大于拼接后的字符串大小即 sizeof(c)strlen(“helloworld”)。 今天的分享到此结束