C:知识点:一维数组、二维数组、字符数组(不断更新)

mac2024-11-13  13

经常用下面的方式定义数组 #define N 10 int a[N];

也经常在定义数组的时候就给初值

#define N 10 int a[N]={1,3,5,7,9}; 给了初值,就可以不写长度,下面默认数组长度是5 #define N 10 int a[]={1,3,5,7,9}; 定义数组时,[ ]里不能有变量,也不能为实型。使用数组元素时,a[0]~a[9]是合法的数组元素。使用数组元素时,[ ]可以有变量,a[i],而且用的特别多经常要对数组遍历,也就是每个元素访问一遍同时做点什么事儿,打印、求和之类的,用for循环很适合 for(i=0;i<N;i++)/*遍历整个数组,打印每个元素并求元素之和*/ { printf("%d",a[i]); sum+=a[i]; } C语言对数组元素越界没有约束,比如 ​#define N 5 int a[N]={1,3,5,7,9};

合法的元素是a[0]~a[4],但是,你可以使用a[5],a[6],甚至a[-1]等这些元素,某些编译器会给警告,但是能运行,这种写法会带来一些问题,因为你引用的元素的空间可能是别的变量的空间,如果对它修改,会引发意想不到的结果。我们称这种现象为数组的越界。我们可以越界,但是越界的带来的错误和后果由程序员承担,C语言不负责。

 数组名是数组的首地址,也是第一元素a[0]的地址,即a&a[0]它俩是一样的。首地址是干嘛的呢?因为定义了数组,就要给它分配内存空间,先计算一下要给多少个字节,比如int型,长度10的话,就是40个字节,系统算好字节数之后,找连续的尚未使用的40个字节给数组用,此时,系统里会标注,当下的这40个字节已经分配使用了,而且是4个字节为一个单位用的,不能把这40个字节再次分配给别的变量用,并且,第1个字节的地址就是用数组名a标记。以后,要想对这10个元素操作,统统用a来表示:比如a[3],则从a地址开始,找3(个)*4(每个4字节)=12个字节处,这个位置正好是a[3]的第1个字节的地址,然后,从这个地址开始连续取4个字节,给a[3]用。以上是编译器做的事儿。可以看出来,系统管理一大段连续的内存空间,只需要标注哪里是首地址,然后每次都从首地址,也就是a,开始计算到底是哪个元素。编译器对a的处理,是当成常量看的,因为给你分配的这些内存空间是哪些,这个在本次程序运行期间是不会变化的,所以a是常量。平时使用数组时,不必考虑这些细节,但是到了指针,就得结合上面的原理去理解。数组元素怎么用呢,就是跟之前的同数据类型的变量一样使用。每个数组元素其实就是一个变量,只是对数组元素的表达方式有点不一样,要加[ ],还要加下标。

数组名是首地址,地址的处理有对应的指针变量搭配用,最基本的操作就是指针变量保存地址。

 

int *p;//定义指针变量,就是定义的时候多个*,指针变量是p,不是*p int a[10]={1,3,5,7,9}; p=a; 数组的应用题非常多,将来会跟函数,指针一起用。------------------------------------------------------------------------------------------------------------------------------------------------------

数组的常用算法如下

下面这个收纳公式非常重要,非常重要,非常重要 //数组收纳问题的编程套路是(设收纳到数组 a 中): j=0; for (循环a中所有数据) if (某个数据符合收纳条件) a[j++]=该数据; //符合条件的又放到了a数组中,也可以放到别的数组里 // 已收纳数据个数为:j(下一可用空间是a[j])

排序算法非常重要,非常重要,非常重要,本学期掌握两种排序,冒泡排序和选择排序:

1、选择排序:

#include <stdio.h> #define N 6 int main() { int i,j,t,a[N]; for(i=0;i<N;i++) scanf("%d",&a[i]); for(i=0;i<N-1;i++) for(j=i+1;j<N;j++) if(a[i]>a[j])//从小到大排序 { t=a[i]; a[i]=a[j]; a[j]=t; } for(i=0;i<N;i++) printf("%d ",a[i]); }

2、冒泡排序

#include <stdio.h> #define N 6 int main() { int i,j,t,a[N]; for(i=0;i<N;i++) scanf("%d",&a[i]); for(i=0;i<N-1;i++) for(j=0;j<N-1-i;j++)//从小到大排序 if(a[j]>a[j+1]) { t=a[j]; a[j]=a[j+1]; a[j+1]=t; } for(i=0;i<N;i++) printf("%d ",a[i]); }

二个排序是从嵌套循环的第二个for语句开始不一样


二维数组:

int a[3][4];

数组名是a,a也是首地址,也是常量。

也可以看成a是一个长度是3的一维数组,a的每个元素也是一个长度是4的一维数组。

在二维数组里,有a,a[i],a[i][j]三种形式,前两种都是数组名,即地址,最后一种才是数组元素,才能当成变量用。

a与高级指针变量(指向指针的指针变量)搭配用,a[i]与普通指针变量搭配用,a[i][j]与普通变量搭配用。


字符数组:

数组是char类型的,作用是对字符串进行处理,由于字符串是'\0'结尾,所以数组中必须有'\0'才能把数组中的多个字符当成字符串用。

常用算法:

//求字符串长度,和求\0在数组中的下标,是同一个问题 i=0; char str[10]; gets(str);//假设输入abcde,则数组中是 abcde\0 while (str[i]!='\0') i++; //i就是字符串长度或者\0所在位置的下标 或者: for(i=0;str[i]!='\0';i++);//循环体是空语句

用收纳公式处理下列问题:

****abcd**ef***gh*****

1、删除所有*:abcdefgh

【解析】i=0;j=0;所有字符访问一遍:while(str[i])!='\0'),不是星号的:if(str[i]!='*'),留在数组中:str[j++]=str[i];  最后末尾加‘\0’:str[j]='\0';合起来:

 

i=0; j=0; while(str[i]!='\0')//上方红色的i从0开始,到\0结束,表示遍历的范围,这里是所有元素 if(str[i]!='*') str[j++]=str[i];//从下标j=0开始存放新数据 str[j]='\0';//手工加'\0'

2、****abcd**ef***gh*****,删除最前面的*:abcd**ef***gh*****

【解析】还是用收纳公式做,只是要收纳的数据从字母a开始遍历(不是从最开始的*)到\0,先要定位a的下标:

m=0; while(str[m]=='*') m++; //循环结束时,字母a在数组中的下标就是m //这个算法很眼熟,跟求\0位置是不是很相似?

上面求得了字母a的下标,剩下的问题也就解决了。

将1,2题合起来就是答案:

i=m; j=0; while(str[i]=='*') i++; //上面m是求字母a的下标 //下面是第1题的代码,收纳公式 while(str[i]!='\0')//此时i=m,表示从m下标开始到\0结束进行遍历 str[j++]=str[i];//收纳的位置j=0,表示从下标0开始保存。从整体迁移,就不需要if了 str[j]='\0';

 


小总结:****abcd**ef***gh*****

对于这个字符串来说,有几个点位很重要:

\0下标(已解决)第一个非*即字母a的下标(已解决)最后一个字母h的下标n:从前往后找哪个是最后一个非*字符很困难,要从后往前找,即先找到\0下标n(已解决),n--;是最后一个*位置,再while(str[k]=='*') n--; 循环结束时,n刚好是字母h的下标。 n=0; while(str[n]!='\0') n++; //n是\0的下标 n--;//最后一个*下标 while(str[n]=='*') n--; //n是最后一个字母h的下标

所有的问题,都是收纳,如果收纳的数据不是从头到尾,而是一部分的话,需要注意循环的条件(即要访问哪些元素,要控制i)和j的初始位置

那么三个点位(\0,a,h)的下标求解,和收纳算法,牢记之后,这一系列问题就会快速写出代码

根据上图的说明,看一下之前的删除前面的*:

4.删除最后面的*:****abcd**ef***gh

5.删除中间和后面的*:****abcdefgh

6.删除前面和中间的*:abcdefgh*****

7.删除前面和后面的*:abcd**ef***gh

8.删除所有*:abcdefgh

 

 

最新回复(0)