指针小结
来源:互联网 发布:手机淘宝用银行卡支付 编辑:程序博客网 时间:2024/05/01 05:15
随着课程的深入,知识点越来越多,不少同学开始迷惑了,尤其是学了指针以后,更加混乱了。指针似乎和谁都能扯上关系: 指向变量的指针、指向函数的指针、指向数组的指针、指向字符串的指针、返回指针值的函数、指针数组等等。我在这里把一些重要的概念进行小结,希望能起到理清思路的作用。重点理解不同指针的主要用途和优势。
1.指向变量的指针
1.1 定义:
int *p, a;
p = &a; // p存储a的地址,或者说p指向a
1.2 使用:
利用指针运算符 *来间接访问所指向的变量。
*p = 13; //*p等价于a
1.3 优势:
利用指针间接操作变量a,这种间接操作的优势在“指针做函数参数”时得到体现,最典型的例子就是编写“交换两个变量值的函数”,值传递没法实现实参变量的交换。如果形参改为指针变量,则可以实现地址传递,从而实现实参变量值的交换。
void swap(int * x, int * y)
{
int temp;
temp= *x;
*x= *y;
*y= temp;
}
int main()
{
int a=6,b=7;
swap(&a,&b);
printf("a=%d\nb=%d\n",a,b);
}
2.指向函数的指针
2.1定义和基本用法:
void fun(int a, int b)
{
printf("a+b=%d\n",a+b);
}
main()
{
void (*pfun)(int,int); //定义函数指针pfun
pfun= fun; //存储函数fun的首地址,或者说pfun指向函数fun
(*pfun)(5,6); //利用pfun来间接调用函数fun
}
2.2 函数指针的优势:
和指向变量的指针相似,函数指针的优势在“函数作为参数传递”时得到体现。它为编写通用函数提供了可能性。通用函数内部使用形参定义的函数指针来间接调用函数,而实参可以传递具体的函数名过去,根据需要,多次调用通用函数时,可以传递不同的函数进去。最典型的例子就是编写通用的求定积分的程序,即在区间[a, b]上求函数f(x)的定积分。不管f(x), a, b怎样变化,求解的过程是一样的: 将区间[a, b]等分成若干份,求f(x)和x轴之间的若干小梯形的面积和。因此可以写一个通用函数来求解定积分,在调用的时候,通过参数把不同的a,b和函数f(x)的指针传进去即可。程序如下:
//求定积分的通用函数
void Integral(float (*f)(float), float a, float b) //形参定义函数指针
{
float sum;
float h=(b-a)/100.0;
int i;
sum= ((*f)(a)+(*f)(b))/2.0; //用函数指针间接调用函数
for(i=1;i<100;i++)
{
sum+=(*f)(a+i*h);
}
sum *= h;
printf("The result is: %.2f\n",sum);
}
//定义不同的f(x)
float f1(float x)
{
return 1+x*x;
}
float f2(float x)
{
return x/(1+x*x);
}
int main()
{
//调用通用函数,传递不同的函数首地址(函数名)和a、b过去
Integral(f1,0,1);
Integral(f2,0,3);
}
利用函数指针变量编写的通用函数避免了代码的重复编写,提高了效率。
3.返回指针值的函数
也就是说函数返回了一个地址值。这种函数在C语言中很普遍,尤其是一些字符串处理函数。比如strcpy,strcat等都返回第一个参数str1的首地址。因为字符串总是以一个’\0’结尾的,所以只要知道了起始地址,也就能方便的处理整个字符串了。很多字符串处理函数的参数也定义为字符指针,所以可以直接用函数调用作为函数的参数。例如:
strlen(strcat(str1,str2));
strcat(strcpy(str1,str2), str3);
另外,动态内存分配函数malloc等也是返回指针值的函数。如果分配成功,返回被分配内存区域的首地址,它是void型指针,配合强制类型转换,可以转换成需要的指针类型。因为分配的内存区域是连续的,所以本质上可以当作数组一样去使用。利用返回的首地址和指针变量,可以访问每一个元素。
4.指针和一维数组
4.1指针指向一维数组:
int a[10], *pa;
pa = a; //指针指向数组头
pa = &a[3]; //指针指向数组的第3个元素
4.2 指针做++和- -运算
和数组名a不同,pa的值可以被修改,因此可以做++和- -运算(跳过4个字节),指向下一个元素或前一个元素。配合指针运算符*,可以写出非常简练的表达式或语句。比如两个字符串(分别存储在字符数组str1和str2中)的拷贝,可以简化成如下的形式:
char *pstr1 = str1;
char * pstr2= str2;
while(*pstr1++ = *pstr2++) ;
4.3指针做关系运算
因为数组元素是连续存放的,所以指向一维数组的指针变量也可以做关系运算。关系运算主要用在循环语句的条件中,比如:
for( pa = a; pa<a+5; pa++ )
printf(“%d”, *pa);
5.指针和二维数组
重点是理解行指针和列指针的概念:对于二维数组a,数组名a是行指针,a+i则跳过i行,为了访问行上的元素,需要把行指针转换为列指针,方法是加*,即 *(a+i),这个地址在数值上等于a+i,但它是列指针,它加j后在列上跳过j个元素,即*(a+i)+j指向第i行第j列的元素,也就是说*(a+i)+j == &a[i][j];那么为了访问元素a[i][j],还需要再做一次指针运算,即,*(*(a+i)+j),它等价于a[i][j]。因为从一维数组我们知道,*(a+i)等价于a[i],所以*(*(a+i)+j)也可以写作*(a[i]+j)。二维数组的每一行本来就可以看作是一个一维数组,数组名是a[i],所以*(a[i]+j)就是a[i]这个一维数组中的第j个元素。
数值上,它们是相等的: a == a[0] == &a[0][0] == *a。
还有一个需要掌握的是行指针变量 int (*p)[4],它的优势是可以将二维数组的一行传递给某个函数。
例:二维数组存储学生的成绩,每行存储一个学生,求指定学号的学生的平均成绩。
# include"stdio.h"
float ave(int(*pi)[3],int n) //定义行指针变量pi,准备接收行指针
{
int j;
float sum=0,aver;
for(j=0;j<n;j++)
{
sum+= *(*pi+j); // 在该例中*(*pi+j)等于 *(*(score+2)+j)或者 *(score[2]+j)
}
aver= sum/n;
return aver;
}
int main()
{
int score [3][3]={{1,2,3},{4,5,6},{7,8,9}};
printf("ave= %.2f\n",ave(score+2,3)); //传递行指针score+2给pi
return 0;
}
6.指针数组
每个元素都是一个指针值的数组。注意区别指针数组和上面定义的行指针变量(有点像函数指针和返回指针值的函数):
int * p[4]; int (*p)[4]; (前者是指针数组,后者是行指针变量)
int *fun( ) int (*p)( ) (前者是返回指针值的函数,后者是函数指针)
指针数组的每一个元素存储一个地址值,每个元素都可以当作同类型的指针变量一样去使用。使用前必须初始化。
int * p[4]; int a[4];
for(i=0;i<4; i++)
p[i] =&a[i];
指针数组主要用于字符串的处理。教材上例11.4用字符指针数组指向若干个字符串,在对字符串进行排序的时候,不需要挪动字符串本身,而只需要挪动指针数组元素即可,也就是说改变指向关系即可。这提高了程序的效率。
指针数组的另一个用途就是带参数的main函数:int main(int argc, char * argv[ ]),用命令行方式运行程序时,可以给main函数传递参数,参数和程序名都是字符串,字符串的个数保存在变量argc中,字符串的首地址保存在argv指针数组中。
例:从命令行输入两个整数,计算并输出它们的和:
//add.c
#include “stdio.h”
#include “stdlib.h”
int main(int argc, char*argv[])
{
int a, b;
a= atoi(argv[1]);
b= atoi(argv[2]);
printf("a+b= %d\n",a+b);
}
命令行输入:add 45 67
运行结果:a+b = 112
注意:上面的输入中,字符串“add”的地址存入了argv[0],“45”的地址存入了argv[1],“67”的地址存入了argv[2]。
- 指针小结
- 指针小结
- 指针小结
- 指针小结
- 指针小结
- 指针小结
- 指针小结
- 指针小结
- 指针小结
- 指针小结
- 指针小结
- 引用和指针小结
- 再再论指针小结
- C指针小结
- 野指针小结
- C指针小结
- 指针与引用小结
- c++指针访问小结
- Kali 解决默认启动HDMI没有声音问题
- 13.1节练习
- cell的3三种重用方法
- 让我震惊的SqlServer 对 ''特殊的处理
- Android 常用知识总结
- 指针小结
- Java中的堆和栈
- ViewPager简单用法
- “车城”项目问题总结之UITableView不同样式cell的运用
- Java 网络编程
- Android 字符串截取替换函数
- UVa 10071 - Back to High School Physics
- c++再回首之继承
- 关于转义字符