从零开始学编程---第一步-C语言(二十三)
现在再做一个交换字符串的程序
例如把数组a的元素跟数组b的元素交换
其实很简单,就跟交换变量一样的,你需要额外定义一个作为中间交换的数组
代码如下:
#include <stdio.h>
main()
{
char a[]="aaaa",b[]="bbbb",c[5]="";
for(int i=0;i<5;i++)
{
c[i]=a[i];
a[i]=b[i];
b[i]=c[i];
}
puts(a);
puts(b);
}
下面用指针;
#include <stdio.h>
main()
{
char a[]="aaaa",b[]="bbbb",c[5];
char *p=a,*p1=b,*p2=c;
while(*p2++=*p++);
p=a,p2=c;
while(*p++=*p1++);
p1=b;
while(*p1++=*p2++);
puts(a);
puts(c);
}
其中
while(*p2++=*p++); 把a数组的个个元素赋值到c数组
p=a,p2=c; p指针和p2指针重新归位
while(*p++=*p1++); b数组赋值到a数组
p1=b; p1归位
while(*p1++=*p2++); c数组赋值到b数组
上面while(*p2++=*p++); 这类语句省略了!="\0"
因为\0的ASCⅡ码为0,对于while语句只看表达式的值为非0就循环,为0则结束循环,因此也就可省去“!=`\0'”这一判断部分
下面说说数组指针,即数组首元素地址的指针。也是指向数组的指针。
这似乎有点难以理解,先看看怎么定义的
先定义一个int类型的数组指针
int (*p)[5] 这里括号绝对不能少
它包含5个元素 p[0], p[1],p[2],p[3],p[4].
现在看下面代码
#include <stdio.h>
main()
{
int a[]={1,2,3,4,5};
int (*p)[5]=&a;
printf("%d",(*p)[0]);
}
int (*p)[5]=&a;这句可能难以理解
其实就是数组名a的指针,p是这个数组的数组名
意思是数组名为p的指针数组各个元素指向数组a的各个元素
&a 就是取数组名a的地址,而a是数组第一个元素的地址
其实&a和a的结果是一样的,但这里必须用&a,代表一个数组的地址
a其实是没有空间的,它只是一个地址(别名叫a)
这里也许很难理解,需要深入了解地址和空间的关系
我们定义的变量名或常量名其实就是一个地址的别名而已
不说多了,越说可能会越糊涂,以后再去深究吧
上面的程序最后打印1
其中*p,p,p[0]都是同一地址
p[0], p[1],p[2],p[3],p[4].分别指向(存储)a[0],a[1],a[2],a[3],a[4]的地址
printf("%d",(*p)[0]);这是打印指向a[0]的值,注意这里可不能*p[0],括号是必须加的
还有,数组长度必须保持一致,下面的是错误的
int a[5]={1,2,3,4,5};
int (*p)[6]=&a;
这一部分可能会让你感到头有点晕,没关系,你可以跳过去
下面说的指针数组要容易理解的多
上面说的数组指针其实就是把数组指针化,其元素只能指向普通数组的元素,一般应用在二维数组中,而指针数组是同一类型指针的**,里面的元素不需要都指向另一数组的元素,用法和普通数组的用法一样,只不能数组类型是指针型的
下面我们就定义一个指针数组,定义int类型的指针数组
int *p[5]: 记住里面元素指向类型得一致,全是指向int型
下面程序是给数组元素赋值
#include <stdio.h>
main()
{
int a,b,c,d,e;
int *p[5]={&a,&b,&c,&d,&e};
}
其实就是
p[0]=&a;
p[1]=&b;
p[2]=&c;
p[3]=&d;
p[4]=&e
打印各元素的程序
#include <stdio.h>
main()
{
int a=1,b=2,c=3,d=4,e=5;
int *p[5]={&a,&b,&c,&d,&e};
for(int i=0;i<5;i++)
printf("%d ",*p[i]);
}
和普通数组一样,p是p[0]的地址,*p是p{0}的值(它的值也是个地址);
看,有括号就是数组指针,没括号就是指针数组
接下来我们就运用指针数组做个程序,这个程序的功能是排序字符串
先定义5个字符串数组
char a[]="eeee",b[]="dddd",e[]="cccc",d[]="bbbb",e[]="aaaa"
我们要做的就是按字母顺序排列这5个字符串并打印出来
aaaa
bbbb
cccc
dddd
eeee
字符串比较我之前已经说过,字母可以直接比较,比较的只是它们的ASC编码
例如a的编码是97,b的编码是98,按冒泡排序进行比较
比较只要比较字符串第一个字母就可以了
如果没学过指针数组,我们可能会这样想
a[0]分别比较b[0],c[0],d[0],e[0],如果a[0]比它们大就相互交换
然后又是b[0]开始分别比较。。。这样冒泡比较
说实话,这样非常麻烦,而且连循环也不好用
现在有指针数组就好用了,用5个指针变量分别指向5个字符串
char *p[5]={a,b,c,d,e};也可用char *p[5]={&a[0],&b[0],&c[0],&d[0],&e[0]};
p数组的每个元素都指向5个字符串数组的首地址
知道首地址也就是知道每个字符串第一个字母
我们现在比较a[0]和b[0]就可以在一个数组里进行了
比较a[0]和b[0]在p数组里就是*p[0]和*p[1]的比较
比较之后我们只需要交换p[0]和p[1]的指向地址(也就是它们的值,它们的值是地址)
而不需要对a,b两个字符串数组做任何改动
程序很简单,如下
#include <stdio.h>
main()
{
char a[]="eeee",b[]="dddd",c[]="cccc",d[]="bbbb",e[]="aaaa";
char *p[5]={a,b,c,d,e},*t;
for(int i=0;i<4;i++)
for(int j=i+1;j<5;j++)
{
if(*p[i]>*p[j])
{
t=p[i];
p[i]=p[j];
p[j]=t;
}
}
for(i=0;i<5;i++)
puts(p[i]);
}
t是作为交换变量,由于是地址交换,所以t也要定义为指针类型
冒泡排序还应该记得吧,不记得还得多回去看看
我们现在把上面5个字符串数组修改一下
char a[]="aaaaa",b[]="aaaa",c[]="aaa",d[]="aa",e[]="a";
要求按字符个数排序字符串,也就是长度最小的字符串在最上面
其实和上面的程序差不多,上面的程序是比较第一个字母
现在我们要比较字符串长度,越长的字符串就越排到后面
首先我们要怎么获取一个字符串数组的长度?
这里可以用函数strlen(),意思是string+length(字符串长度)它返回字符串的长度,但并不是说就是数组的长度
因为它不会把‘\0’计算在内,使用它需要引用<string.h>头文件
另外sizeof()操作符(关键字)也可以,它可以计算包括‘\0’在内的所有字符
但是它不是函数,现在我们只用strlen就好了,sizeof()以后再说
先看看它们的用法
#include <stdio.h>
#include <string.h>
main()
{
char a[]="hello";
printf("%d\n",strlen(a));
printf("%d\n",sizeof(a));
}
理解了strlen()的用法我们就可以开始程序了,只需做个小修改
如下:
#include <stdio.h>
#include <string.h>
main()
{
char a[]="aaaaa",b[]="aaaa",c[]="aaa",d[]="aa",e[]="a";
char *p[5]={a,b,c,d,e},*t;
for(int i=0;i<4;i++)
for(int j=i+1;j<5;j++)
{
if(strlen(p[i])>strlen(p[j]))
{
t=p[i];
p[i]=p[j];
p[j]=t;
}
}
for(i=0;i<5;i++)
puts(p[i]);
}
结果就是我们想要的
继续,接232楼
我们还可以把5个字符串数组改一下
char a[]="abcd",b[]="aabc",c[]="bbde",d[]="bacc",e[]="baaa";
然后对5个字符串进行字母大小排序
现在我们不能再去按第一个字母进行比较了,因为你会发现第一个字母都有相同的
那我们就按字母进行依次进行比较
比如先比较字符串数组a和b
abcd比aabc 第一个字母相同,我们接这就比较它们的第2个字母
b比a aabc的第2个字母比abcd的第2个字母小所以,aabc排在前面
逻辑很简单,只需要加个判断,代码如下
#include <stdio.h>
main()
{
char a[]="abcd",b[]="aabc",c[]="bbde",d[]="cacc",e[]="baaa";
char *p[5]={a,b,c,d,e},*t;
for(int i=0;i<4;i++)
for(int j=i+1;j<5;j++)
{
if(*p[i]==*p[j]) //判断第一个字母是否相同
{ p[i]++,p[j]++; //如果相同,字母移动到第2位
if(*p[i]>*p[j]) //接着比较字母的大小
{
t=p[i];
p[i]=p[j];
p[j]=t;
}
p[i]--,p[j]--; //比较结束后,指针重新指向第一个字母
}
if(*p[i]>*p[j]) //比较字母的大小
{
t=p[i];
p[i]=p[j];
p[j]=t;
}
}
for(i=0;i<5;i++)
puts(p[i]);
}
输出结果为:
aabc
abcd
bacc
baaa
bbde
楼上的代码比较麻烦,并且不能比较第3位字母
比如bacc和baaa bacc就在baaa的前面
我们应该用一个循环,只要第一个字母相等,指针就自动指向下一个字母,循环到直到字母不相等就结束循环
while(*p[i]==*p[j])
{
p[i]++,p[j]++,k++;
if(*p[i]!=*p[j])
break;
}
这里的k是什么意思?只是记录循环次数而已,只有知道循环了多少次,才能让指针回归到字母第一位
因为当指针指向下一个字母的时候,或者下下个字母的时候,它是不会自动还原的
我们需要手动让它重新指向第一个字母,好让下一轮循环接着从第一个字母比较起
while()
{
p[i]--,p[j]--,k--;
if(k==0)
break;
}
这里要用什么条件让它进行还原循环呢?用一个循环开关就可以了
现在看代码:
#include <stdio.h>
main()
{
char a[]="abcd",b[]="aabc",c[]="bbde",d[]="bacc",e[]="baaa";
char *p[5]={a,b,c,d,e},*t;
int on,k=0; //on为循环开关,k为循环计数
for(int i=0;i<4;i++)
for(int j=i+1;j<5;j++)
{
on=0; //每进行一轮循环,默认on为0(1为开,0为关)
while(*p[i]==*p[j])
{
on=1; //表示这个循环已开启
p[i]++,p[j]++,k++; //如果字母相等,指向指向下一字母,k记录循环次数
if(*p[i]!=*p[j]) //字母不相等的时候结束循环
break;
}
if(*p[i]>*p[j]) //比较字母的大小
{
t=p[i];
p[i]=p[j];
p[j]=t;
}
while(on) //当on不为0时,表示on被开启过 或者while(on==1)
{
p[i]--,p[j]--,k--; // 重新指向第一个字母,k也重新回归到0
if(k==0)
break;
}
}
for(i=0;i<5;i++)
puts(p[i]);
}
我们设置一个整型变量on,每次FOR循环开始时默认为0,只要判断字母相等的while循环执行过,就自动赋值1,接着字母比较排序,到最后再来个循环判断,如果on为1,就表示判断字母相等的循环执行过,所以我们需要还原指针,接着用循环让指针还原,k是记录循环次数,指针往后移动多少次,k就会记录多少次,还原的时候只要让k递减等于0就可以,指针就会按k的数字递减回到初始的地方
注意if(k==0)不能if(k=0) 一个是相等一个是赋值
最后输出为:
aabc
abcd
baaa
bacc
bbde
其实这个程序并不理想,但初期我们只要达成程序目的就可以
到了中期我们就要注意代码的美观和效率等等了
现在主要是培养逻辑思维
继续,怎么让5个字符串数组进行自身的排序
char a[]="abcd",b[]="aabc",c[]="bbde",d[]="bacc",e[]="baaa";
就是把这5个字符串重新按字母顺序排列
a,b,c数组都不要排列了
排列的只是d和e数组
d数组需要排列为abcc,e数组需要排列为aaab
如果是一个数组,大家应该知道应该怎么做,就是a{0}比a[1]这样的冒泡排序下去
这个有5个数组,我不可能弄5个冒泡排序,要是有100个数组,那不得累死
所以我们还是用指针数组解决这个问题
只用一个冒泡排序循环就完成5个字符串数组的排序
在此之前,我们先看下面这个程序
#include <stdio.h>
main()
{
char a[]="dcba";
char *p=a,t;
int i,j;
for(i=0;i<3;i++)
for(j=i+1;j<4;j++)
{
if(*(p+i)>*(p+j))
{
t=*(p+i);
*(p+i)=*(p+j);
*(p+j)=t;
}
}
puts(a);
}
这是用指针来完成一个字符串数组的排序
指针p指向a数组,p就指向a[0]的地址,p+1就相当于a[1]的地址
不用指针我们通常是a[0]>a[1]
用了指针后*p>*(p+1)和a[0]>a[1]是一样的
数组
d c b a
地址1000 1001 1002 1003
指针p p+1 p+2 p+3
注意如果进行交换的话不能交换地址,因为数组的地址是连续的,所以只能交换地址对应的值
指针p是一个指针变量,但p+1不是指针变量,虽然它代表的是c的地址,也就是a[1]的地址,但是不表示它存储着a[1]的地址,这里你不能把它看做整体
所以如果你硬是是要交换p和p+1地址,是不可能的
可是*p和*(p+1)可以交换,这里是交换值,这里你要把*(p+1)看做一个整体,它就是一个值,*(p+1)就是a[1]的值c,实际也就是a[0]和a[1]值之间交换
理解了这些,相信你也能看懂这个程序了,最后a数组本身也会完成排序,因为是根据地址来交换的
char a[]="abcd",b[]="aabc",c[]="bbde",d[]="cacc",e[]="baaa";
char *p[5]={a,b,c,d,e};
一个指针数组把指向各个数组的指针**起来
p表示p[0]的地址,即p=&p[0],*p表示p[0]的值(也是个地址),它的值就是a[0]的地址
**p(二级指针,以后讲解)表示p[0]指向地址的值,也就是a[0]的值
*p[0]也是表示a[0]的值
p[0]+1也就是a+1,*(p[0]+1)就是*(a+1),即a[1]
这里确实会有一点晕,需要慢慢来,到了二维数组指针,会更晕,做好坚持的准备吧
首先是a数组的排序比较
*p[0]比*(p[0]+1) 后面就知道该怎么比较了把
b数组则是
*p[1]比*(p[1]+1) 应该已经明了了
ok,我们需要一个3层循环
代码如下:
#include <stdio.h>
main()
{
char a[]="abcd",b[]="aabc",c[]="bbde",d[]="bacc",e[]="baaa";
char *p[5]={a,b,c,d,e},t;
int i,x,y;
for(i=0;i<5;i++)
for(x=0;x<3;x++)
for(y=x+1;y<4;y++)
{
if(*(p[i]+x)>*(p[i]+y))
{
t=*(p[i]+x);
*(p[i]+x)=*(p[i]+y);
*(p[i]+y)=t;
}
}
for(i=0;i<5;i++)
puts(p[i]);
}
x,y循环大家都知道,这是个冒泡排序循环
i循环则是循环p[0]~p[4]
能看懂说明进步很大了
最后打印
abcd
aabc
bbde
abcc
aaab
可以把这5个字符串先自身排序再进行头字母排序,很简单,复制一下就OK了
代码如下:
#include <stdio.h>
main()
{
char a[]="abcd",b[]="aabc",c[]="bbde",d[]="bacc",e[]="baaa";
char *p[5]={a,b,c,d,e},t,*t1;
int i,x,y,on,k=0;
for(i=0;i<5;i++)
for(x=0;x<3;x++)
for(y=x+1;y<4;y++)
{
if(*(p[i]+x)>*(p[i]+y))
{
t=*(p[i]+x);
*(p[i]+x)=*(p[i]+y);
*(p[i]+y)=t;
}
}
for(i=0;i<4;i++)
for(int j=i+1;j<5;j++)
{
on=0;
while(*p[i]==*p[j])
{
on=1;
p[i]++,p[j]++,k++;
if(*p[i]!=*p[j])
break;
}
if(*p[i]>*p[j])
{
t1=p[i];
p[i]=p[j];
p[j]=t1;
}
while(on)
{
p[i]--,p[j]--,k--;
if(k==0)
break;
}
}
for(i=0;i<5;i++)
puts(p[i]);
}
还可以让我们自己输入5个字符串来进行比较
如果输入的的每个字符串都是4个字符的长度,那就不需要改什么
但是假如我们输入的字符串有长有短,那就要考虑一下了
特别是循环判断的时候
for(x=0;x<3;x++)
for(y=x+1;y<4;y++)
这个冒泡排序的循环是针对4个字符长度的字符串数组
如果是其他长度的字符串数组怎么办
这就需要strlen()函数,之前说过的,获取字符串长度
char a[10]="aa"; strlen(a)
strlen获取的长度是2,而非10,它是获取数组里存在的字符长度(不包括'\0')
现在就知道该怎么做了。
程序如下:
#include <stdio.h>
#include <string.h>
main()
{
char a[20],b[20],c[20],d[20],e[20];
char *p[5]={a,b,c,d,e},t,*t1;
int i,x,y,on,k=0;
//输入
puts("请输入5个字符串");
for(i=0;i<5;i++)
{
printf("第%d个:",i+1);
gets(p[i]);
}
//数组自身的排序
for(i=0;i<5;i++)
for(x=0;x<strlen(p[i])-1;x++)
for(y=x+1;y<strlen(p[i]);y++)
{
if(*(p[i]+x)>*(p[i]+y))
{
t=*(p[i]+x);
*(p[i]+x)=*(p[i]+y);
*(p[i]+y)=t;
}
}
//5个数组按头字母顺序排序
for(i=0;i<4;i++)
for(int j=i+1;j<5;j++)
{
on=0;
while(*p[i]==*p[j])
{
on=1;
p[i]++,p[j]++,k++;
if(*p[i]!=*p[j])
break;
}
if(*p[i]>*p[j])
{
t1=p[i];
p[i]=p[j];
p[j]=t1;
}
while(on)
{
p[i]--,p[j]--,k--;
if(k==0)
break;
}
}
//打印
puts("字符串排序之后的结果为:");
for(i=0;i<5;i++)
puts(p[i]);
}
这里需要注意一下,如果我们输入a和ab
a和ab进行头字母比较的时候,会移动到下一个字母
a它的指针会移动到'\0'位置
'\0'会比较b,‘\0’ASC码比b小,所以a会排前
最新内容
热点内容
- QQ群
-
微信
- 返回首页
- 返回顶部