数据结构中线性表的顺序实现,这次的数据元素是字符数组,在这个例子中,好好体会了char * 和字符数组名的区别:
char *name;那么name是一个字符指针变量,注意它是一个变量,是可以进行四则运算的,它的值是可以改变的。所以:
printf("%s",name+20);
没有问题。但是如果是 char name[20];那么name是一个地址常量,值是一个字符串的首地址,注意它是常量,是不能四则运算的,所以这时候:
printf("%s",name[20]);是不行的,name是常量,怎么能+20呢?
实现代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define INITSIZE 100
#define INCREMENT 10
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
typedef struct {
char *elem;
int length;
int size;
}Sqlist;
int main(){
Status InitList(Sqlist *);
Status ListInsert(Sqlist *,int i,char *);
Status ListDelete(Sqlist *,int i, char *);
Sqlist l,*lp=&l;
if(InitList(lp)==1){
printf("线性表初始化成功\n");
}
char a[20]="hello";
char b[20]="good";
char c[20]="moring";
if(ListInsert(lp,1,a)==1){
printf("插入字符串成功\n");//不能写成lp->elem[0]
printf("%s\n",lp->elem;
}
if(ListInsert(lp,2,b)==1){
printf("插入字符串成功\n");
printf("%s\n",lp->elem+20);//不能写成lp->elem[20]
}
if(ListInsert(lp,3,c)==1){
printf("插入字符串成功\n");
printf("%s\n",lp->elem+40);//不能写成lp->elem[40]
}
char name[20];
if(ListDelete(lp,2,name)==1){
printf("成功删除一个元素\n");
printf("%s\t%s\n",lp->elem,lp->elem+20);
printf("被删除的元素是:%s\n",name);
}
}
Status InitList(Sqlist *lp){
lp->elem=(char*)malloc(INITSIZE*sizeof(char [20]));
if(!lp->elem){
exit(ERROR);
}
lp->length=0;
lp->size=INITSIZE;
return OK;
}
Status ListInsert(Sqlist *lp,int i,char *e){
if((i<1)||(i>lp->length+1)){
exit(ERROR);
}
if(lp->length>=lp->size){
char *newbase=(char *)realloc(lp->elem,(lp->size+INCREMENT)*sizeof(char [20]));
if(!newbase){
exit(OVERFLOW);
}
lp->elem=newbase;
lp->size+=INCREMENT;
}
char *q=&(lp->elem[20*(i-1)]);
char *p=&(lp->elem[20*(lp->length-1)]);
for(;p>=q;p-=20){
strcpy(p+20,p);
}
strcpy(q,e);
lp->length++;
return OK;
}
Status ListDelete(Sqlist *lp,int i,char *e){
if((i<1)||(i>lp->length)){
exit(ERROR);
}
char *p=&(lp->elem[20*(i-1)]);
strcpy(e,p);
char *q=lp->elem+20*(lp->length-1);
for(p+=20;p<=q;p+=20){
strcpy(p-20,p);
}
lp->length--;
return OK;
}
运行结果:
线性表初始化成功
插入字符串成功
hello
插入字符串成功
good
插入字符串成功
moring
成功删除一个元素
hello moring
被删除的元素是:good
--------------------------------
Process exited after 2.165 seconds with return value 0
请按任意键继续. . .
然我不解的是:
char *q=&(lp->elem[20*(i-1)]);
可以,但是:
printf("%s\n",lp->elem+20);//不能写成lp->elem[20]
不行
测试了一下:
printf("%s\n",&(lp->elem[0]));
printf("%s\n",&(lp->elem[20]));
却可以正常运行。这是什么原理呢?我们这里至少可以看的出来:&(lp->elem[20])是一个字符型地址,因为它可以被赋值给char q。这就很奇怪了,我能知道printf的格式化打印通常都是直接给出变量名就好的,打印int,char字符,float,double变量的时候,都是给出变量名而不是给出变量地址的。但是%s是与众不同的,我们知道printf不能直接打印其他类和类型的数组。唯独可以依靠字符数组的变量名就把字符数组打印出来。关于printf打印字符数组不需要我们指出字符数组长度却不会越界的原因我之前总结过,挂出链接:
字符数组的\0机制
我在那篇文章里研究了printf打印字符数组不越界的问题。但是没有想清楚另一个问题们就是今天碰到的这个问题。printf在打印%s的时候,需要的其实是一个地址,准确地说是一个字符型地址,这个地址是想要打印地字符串地首地址。打印碰到\0,就结束。所以printf打印字符串地时候是可以用指针的。但是要注意用法:在上面的线性表中:lp->elem是一个字符指针变量。lp->elem+20是什么?还是一个字符指针变量,而printf用%s打印字符串要的就是一个地址量作为开始地点,所以使用lp->elem;lp->elem+40;lp->elem+40都是没问题的。想想lp->elem[0];lp->elem[20];为什么不行。lp->elem[0]不是地址量,而是相当于(lp->elem)。它是一个字符型变量。所以就变成了用%s来打印一个char 量(这个printf中应该用%c),当然是不对的。而**&(lp->elem[0])又是一个地址量**,它是上面那个字符串开头字符的地址。
举个简单的例子:
char name[20]="hello";
char *p=name;
我们都知道**name就是这个字符串都地址,就相当于:&name[0];而p由等价于name,所以p等价于&(name[0])。**而printf需要的就是字符串其实地址:所以:
printf("%s",name);
printf("%s",p);
printf("%s",&name[0]);
printf("%s",&(*p));
都是可以的:
#include<stdio.h>
int main(){
char name[20]="hello";
char *p=name;
printf("%s\n",name);
printf("%s\n",p);
printf("%s\n",&name[0]);
printf("%s\n",&(*p));
return 0;
}
运行结果:
hello
hello
hello
hello
--------------------------------
Process exited after 2.214 seconds with return value 0
请按任意键继续. . .
关于&(p)不好理解,我们可以这样想,&取地址和取值运算是相反的运算,同时作用与p相互抵消了,&(*p)==p。