指针:用来存储变量地址的变量,它本身也是一个变量,只是里面的放的不是整数,不是浮点数,而是变量的内存地址,使用指针可以产生更高效、更紧凑的代码。ASCI用void*(指定void的指针)作为通用指针类型。
指针是指能够存入一个地址的一组存储单元,通常是2个或4个字节。
机器中一般都有一段连续编号或编址的存储单元,我们可以单独或连续的操作它们,比如,机器一般用一个字节来存储字符,用2或个4个字节来存储整型。
int *p:表明*p是一个int型变量,所以p为指向int变量的指针。
*p++与*(p++)是等价的,因为++与*是右结合的。
用*来直接寻址或间接引用指针变量。
因为函数是以传值的方式将参数传递给被调函数,所以真实被调函数里操作的是副本值。因为如果想对被传入的值进行改变,是需要将它们的地址传进去。swap(x,y)用来交换x,y的值,需要这样用swap(&x,&y)。
下面通过一个读取字符串转换成整形的函数说明指针在函数中的应用:
int getint(int *n) { int c; int sign; while(isspace(c=getch())) ; if(!isdigit(c) && c!='-' && c!='+') { ungetch(c); return 0; } sign=(c=='-')?-1:1; if(c=='+'||c=='-') c=getch(); for(*n=0;isdigit(c);c=getch()) { *n=*n*10+(c-'0'); } *n=sign*(*n); if(c!=EOF) ungetch(c); return c; }
数组名实际就是一个地址,因为可以用指针的偏移来代表数组元素的地址:p=&a[0];a[i]=*(p+i);
但是值得注意的是指针它是一个变量,可以执行赋值,自增等操作,但是数组名是一个常量。
当数组作为参数调用函数时,实际上传入的数组的地址,因此跟指针实际上是等价的:f(char array[]) 等价于f(char * array).
有效的指针运算包括相同类型指针之间的赋值运算;指针同整数之间的加法或减法运算;指向相同数组中元素的两个指针间的减法或比较运算;将指针赋值为0或指针与0之间的比较运算。其他所有形式的指针运算都是非法的,例如两个指针间的加法、乘法、除法、移位或屏蔽运算;指针同float或double类型之间的加法运算;不经强制类型转换而直接将指
向一种类型对象的指针赋值给指向另一种类型对象的指针的运算(两个指针之一是void*类型的情况除外)。
下面通过一个缩小版本的内存分配程序来说明指针的这些算术运算。
#include<stdio.h> #define BUFSIZE 1000 static char buf[BUFSIZE]; static char * pbuf=buf; char *alloc(int n) { if(buf+BUFSIZE-pbuf>=n) { pbuf=pbuf+n; return pbuf-n; } else return 0; } void afree(char *p) { if(p>=buf && p<=buf+BUFSIZ) pbuf=p; }
字符串常量可通过一个指向其第一个元素的指针来访问。
书中的这部分通过分别用数组和指针来作为传入参数写了两个字符串的函数strcat和strcmp
void strcpy(char *s,char *t) { while(*s++=*t++) ; } int strcmp(char *s,char *t) { while(*s==*t) { if(*s=='\n') return 0; s++;t++; } return *s-*t; }
指针本身是一个变量,所以可以像其他变量一样储存在一个数组里。这里主要通过一个字符串的排序程序说明一个指针数组的用法,因为在排序的时候,我们并不想真实的移动字符串的位置,所以我们只声明了一个字符串指针数组,使数组的每个指针指向一个字符串,然后排序这个指针。
这里依然使用的是快速排序,只是这里的比较是两个字符串之间的比较。
程序的关键,在于声明一组指向字符串的数组,并指向读入的一系列字符串。
#include<stdio.h> #define MAXLINE 100 #define LIM 1000 int readline(char *s[]); void qsort(char *s[],int left,int right); void linewrite(char *s[],int n); int main(void) { char *s[MAXLINE]; /*指针数组*/ int nlines; while((nlines=readline(s))>0)/*读取所有字符行*/ { qsort(s,0,nlines-1); /*快速排序*/ linewrite(s,nlines); /*显示*/ } }
上面是函数主体部分,我们把读所有字符串(readline),和打印(linewrite)都写成了函数形式,快速排序(sqort)也是一个函数。
下面分开来看各个部分的代码:
int readline(char *s[]) { int getline(char *s,int lim); int nline=0; char str[LIM]; char *p; int n; while((n=getline(str,LIM))>0) { if (nline>=MAXLINE) return -1; else { str[n-1]='\0'; /*去掉换行符*/ p=(char*)malloc(n*sizeof(char)); strcpy(p,str); s[nline++]=p; } } return nline; }
realine函数主要是读取所有行的字符串,并将字符串的指针储存在指针数组里。里面getline为行读取函数,前面已经实现。
而qsort函数只用将声明改变,并改变里面swap函数的就可以了。
void Qsort(char *s[],int left,int right) { void swap(char *s[],int i,int j); int i; int last=left; if(left-right>=0) return; swap(s,left,(left+right)/2); for(i=left+1;i<=right;i++) if(strcmp(s[left],s[i])>0) swap(s,++last,i); swap(s,last,left); Qsort(s,left,last); Qsort(s,last+1,right); } void swap(char *s[],int i,int j) { char *temp; temp=s[i]; s[i]=s[j]; s[j]=temp; }
最后是打印函数:
void linewrite(char *s[],int n) { int i; for(i=0;i<n;i++) printf("%s\n",s[i]); }
很多情况下,我们的数据并不仅限在一维上,例如如果需要保存一个矩阵,那么矩阵的每一行是个一维数组,而矩阵有很多行,所以需要用一个2维数组来表示int matrix[m][n] 其中m表示行数,n表示列数。
下面用一个程序说明多维数组的声明与使用:我们给一个日期(年、月、日)来转换为某年的多少天,相反,给一个天数,转换为某月某日。程序中需要考虑闰年的问题。
static int month_day[2][13]={ {0,31,29,31,30,31,30,31,31,30,31,30,31}, {0,31,28,31,30,31,30,31,31,30,31,30,31} }; int day_of_year(int year,int month,int day) { int leap; int tday=0; int i; if((year%4==0&&year%100!=0)||year%400==0) leap=0; else leap=1; for(i=0;i<month;i++) { tday=tday+month_day[leap][i]; } tday=tday+day; } void month_of_year(int year,int tday,int * month,int * day) { int leap; int i=1; *month=0; if((year%4==0&&year%100!=0)||year%400==0) leap=0; else leap=1; while(tday>month_day[leap][i++]) { tday=tday-month_day[leap][i++]; } *month=i; *day=tday; }
多维数组作为参数传递给函数时,除了第一维可以省略,其他维数不能省。
int (*daytab)[13]是一个二维数组,daytab为数组的指针。
int *daytab[13]是一个有13个元素的指针数组。
static char *name[]={ "Illegal month", "January","February","March", "April","May","June", "July","August","September" "October","November","December" };
注意区别指针数组与二维数组,像5.8中的name就是一个指针数组
int a[10][20];
int *b[10];
a是一个分配了10*20个整型空间的二维数组,而b只是10个指针,这些指针的内容并不知道。
调用主函数main时,实际上它有两个参数argc,argv。int main(int argc,char *argv[]),其中argc为一个整数,表示参数的数目,argv为一个指针数组,是一系参数。
argc>=1,因为当无参数时,argc=1,*argv[1]=程序名。
echo hello,world 这个命令行程序argc=3,argv[0]=”echo”;argv[1]=”hello”,argv[2]=”world”,ASCI规定,argv[argc]=NULL。
int main(int argc,char **argv) { while(--argv>0) printf("%s%s",*++argv,argc>1?" ":""); printf("\n"); return 0; }
int (*cmp)(void*,void*);
cmp就是指向函数的指针。两个参数都是void*类型的。