指针是一种保存变量地址的变量。
5.1 指针与地址
通常情况下,机器的一个字节可以存放char类型的数据 两个相邻的字节可以存储一个short类型的数据
指针是能够存放一个地址的一组存储单元(通常是两个或4个字节)
一元运算符&可用于取一个对象的地址,因此: p=&c 把c的地址赋值给p p为指向c的指针
地址运算符&只能应用于内存中的对象,即变量与数组元素,不能作用于表达式、常量或register变量。
一元运算符*是间接寻址或间接引用运算符。 当它作用于指针时,将访问指针所指向的对象。
int x=1,y=2,z[10];
int *ip; /*ip是指向int类型的指针*/
ip=&x; /*ip指向x*/
y=*ip; /*y=1*/
*ip=0; /*x=0*/
ip=&z[0]; /*ip现在指向z[0]*/int *ip 这样声明时为了便于记忆。该声明语句表明表达式*ip的结果是int类型。
对函数的声明也采取这种方式 double *dp,atof(char *);
我们应该注意,指针只能指向某种特定类型的对象,也就是说每个指针都必须指向某种特定的数据类型。
如果指针ip指向整型变量x,那么x出现的任何上下文中都可以使用*ip
一元运算符*和&的优先级比算术运算符的优先级高。
y=*ip+1 将把ip指向的对象的值取出并加1,然后赋值给y
*ip+=1 将ip指向的对象的值加1 等同于++*ip 或 (*ip)++ (括号必须加,因为*和++这样的一元运算符遵循从右至左的结合顺序)
指针也是变量,可以直接使用。
iq=ip 则把ip中的值拷到iq中,这样,指针iq也指向ip所指的对象。
5.2 指针与函数参数
#include <stdio.h>
void swap(int *px,int *py);
int main(){
int a=1;
int b=2;
swap(&a,&b);
printf("%d\n",a);
printf("%d\n",b);
}
void swap(int *px,int *py){
int temp;
temp=*px;
*px=*py;
*py=temp;
}主调程序将指向变量的指针传递给swap
swap函数的参数声明为指针,并且通过指针访问指向的操作数
5.3 指针与数组
在c语言中,指针和数组之间的关系十分密切。
通过数组下标所能完成的任何操作都可以通过指针来实现。
int a[10] 定义了一个长度为10的数组a a[i]表示该数组的第i个元素。
如果pa的声明为 int *pa;
则说明它是一个指向整型对象的指针 那么,赋值语句pa=&a[0] 指向数组的第0个元素 pa的值为数组元素a[0]的地址
x=*pa 把数组a[0]的值复制到x中
*(pa+i) 是数组元素a[i]的内容
pa=&a[0] 也可以写成 pa=a
但是数组名和指针之间有一个不同之处。 指针是一个变量,因此,在C语言中,pa=a和pa++是合法的
但是 a=pa和a++是不合法的
5.4 地址算术运算
一个不完善的存储分配程序 alloc(n)返回一个指向连续n个字符存储单元的指针
afree(p) 释放已分配的存储空间
#define ALLOCSIZE 1000
static char allocbuf[ALLOCSIZE];
static char *allocp=allocbuf;
char *alloc(int n){
if(allocbuf+ALLOCSIZE-allocp>=n){
allocp+=n;
return allocp-n;
}
else
return 0;
}
void afree(char *p){
if(p>=allocbuf&&p<allocbuf+ALLOCSIZE)
allocp=p;
}指针的减法运算是有意义的:如果p和q指向同数组中的元素,且p<q,那么q-p+1就是位于p和q指向的元素之间的元素的数目
5.5 字符指针与函数
练习5-4 编写函数strend(s,t)。如果字符串t出现在s的尾部,则返回1,否则返回0;
#include <stdio.h>
int strend(char *s,char *t);
int strend(char *s,char *t){
char *temp1=s; //用temp1保存在比较前的s
char *temp2=t; //用temp2保存比较前的t
for(;*s!='\0';s++){
temp1=s;
for(;*t!='\0'&&*t==*s;t++,s++);
if(*s=='\0'&&*t=='\0')
return 1;
s=temp1;
t=temp2; //因为此时t可能已经往前走了,所以需要还原
}
return 0;
}
int main(){
char *s="hello";
char *t="llo";
char *u="h";
printf("%d\n",strend(s,t));
printf("%d\n",strend(s,u));
}练习5-5
/*练习5-5 实现strncpy、strncat、strncmp,它们最多对参数字符串中的前n个字符进行操作。*/
#include <stdio.h>
void strncpy(char *s,char *t,int n); //将t中最多前n个字符进行操作
void strncat(char *s,char *t,int n);
int strncmp(char *s,char *t,int n);
#include <stdio.h>
void strncpy( char *s, char *t, int n )
{
while ( n-- && ( *s++ = *t++ ) ) //&&的优先级高于=,所以要加上括号
;
*s = '\0';
}
void strncat(char *s,char *t,int n){
while(*s!='\0'){
s++;
};
while((*s++=*t++)&&n--);
*s='\0';
}
int strncmp(char *s,char *t,int n){
for(;*s==*t&&n--;s++,t++);
if(*s=='\0')
return 0;
return *s-*t;
}
int main(){
char s[20]="now is";
char t[]=" the time";
//strncpy(s,t,10);
//printf("%s\n",s);
strncat(s,t,5);
printf("%s\n",s);
}5.6 指针数组和指向指针的指针
例题 对不同长度的字符串进行排序
#include <stdio.h>
#include <string.h>
#define MAXLINE 5000
char *lineptr[MAXLINE];
int readlines(char *lineptr[],int nlines);
void writelines(char *lineptr[],int nlines);
void qsort(char *lineptr[],int left,int right);
int main(){
int nlines;//输入的行数
if((nlines=readlines(lineptr,MAXLINE))>=0){
qsort(lineptr,0,nlines-1);
writelines(lineptr,nlines);
return 0;
}else
{
printf("error:input too big to sort");
return -1;
}
}
#define MAXLEN 1000
int getline(char *,int );
extern char *alloc(int);
/*readlines函数 读取输入行*/
int readlines(char *lineptr[],int maxlines){
int len,nlines;
char *p,line[MAXLEN];
nlines=0;
while((len=getline(line,MAXLEN))>0){
if(nlines>=maxlines||(p=alloc(len))==NULL)
return -1;
else{
line[len-1]='\0'; //删除换行符
strcpy(p,line);
lineptr[nlines++]=p;
}
}
return nlines;
}
/*writelines函数:写输出行*/
void writelines(char *lineptr[],int nlines){
int i;
for(i=0;i<nlines;i++)
printf("%s\n",lineptr[i]);
}
int getline(char *line,int lim){
int c,i;
for(i=0;i<lim-1&&(c=getchar())!=EOF&&c!='\n';++i)
*line++=c;
if(c=='\n'){
*line++=c;
++i;
}
*line='\0';
return i;
}
/*qsort函数:按递增顺序对v[left]...v[right]进行排序*/
void qsort(char *v[],int left,int right){
int i,last;
void swap(char *v[],int i,int j);
if(left>=right)
return;
swap(v,left,(left+right)/2);
last=left;
for(i=left+1;i<=right;i++)
if(strcmp(v[i],v[left])<0)
swap(v,++last,i);
swap(v,left,last);
qsort(v,left,last-1);
qsort(v,last+1,right);
}
/*swap函数:交换v[i]和v[j]*/
void swap(char *v[],int i,int j){
char *temp;
temp=v[i];
v[i]=v[j];
v[j]=temp;
}5.7 多维数组
#include <stdio.h>
static char daytab[2][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
int day_of_year(int year,int month,int day){
int i,leap=0;
leap=(year%4==0&&year%100!=0||year%400==0);
if(month<1||month>12)
return -1;
if(day<1||day>daytab[leap][month])
return -1;
for(i=0;i<month;i++)
day+=daytab[leap][i];
return day;
}
/*month_day函数:将某年中第几天的日期表示形式转换为某年某月的表示形式*/
void monty_day(int year,int yearday,int *pmonth,int *pday){
int i,leap=0;
if(year<1){
*pmonth=-1;
*pday=-1;
return;
}
leap=(year%4==0&&year%100!=0||year%400==0);
for(i=0;yearday>daytab[leap][i];i++)
yearday-=daytab[leap][i];
if(i>12&&yearday>daytab[leap][i]){
*pmonth=-1;
*pday=-1;
}else{
*pmonth=i;
*pday=yearday;
}
}
int main(){
printf("%d",day_of_year(7,2,29));
}如果将二维数组作为参数传递给函数,那么在函数的参数声明中必须指明数组的列数,和行数没有太大关系。
因为函数调用时传递的是一个指针,它指向由行向量构成的一维数组
f(int daytab[2][13]){.....}
可以写成 f(int daytab[][13]) {......}
也可以写成 f(int (*daytab)[13]) {.....} 表明参数是一个指针,它指向具有13个整型元素的一维数组
5.8 指针数组的初始化
/*month_name函数:返回第n个元素的名字*/
char *month_name(int n){
static char *name[]={
"Illegal name","January","Feb","Mar","Apr","May",
"June","July","Aug","Sep","Oct","Nov","December"
};
return (n<1||n>12)?name[0]:name[n];
}5.9 指针数组和多维数组
要注意指针数组和多维数组的区别
练习5-9 用指针方式代替数组下标方式改写函数day_of_year和month_day
#include <stdio.h>
static char daytab[2][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
int day_of_year(int year,int month,int day){
int leap=0;
char *p;
leap=(year%4==0&&year%100!=0||year%400==0);
if(month<1||month>12)
return -1;
if(day<1||day>daytab[leap][month])
return -1;
p=daytab[leap];
while(month--)
day+=*++p;
return day;
}
/*month_day函数:将某年中第几天的日期表示形式转换为某年某月的表示形式*/
void monty_day(int year,int yearday,int *pmonth,int *pday){
int leap=0;
char *p;
if(year<1){
*pmonth=-1;
*pday=-1;
return;
}
leap=(year%4==0&&year%100!=0||year%400==0);
p=daytab[leap];
while (yearday>*++p)
{
yearday-=*p;
}
*pmonth=p-*(daytab+leap);
*pday=yearday;
}
int main(){
printf("%d",day_of_year(7,2,29));
}5.10 命令行参数
在支持C语言的环境中,可以在程序开始执行时将命令行参数传给程序。调用主函数main时,它带有两个参数,第一个参数(习惯上称为argc,用于参数计数)的值表示运行程序时命令行中参数的数目;第二个参数(称为argv,用于参数向量)是一个指向字符串数组的指针,其中每个字符串对应一个参数。通常用多级指针处理这些字符串。
argv[0]的值是启动该程序的程序名,因此argc的值至少为1
第一个可选参数为argv[1],最后一个为argv[argc-1]
lingwai ,ANSI标准要求argv[argc]的值必须为一个空指针
#include <stdio.h>
/*回显程序命令行参数:版本2*/
main(int argc,char *argv[]){
while(--argc)
printf("%s%s",*++argv,(argc>1)?" ":"");
printf("\n");
return 0;
}
printf((argc>1)?"%s ":"%s",*++argv);
第二个例子: 增强4.1节中pattern查找程序的功能。
在4.1节中,我们将pattern内置到程序中了。效仿程序grep中的实现方法改写该程序。
通过命令行的第一个参数指定pattern
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char *line,int max);
int main(int argc,char *argv[]){
char line[MAXLINE];
int found=0;
if(argc!=2)
return -1;
else
{
while (getline(line,MAXLINE))
{
if(strstr(line,argv[1])!=NULL){
printf("%s",line);
found++;
}
}
}
return found;
}
UNIX系统中的C语言程序有一个公共的约定:以负号开头的参数表示一个可选标志或参数。 假定用-x表示打印所有与模式不匹配的文本行,用-n表示打印行号
例如:
find -x -n [pattern]
将打印所有与模式不匹配的行,并在每个打印行的前面加上行号
改写后的程序如下
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char *line,int max);
/*find函数:打印所有与第一个参数指定的模式相匹配的行*/
main(int argc,char *argv[]){
char line[MAXLINE];
long lineno=0;
int c,except=0,number=0,found=0;
while(--argc>0&&(*++argv)[0]=='-') /*在处理每个可选参数前,argc进行自减运算,argv执行自增运算。 如果没有错误, 则argc表示还没有处理的参数数目
argv指向未处理参数中的第一个。 所以此时argc的值为1,*argv指向pattern*/
while (c=*++argv[0])
switch (c)
{
case 'x':
except=1;
break;
case 'n':
number=1;
break;
default:
printf("find: illegal option %c\n",c);
argc=0;
found=-1;
break;
}
if(argc!=1)
printf("usage: find -x -n pattern\n");
else
while (getline(line,MAXLINE))
{
lineno++;
if((strstr(line,*argv)!=NULL)!=except){
if(number)
printf("%ld:",lineno);
printf("%s",line);
found++;
}
}
return found;
}注意:
(*++argv)[0]和*++argv[0]
*++argv是一个指向参数字符串的指针, 因此 (*++argv)[0]是它的第一个字符 还可以写成 **++argv
因为[]与操作数的结合比*和++高,所以
*++argv[0] 是遍历一个特定的参数串
练习
练习5-10 从命令行输入逆波兰表达式并计算
#include <stdio.h>
#include <math.h>
#define MAXOP 100 //表达式的最大长度
#define NUMBER '0' //标记数字
//对栈的操作
void push(double); //入栈
double pop(void); //取出
int getop(char []); //获取操作数或符号
void ungets(char []);
int main(int argc,char *argv[]){
char s[MAXOP];
double op2;
while (--argc)
{
ungets(" "); //表示该参数结束
ungets(*++argv); //参数放入缓存
switch (getop(s))
{
case NUMBER:
push(atof(s));
break;
case '+':
push(pop()+pop());
break;
case '*':
push(pop()*pop());
break;
case '-':
op2=pop();
push(pop()-op2);
break;
case '/':
op2=pop();
if(op2==0.0)
printf("error");
else
push(pop()/op2);
break;
default:
printf("error:unknown command");
argc=1;
break;
}
}
printf("\t%.8g\n",pop());
}/*对栈的操作*/
#include <stdio.h>
#define MAXVAL 100
int sp=0;
double val[MAXVAL];
/*把f压入到栈中*/
void push(double f){
if(sp<MAXVAL)
val[sp++]=f;
else
printf("error:stack full");
}
/*弹出并返回栈顶值*/
double pop(void){
if(sp>0)
return val[--sp];
else{
printf("error: stack empty");
return 0.0;
}
}
/*getop.c*/
#include <ctype.h>
#include <string.h>
#include <stdio.h>
int getop(char s[]){
int c;
while ((*s=c=getch())!=' '||c=='\t');
*++s='\0';
if(!isdigit(c)&&c!='.')
return c;
if(isdigit(c))
while (isdigit(*s++=c=getch()))
;
if(c=='.')
while (isdigit(*s++=c=getch()))
;
*s='\0';
if(c!=EOF)
ungetch(c);
return NUMBER;
}
#include <stdio.h>
#include <string.h>
#define BUFSIZE 100
char buf[BUFSIZE];
int bufp=0;
int getch(void){
return (bufp>0)? buf[--bufp]:getchar();
}
void ungetch(int c){
if(bufp<BUFSIZE)
buf[bufp++]=c;
else
printf("too many characters");
}
void ungets(char s[]){
int len=strlen(s);
void ungetch(int);
while(len>0)
ungetch(s[--len]);
}
练习 5-11
修改程序entab和detab(第一章中的函数),使它们接受一组作为参数的制表符停止位。如果启动程序时不带参数,则使用默认的制表符停止位设置。
settab函数 将参数设置到tab[]中。
tabpos函数 返回tab[i]是否是'\t'
entab函数 改写自原来程序
#include <stdlib.h>
#define MAXLINE 1000
#define TABINC 8
#define YES 1
#define NO 0
/*settab: set tab stops in array tab*/
void settab(int argc,char *argv[],char *tab){
int i,pos;
if(argc<=1){
for(i=0;i<MAXLINE;i++){
if(i%TABINC==0)
tab[i]=YES;
else
{
tab[i]=NO;
}
}
}
else
{
for(i=0;i<MAXLINE;i++)
tab[i]=NO;
while (--argc)
{
pos=atof(*++argv);
if(pos>0&&pos<=MAXLINE)
tab[pos]=YES;
}
}
}#define MAXLINE 1000
#define YES 1
int tabpos(int pos,char *tab){
if(pos>MAXLINE)
return YES;
else
return tab[pos];
}#include <stdio.h>
#define MAXLINE 1000
#define TABINC 8
#define YES 1
#define NO 0
void settab( int argc,char *argv[],char *tab);
int tabpos(int pos,char *tab);
void detab(char *tab);
int main(int argc,char *argv[]){
char tab[MAXLINE+1];
settab(argc,argv,tab);
detab(tab);
return 0;
}
void detab(char *tab){
int c,pos;
pos=1;
while ((c=getchar())!=EOF)
{
if(c=='\t'){
putchar(' ');
while (tabpos(pos++,tab)!=YES)
{
putchar(' ');
}
}
else if(c=='\n'){
putchar(c);
pos=1;
}else{
putchar(c);
pos++;
}
}
}
void entab(char *tab){
int c,pos;
int nb=0;
int nt=0;
for(pos=1;(c=getchar())!=EOF;++pos){
if(c==' '){
if(tabpos(pos,tab)!=NO)
++nb;
else{
nb=0;
++nt;
}
}else
{
for(;nt>=0;--nt)
putchar('\t');
if(c=='\t')
nb=0;
else{
for(;nb>=0;--nb)
putchar(' ');
}
putchar(c);
if(c=='\n')
pos=0;
else if(c=='\t')
{
while (tabpos(pos,tab)!=YES)
{
pos++;
}
}
}
}
}
本文深入探讨了C语言中的指针概念,包括基本定义、地址运算、与数组及函数的关系等核心内容。并通过实例展示了如何利用指针进行高效的数据处理。

447

被折叠的 条评论
为什么被折叠?



