从零开始学编程---第一步-C语言(二十三)

时间:2014-03-09 00:25    点击:

现在再做一个交换字符串的程序
例如把数组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会排前

来源:幻想编程//所属分类:站长原创/更新时间:2014-03-09 00:25
顶一下
(9)
90%
踩一下
(1)
10%
上一篇:从零开始学编程---第一步-C语言(二十二)
下一篇:从零开始学编程---第一步-C语言(二十四)
相关内容