null10、指针10、指针10.1 指针概念
10.2 指针变量的定义和使用
10.3 指针与一维数组
10.4 指针与函数
10.5 指针数组、多级指针与指向一维数组 的指针
10.6 指针与字符串 10.1 指针概念10.1 指针概念 所谓指针,某种程度上就相当于地址,但要比地
址的含义更加丰富
• 变量的指针
变量的指针指的就是该变量的首地址
• 指针变量
指针变量是以指针(地址)为值的一种变量
• 指针指向变量
如有普通变量x及指针变量p,且p=&x; ,则称:
指针p指向变量x null • 指针变量的类型
指针变量按其所指向变量类型的不同,也分成相
应的多种不同类型,指针变量只允许指向相应类
型的变量。例如:
有定义: int a, *p; float b, *q;
则允许:p=&a; q=&b;
不允许:p=&b; q=&a; p=q; q=p;
• 空指针
指向地址为0的指针称为空指针,空指针用于
示指向空,记为NULL,NULL是一个指针常量,
对应0地址,在头文件stdio.h中定义。 10.2 指针变量的定义和使用10.2 指针变量的定义和使用 • 指针变量的定义
一般形式: 数据类型 *指针变量名;
如:int n, *p; float x, *p1, *p2;
• 取址运算符&
int n, *p;
p = &n;
• 取值运算符*
int m, n, *p;
p = &n; *p=5; m = *p;null • 指针使用举例
【例7.1】输入一个整数,通过指针方式赋值给另
一个变量后输出。
#include
main()
{ int n, m, *p;
scanf( "%d", &n );
p = &n;
m = *p;
printf( "%d\n", m );
}地址 内存 变量null • 指针变量的初始化
int i, *p = &i;
char *q = "abcde"; 把字符串首地址赋给 q
int *p = NULL; 0地址赋给 p (指向空)
• 指针必须先赋值再使用
int n, *p; 定义指针变量p
*p = 5; 可怕的错误
p=&n; *p=5; 先赋值再使用
10.3 指针与一维数组
10.3.1 一维数组的内存安排 10.3 指针与一维数组
10.3.1 一维数组的内存安排 int a[]={10,11,12,13,14};
int p=a;10.3.2 指向数组的指针运算 10.3.2 指向数组的指针运算 1. 指向数组的指针
• 指针可以指向数组中的某个元素
int c[5], *p, *q;
p = &c[0]; /* 与 p = c; 等价 */
q = &c[1];
• 指向整型数组的指针与指向单个整型变量的指针
在指针类型上是一样的,都是指向整型量的指针
int n, c[5], *p, *q = c;
p=&n; 2. 指针的加减整数运算2. 指针的加减整数运算 指针加减一个整数,该整数表示的是该指针所指
向数据类型的数据的个数,指针实际的增加量或
减少量是该整数乘以指针所指向数据类型的长度
(字节数)。
例如:p为指针变量,则p=p+1后指向了原指向的数据的后面一个数据的首地址。
如果p为字符型指针变量,则其增量为1字节;
如果p为整型指针变量,则其增量为2字节;
如果p为实型指针变量,则其增量为4字节。
null3. 指针和所指向值的自增自减运算
• 指针可以进行自增自减运算
设p为指针,则有:
p++; ++p; p--; --p;
• 对指针所指向的值也可以进行自增自减运算,如:
(*p)++; 或 ++(*p); 相当于 *p = *p + 1;
(*p)--; 或 --(*p); 相当于 *p = *p - 1;
• 指针与++、--和* 运算符
++、--和 * 运算都是单目运算符,具有同等的优
先级,C语言规定单目运算符是右结合的 null 设有:int n, a[5] = { 0, 1, 2, 3, 4 }, p = &a[2]; 则:
表达式 作用和意义
*++p 先++p使p指向a[3],再取*p即a[3]值
++*p p指向的元素值增1,即++a[2]
(*p)++ 先取*p即a[2]值,再使a[2]值增1,即a[2]++
*(p++) 先取*p即a[2]值, 再使p增1指向a[3]
注: *p++等价于*(p++)
null【例7.2】以下程序的运行结果。
#include
main()
{ int a[] = {10, 11, 12, 13, 14}, *p, n, i;
p = &a[0];
n = *(p++);
n += *(++p);
n += (*(p+=2))++;
n -= (*(--p))--;
n -= *(--p-1);
printf( "%d\n", n );
for(i=1;i<5;i++) printf("%d ",a[i]);
}/* n=a[0]=10 p→a[1] *//* p→a[2] n=n+a[2]=22 *//* p→a[4] n=n+a[4]=36 a[4]=15 *//* p→a[3] n=n-a[3]=23 a[3]=12 *//* p→a[2] n=n-a[1]=12 *//* p→a[0] *//* 12 *//* 10 11 12 12 15 */null4. 指针的比较运算
• 任意两个指针可以进行相等比较,以确定它们是
否指向同一个变量
• 两个指针指向同一数组时,可进行比较运算(含
<、<=、 >、 >=、 !=、 = =)。以确定它们在数
组中的前后位置值。
例:指针关系运算举例。
#include "stdlib.h"
#include "time.h"nullmain()
{ int a[20] ,*p, *q, n1, n2;
randomize(); /* 初始化随机数发生器 */
n1=random(20);
n2=random(20);
p=a+n1; /* p指向a[n1] */
q=a+n2; /* q指向a[n2] */
if(p==q) printf( "\np and q points same element." );
else if(p a[j+1])
swap(&a[j],&a[j+1]);
}main()
{ int x[10], i;
for(i=0; i < 10; i++)
scanf("%d",&x[i]);
sort(x, 10);
for(i = 0; i < 10; i++)
printf("%d ",x[i]);
printf( "\n" );
}
10.4.2 指针做为函数返回类型 10.4.2 指针做为函数返回类型 【例7.6】编写函数,在一个已从小到大排序的数
组中查找一个指定的数,如果找到,返回指向数组
中该数的指针,否则返回空指针。nullint *find(int *a, int n, int data) /* 数组a长度n待查数data */
{ int *p=a, *q=a+n, *r;
while ( p < q ) /* 待查找区不为空时 */
{ r = p + (q-p)/2; /* 待查找区的中点位置 */
if ( *r==data )
return r; /* 如找到,返回该指针 */
else if ( *r < data )
p = r + 1; /* 比中点大,在后半区 */
else q = r; /* 比中点小,在前半区 */
}
return NULL; /* 没找到,返回空指针 */
}nullmain()
{ int x[]={11,22,33,44,55,66,77,88,99,100}, d, *p;
scanf( "%d", &d ); /* 输入要查找的数 */
p = find( x, 10, d ); /* 调用查找,结果赋值给p */
if ( p != NULL ) /* 判断是否找到数 */
printf( "Found at %d\n", p-x ); /* 找到,输出下标 */
else
printf( "Not Found.\n" ); /* 没找到 */
}10.4.3 动态内存分配与释放函数 10.4.3 动态内存分配与释放函数 在处理实际问题时,常会遇到数组的大小要在程
序执行中才能确定的情况,合理的方法是在程序中
再决定数组的大小。
C语言提供了动态存储分配函数,可在程序运行
过程中,动态分配内存区供数据存储,可根据需要
指定大小,以及不需时予以释放。
下面介绍动态存储分配函数中的malloc()和free(),
在stdlib.h和alloc.h头文件中均含有它们的原型。null• 内存分配函数malloc()
原型:void * malloc(unsigned size);
功能:内存中分配一块连续的size个字节的未初始
化的存储区。返回一指向该区首址的void *
类型(称通用型或无类型)指针,须按实际需
要的数据类型进行强制转换,才能赋值给 相应的指针变量。如无法分配,则返回空指针。
• 内存释放函数free()
原型:void * free(void *buffer);
功能:释放buffer所指向的由malloc()等内存分配函数所分配的内存块。null【例7.7】输入n和n个整数,输出其中不重复出现
的整数,要求:只要内存容量许可,不对n的上限
做明显的限定。
#include
main()
{ int *p, n, i, j, ct;
scanf( "%d", &n );
p = (int *)malloc( n*sizeof(int) );
if ( p == NULL )
{ printf( "There is not enough memory.\n" );
return -1;
}null for ( i = 0; i < n; i++ )
scanf( "%d", &p[i] );
for ( i = 0; i < n; i++ )
{ for (ct = 0, j = 0; j < n; j++ )
if ( p[i] == p[j] )
ct++;
if ( ct == 1 )
printf( "%d\t", p[i] );
}
free( p );
printf( "\n" );
}运行结果:
输出:33 44 100输入:8 11 33 5 11 5 5 44 10010.4.4 函数指针 10.4.4 函数指针 1. 函数指针的定义和使用
• 函数指针的概念
函数名具有类似数组名的地址特性:
数组名 — 该数组的首地址
函数名 — 该函数的入口地址
一个函数作为一段程序,在内存中占据一片连续
的存储单元,其中第一条执行指令所在的位置称
为函数的入口地址,取值为函数入口地址的指针
变量,称为指向函数的指针变量,简称函数指针。
通过该指针可以调用并执行函数。null • 函数指针的定义
函数值类型 ( *函数指针变量名 ) ( 参数说明表 ) ;
参数说明表中可只说明形参类型,而无形参本身。
如省略参数说明表,系统不检查参数传递正确性;
例如:
有函数说明:int max(int, int), abs( int ) ;
如要定义一个用来指向max函数的指针变量,则可
定义如下:
int (*p) ( int, int ); 或 int (*p) ( );
null • 函数指针的使用
给函数指针变量赋值时,只需给出函数名而不必
(也不能)给出参数。如:
int a, b, c, max(int, int), (*p)( );
p=max; /* p为函数指针变量,max为函数名*/
函数可通过函数名调用,也可通过函数指针调用,
如上例后,只需用(*p)代替函数名max即可。如:
c=max(a, b); /* 通过函数名调用 */
c=(*p)(a, b); /* 通过函数指针调用 */
对函数指针变量,象p+i、p++、p--等运算无意义。null2. 函数指针做函数的参数
【例7.8】求三角函数sin(),cos()和tan()在10度,
20度,30度,40度和50度时的数值。
#include
void printvalue( double (*fun)(double), int n )
{ int i;
for( i=1; i<=n; i++ )
printf( "%d\t%lf\n", i*10, (*fun)(3.14159*i/18) );
}
main()
{ printvalue( sin, 5 ); printvalue( cos, 5 );
printvalue( tan, 5 ); }null3. 函数指针数组
函数指针可以组成指针数组,称为函数指针数组,
该数组的元素都是函数指针。
定义格式:
数据类型 (*函数指针数组名[常量表达式] ) ( )
其中常量表达式的值表示函数指针数组的长度。
例:用循环语句for,while,do-while和if-goto编
制计算 的函数,并验证其正确性。要求
用函数指针数组nulllong sum1(int n)
{ int i;
long sum=0;
for(i=1; i<=n; i++)
sum+=i;
return sum; }
long sum2(int n)
{ int i=0;
long sum=0;
while(++i<=n)
sum+=i;
return sum; }long sum3(int n)
{ int i=0;
long sum=0;
do sum+=i;
while(i++<=n);
return sum; }
long sum4(int n)
{ int i=0;
long sum=0;
ss: sum+=i;
if(i++<=n) goto ss;
return sum; }nullmain()
{ long (*f[4])( ), k;
int i, n;
printf("\ninput a number:");
scanf("%d", &n);
f[0]=sum1; f[1]=sum2; f[2]=sum3; f[3]=sum4;
k=(*f[0])(n);
for(i=1; i<4; i++)
if(k!= (*f[i])(n)) { k=-1; break; }
if(k==-1) printf("Error!");
else printf("Right. sum=%ld", k);
}10.5 指针数组、多级指针与
指向一维数组的指针10.5 指针数组、多级指针与
指向一维数组的指针10.5.1 指针数组与多级指针10.5.1 指针数组与多级指针1. 指针的数组
• 一个数组的元素都为指针类型数据,称为指针
数组
• 定义格式:
数据类型 *指针数组名[常量表达式]
其中常量表达式的值表示数组的元素个数。
例:int *b[3];
指针数组b的每个元素相当于可以指向一般整型变量,也可以指向整型数组元素的指针变量。null2. 多级指针
• 指针变量中可以保存另一个指针的地址,变成
“指向指针的指针”,称为多级指针。例如:
有指针数组name,其元素是指向一般数据的指针。
令指针变量p=name, p指向指针数组name。
则称指针变量p为二级指针。
如有指针变量pp再指向p,则称pp为三级指针。
• 多级指针的定义
二级指针的定义形式:数据类型 **指针名
三级指针的定义形式:数据类型 ***指针名
从上可知,n级指针定义时,指针名前应加n个*。null3. 数据的访问
如有:int x[3], y, z[2], *pa[3], **p2;
pa[0]=x; pa[1]=&y; pa[2]=z; p2=pa;
则层次关系为:
则有(每行等价):
*(*(p2+0)+j) p2[0][j] *(pa[0]+j) pa[0][j] x[j]
*(*(p2+1)) p2[1][0] *pa[1] pa[1][0] y
*(*(p2+2)+j) p2[2][j] *(pa[2]+j) pa[2][j] z[j]null例:将8个字符串按词典顺序排列。
⑴ 用二维数组
#include "string.h"
#define SIZE 8
main()
{ char name[SIZE][10]={"constant","pointer","function",
"variable","address","structue","union","array"};
int i, j, k;
char temp[10];null for(i=0; i0) k=j;
if(k!=i)
{ strcpy(temp, name[k]);
strcpy(name[k], name[i]);
strcpy(name[i], temp);
}
}
for(i=0; i0) k=j;
if(k!=i)
{ temp=name[k];
name[k]=name[i];
name[i]=temp;
}
}
for(i=0; i0) k=j;
if(k!=i) /*原name[k], name[j] */
{ temp= *(p+k); /*原name[k]*/
*(p+k) = *(p+i) ; /*原name[k]=name[i]*/
*(p+i) =temp; /*原name [i] */
}
}
for(i=0; i年级
所
有学生的成绩(只考虑一门课程,成绩使用int类型
表示),设年级共有4个班,一班50人,二班30人,
三班60人,四班40人。
1. 使用二维数组和指向一维数组的指针
/* 二维数组, 最多4个班, 每班最多60个学生 */
int stu[4][60];
/* 指向一维数组的指针, 每行60个整型数, 指向二维数组 */
int (*p1stu)[60]= stu; null 2. 在二维数组基础上的指针数组与二级指针
/* 二维数组, 最多4个班, 每班最多60个学生 */
int stu[4][60];
/* 指针数组指向二维数组的4个不同的行 */
int *pastu[4] = { stu[0], stu[1], stu[2], stu[3] };
/* 再在指针数组的基础上构造二级指针 */
int **p2stu = pastu;
3. 在多个一维数组基础上的指针数组与二级指针
/* 定义多个一维数组, 一班有50个学生, 二班有30个学生 ,
三班有60个学生, 四班有40个学生 */
int stu1[50], stu2[30], stu3[60], stu4[40] ; null /* 在这4个一维数组的基础上构造指针数组 */
int *pastu[4] = { stu1, stu2, stu3, stu4 };
/* 再构造一个二级指针变量将所有有信息归结到一个单
一变量 */
int **p2stu = pastu;
4. 访问学生成绩
访问第i个班第j个学生(i和j从0开始取值)的成
绩,可以使用以下表达式:
stu[i][j] /* 二维数组,下标+下标表示法 */
(*(p1stu+i))[j] /* 指向一维数组的指针, 指针+下标*/
*(pastu[i]+j) /* 指针数组,下标+指针表示法 */
*(*(p2stu+i)+j) /* 二级指针,指针+指针表示法 */null 为了简化格式,避免书写的错误,可以统一使用
二重下标法,如下所示:
stu[i][j] /* 二维数组 */
p1stu[i][j] /* 指向一维数组的指针 */
pastu[i][j] /* 指针数组 */
p2stu[i][j] /* 二级指针 */10.6 指针与字符串10.6 指针与字符串10.6.1 字符串的内存安排10.6.1 字符串的内存安排1. 字符数组的初始化 2. 字符数组的输入
char a[10]="Hello!" char a[10]; gets(a);null3. 字符串常量和字符指针的初始化
char *p="Hello!"; 10.6.2 字符串处理程序举例 10.6.2 字符串处理程序举例 【例7.10】 编程,输入一行字符,将其中以一个或
多个空格分开的子串称为单词,将奇数位置的单词
转换为大写,偶数位置的单词保持不变,所有单词
重新组织后输出,输出时单词之间使用 "---" 连接,
行首以"[B]"开始,行尾以"[E]"结束。
例如,输入(符号表示空格):
how are you? *#@-aBc-123-$$$
则输出:
[B]HOW---are---YOU?---*#@-aBc-123-$$$[E]null#include
#include
/* 字符串转换为大写函数 */
char *toucase( char *str )
{ char *p;
for(p=str; *p != '\0'; p++)
if ( *p>= 'a' && *p<= 'z' )
*p=*p - 'a' + 'A';
return str;
}null/* 从字符串中取出下一个单词 */
char *getnextword( char *str, char *word )
{ char *p;
for ( p = str; *p == ' '; p++ ) ; /* 过滤前导的空格 */
/* 循环至单词结束 */
for( ; *p!= ' ' && *p!='\0'; p++,word++ )
*word = *p; /* 复制单词 */
*word = '\0'; /* 单词结束 */
for ( ; *p==' '; p++ ) ; /* 过滤单词尾部空格 */
return p; /* 返回下一个非空格的位置 */
}nullmain()
{ char stra[81], strb[81], word[81], *p=stra; int n=0;
gets( stra );
strcpy( strb, "[B]" ); /* 产生行首子串*/
while ( *p != '\0' )
{ p = getnextword( p, word ); /* 取出下一个单词 */
if ( n != 0 ) strcat( strb, "---" ); /* 加连接子串 */
if ( n % 2 != 0 ) strcat( strb, word ); /* 直接拼接 */
else strcat( strb, toucase(word) ); /* 先大写再拼接 */
n++; /* 单词个数统计 */
}
strcat( strb, "[E]" ); /* 拼接行尾子串 */
puts( strb );
}null例:下面程序的输出结果是 __________ 。
char b[ ]="ABCD";
main( )
{ char *chp;
for(chp=b; *chp; chp+=2)
printf("%s", chp);
printf("\n");
}
ABCDCD10.6.3 多字符串的表示与处理 10.6.3 多字符串的表示与处理 多字符串实际上就是二维的字符数据,可以采用
二维的字符数组,也可以采用字符的指针数组形式
表示,或者二者结合。
【例7.11】编程,输入一行字符,将其中的单词按
逆顺序输出,例如,
输入(符号表示空格):
howareyou?*#@-aBc-123-$$$
则输出:
*#@-aBc-123-$$$ you? are how null#define MAXN 40
int getallwords( char *str, char *pp[ ], int maxn )
{ char *p; int n;
p = str;
n = 0;
while ( *p != '\0' )
{ p = getnextword( p, pp[n] ); /* 分离出第n个单词 */
if ( pp[n][0] != '\0' ) n++;
if ( n >= maxn ) break;
}
return n;
}nullmain()
{ char stra[81], words[MAXN][81], *pa[MAXN];
int i, n;
gets( stra );
for ( i = 0; i < MAXN; i++ )
pa[i] = words[i];
n = getallwords( stra, pa, MAXN );
for ( i = n-1; i >= 0; i-- )
printf( "%s\t", words [i] );
printf( "\n" );
}10.6.4 程序举例 10.6.4 程序举例 例1:编写函数,把字符串 t 接在字符串s之后, 返回 新字符串首地址。
char *strjion (char *s, char *t)
{ char *p;
p=s; /*保存s*/
while (*s) s++; /* 找字符串s末尾*/
while (*s++=*t++); /*连接*/
return p; /*返回指向连接后新字符串的指针*/
}null例2:从键盘输入两个字符串, 不使用库函数strcmp( )
来比较它们的大小,若相等,则输出:“Both
equal.”,否则输出其中较大者。
main()
{ char a[80], b[80], *p, *q ;
gets(a);
gets(b);
for(p=a, q=b; *p &&*q ; p++, q++);
if(*p>*q) puts(a);
else if (*p<*q) puts(b);
else if (*p=='\0'&&*q=='\0') puts("Both equal.");
} null例3:编一个程序,输入一个字符串,内容依次是学
号(位数不定)和姓名的汉语拼音码。经处理后,输出一个字符串,内容依次为,姓名,两个*号和学号。nullmain() /* (1)不使用库函数 */
{ char a[40], b[40], *p=a, *q=b, *r;
gets(a); /* 定位姓名的首字符位置 */
while(*p>='0'&&*p<='9') p++;
r=p; /* 记存姓名的首字符位置 */
while(*q++=*p++) ; /* 将姓名放入数组b */
*(q-1)='*'; /* 数组b中尾随姓名的'\0' 改为'*' */
*q++='*'; /* 数组b中再加一 '*' */
*r='\0'; /* 使a数组的学号尾随'\0' */
p=a;
while(*q++=*p++); /* 将学号放入数组b的'*' 之后 */
p