指针

a[4]表示一个一维数组,包含四个元素;a[5][4]表示一个二维数组,即包含五个单元,每个单元是一个包含四元素的一维数组

对于a[3][2]来说,

a表示指向a[0]的指针;a[0]表示指向a[0][0]的指针,即表示a[0][0]的地址;因此a表示a[0][0]的地址的地址。

 

需要注意的是,试图访问字符串时,直接用指向字符串的指针即可,不用使用*进行间接访问。

eg:char *color[5]={"blue","red","yellow","green","black"}

若访问red,用color[1]表示即可,无需再在前面加“*”号了。

指针作为函数的参数

在c语言中,不同于c++,并无引用传递,而只能进行值传递。若需要调用函数来修改某些量时,就需要用到指针。

作者:Ezreal
链接:https://www.zhihu.com/question/24466000/answer/660215996
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

指针的指针&&在二维数组中的延伸

 

指针的指针

顾名思义,即指向指针这个数据类型的指针,还记得上面那个输出③吗,输出的值就是指针p的地址,那么这个时候我们完全可以再定义一个指针变量,存储该地址,指向指针的指针就应运而生了。

int **pp=&p; //将指针p的地址保存到指针pp中,即指针pp指向指针p
printf("%p",*pp);  //打印出的值与③相同,即指针p的地址

 

在二维数组中的延伸


前面我们应该已经知道在一维数组中a中,a表示一个指针,指向的数组a中的首元素。
那么在二维数组中不过是对这个概念进行了扩充,例如a[5][4],这里的a本质上是一个数组指针指向一个容量为5的一维数组而已。a[0],a[1]等如同一维数组中a一样指向他们所对应的一维数组中首个量。

自己动手试试下面的实验,帮助理解数组,指针之间的关联。

#include<stdio.h>

int main(){
	int a[2][3]={{1,2,3},{4,5,6}};
	
	printf("%p\n",a);   //输出指针a数据,也就是指针a[0]的地址 
	printf("%p\n",a+1);   //输出a+1的数据 ,也就是a[1]的地址
	printf("%p\n",&a[0]);
	printf("%p\n",&a[1]);     //验证上述
	printf("%p\n",(*a)+1);  //输出的是a[0][1]的地址
	printf("%p\n",&a[0][1]);   //验证
	printf("%d\n",*(a[0]));  //输出的是a[0]a[0]的值 
 	printf("%d\n",*(*(a+1)+1));  //输出的是a[1][1]的值 
}

数组指针vs指针数组

 

数组指针

数组两个字放在前面表示什么?没错,表示的是指针指向的数据类型

例如: int * [5] p ,定义一个指向容量为5的一维数组的指针p。事实上类比二维数组,这个指针p指向的是一个指向某个容量为5的一维数组第一个数据的指针。

指针随着指针类型的变化,它的运算方式‘+’也发生了改变。(这里有一个问题,由于博主未接触过指针的更底层的源码,我的猜想是这样的:为了分辨指针的类型,指针的属性中可能还有一些参数,用于辨别指针位移的大小,如同二维数组中a[][]的指针a,本身实际上是一个指向整型指针的指针,但对a做‘+’运算时位移的单位却不是一个sizeof(int),所以推断a一定还有其他参数制约了该操作)

指针数组

字符数组是什么?存储字符的数组。那么指针数组是什么,存储指针的数组!

没问题,聪明的前辈们想到用数组存储一连串的指针,最常见的用法就是在定义多个字符串的时候。

例:#include<stdio.h>
int main(){
char * p[3]={"yellow","red","pink"};  
	char **pp=p;    
	printf("%s",p[1]);   
}

在上列中定义了指针数组p,并定义了指向指针的指针pp。

这里结合上述的二级指针(指针的指针)给出适当的例子予以解释

#include<stdio.h>
#include<string.h>

//寻找五环色,不适用二级指针 
int main(){
    char *color[5]={"blue","red","yellow","green","black"};
    char str[20];
    scanf("%s",str);
	
     for(int i=0;i<5;i++){
	if(strcmp(str,color[i])==0){
		printf("颜色相同");
		break;
		}
	} 
}

这里注意的问题是,试图访问字符串时,直接用指向字符串的指针即可,不用使用*进行间接访问

#include<stdio.h>
#include<string.h>

//寻找五环色,使用二级指针 
int main(){
    char *color[5]={"red","blue","yellow","green","black"};
    char str[20];
    char **pp=color;      //定义二级指针pp,明白color本质上是一个二级指针
    scanf("%s",str);
	
	for(int i=0;i<5;i++){
		if(strcmp(str,*(color+i))==0){
			printf("颜色相同");
			break;
		}
	} 
}

最后这里留有一道题目:尝试用指针数组实现多个字符串的动态输入

指针与函数:

指针作为函数的参数

在c语言中,不同于c++,并无引用传递,而只能进行进行值传递。若需要调用函数来修改某些量时,就需要用到指针。通过将地址当做实参传给函数中形参(即指针),完成赋值。

举例冒泡排序法

#include<stdio.h>


void exchange(int *p,int *q){
	int temp;
	temp=*q;
	*q=*p;
	*p=temp;
} 

int main(){
    int a[10]={3,4,5,1,6,7,8,9,10,0};
	for(int i=0;i<10;i++){
		for(int j=9-i;j>=0;j--){
			if(a[j]<a[j-1]){
				exchange(&a[j-1],&a[j]);
			}
		}
	}	
	for(int i=0;i<10;i++){
		printf("%d",a[i]);
	} 
}
指针作为函数的返回值

函数的返回值可以是int,可以是char,当然也可以返回一个指针类型(一开始已经说过了指针属于一种数据类型)。

下面例子就是一个简单返回指针的例子,实际上就是传递一种数据类型,不过这种数据类型特殊点,叫做指针,并且他的数据是另一个数据的地址。

例:输入一个字符串和一个字符,如果该字符在字符串中,就从该字符首次出现的位置开始输出字符串中的数字(最后可以直接用指针打印出来字符串,末尾有’/0‘判断)

#include<stdio.h>

char *match(char str[],char a){
	int i=0;
	char *p;
	while(1){
		if(str[i]==a){
			p=&str[i];
			return p;
		} 
		i++;
	}
}

int main(){
	char str[20];
	int i=0;
	char *q;
	 
	while((str[i]=getchar())!='\n'){
		i++;
	}
	str[i]='\0';
	
	q=match(str,'a');  //调用match函数,寻找相同的值 
	
	printf("%s",q);    
}
指向函数的指针

在c语言中,函数名代表函数的入口地址。可以定义一个指针变量,并接受函数的入口地址让它指向函数,这就是指向函数的指针,也称为函数指针。通过函数指针可以调用函数,它还可以作为函数的参数。

1.函数指针的定义

类型名*(函数名)(返回值类型)

int *(funptr)(int,int)

定义了一个函数指针funptr,它可以指向有两个整型参数且返回值类型为int的函数。

2.通过函数指针调用函数

使用前函数指针前,需要先对函数指针进行赋值,将一个函数名赋给函数指针,但该函数必须已定义或者已声明,且函数返回值类型与函数指针的类型要一致。

3.函数指针作为函数参数

c语言中,利用函数名作为实参,函数指针作为形参,完成传递传递函数。

例:利用函数指针实现\int_{a}^{b}f(x)dx 其中 f(x)=x^2 计算(按照梯形进行近似运算)

#include<stdio.h>


int calc(int (*fun)(int),int a,int b){
	int fa=(*fun)(a);
	int fb=(*fun)(b);
	return ((b-a)*(fa+fb))/2;
}

int tun(int a){
	return a*a;
}

int main(){
	int a=2,b=3;
	printf("%d",calc(tun,a,b));
}

 

编辑于 2019-05-08

​赞同 22​​2 条评论

​分享

​收藏​感谢

收起​

张知

张知

金融业

115 人赞同了该回答

相当于windows桌面上常见的快捷方式。
快捷方式可以指向某个游戏,这是普通指针。
快捷方式不介意它指向的是什么,也可以指向另一个快捷方式,这就是指向指针的指针。
也可以指向某个文件夹,这是指向数组的指针。
某天你不想下床,告诉室友“打开桌面上叫”音乐“的快捷方式,播放第3首歌”,这就是在指针的基础上加了一个偏移量。
快捷方式可以指向一个文件夹,进而可以进一步指向某一类里的具体某一个,比如“游戏”文件夹里存了很多个游戏的快捷方式,你用一个指向“游戏”文件夹的快捷方式就很容易调用它们。这就是 指向指针数组的指针。

发布于 2014-07-14

​赞同 115​​10 条评论

​分享

​收藏​感谢

Wayne

Wayne

1,665 人赞同了该回答

不理解指针,是因为有人教错了你。

有人告诉你,指针是“指向”某某某的,那就是误导你,给你挖了个坑。初学者小心不要误读这“指向”二字。

第一,“指针”通常用于保存一个地址,这个地址的数据类型在定义指针变量时确定。

举个例子,做个比较:

int a; //定义一个变量a,用于保存一个int类型。

int * b; //定义一个指针变量b,用于保存一个地址,这个地址所保存的数据应该是int类型。

第二,是变量就应该可以赋值,指针变量也一样。但一般不会给指针直接赋值一个数值,而是将其他变量的地址赋值给指针,或者其他指针的值赋值给这个指针。

继续上面的例子:

b=&a; //把变量a的地址赋值给b。“&”操作是取变量的地址。

继续举例:

int * c; //我们又定义一个指针c

c=b; //将b的值赋值给c,上面已经知道b是指针,它的值是a的地址,那么现在c 的值和b一样,也是个a的地址。

第三,指针变量保存的值,也就是地址是可以变的。

举个数组初始化的例子:

int d[100];

int * e;

e=&d[0]; //e保存了数组d的第一个数据的地址

for (int i=0; i<100; i++){

*e = 0; //把该地址中的数据赋值0

e++; //地址累加一次,也就是数组中下一个数据的地址

}

指针和其他变量一样,可以运算(一般是加减),也可以重新赋值。

第四,指针有啥用。

比方说,我们有个函数,如下:

int add(int x){

return (x+1); //把输入的值加1并返回结果。

}

好了,应用的时候是这样的:

{

int a=1;

a=add(a); //add函数返回的是a+1

//现在 a等于2

}

很简单吧,就是把a都累加一次。

用指针怎么写:

void add(int *y){ //给入的是一个int指针,是一个地址。

*y = *y + 1; //* 是指引用 这个地址所保存的变量

//这条语句的意思就是,把这个地址里的值加1,然后放回这个地址。

}

把这个函数用起来:

{

int a=1;

add(&a); //把a的地址传到函数里

//add函数,就是把a的值加1,再放回到变量a里。

//现在a等于2

}

试想一下,如果我们要对一个数据结构里的数据进行处理,那是不是就该传入这个结构的地址,在函数中依靠指针来引用这个地址的数据结构,进行运算。

第五,若已有一个指针变量,可不可以用另外一个指针来保存这个变量的地址呢。

可以的。

一个变量保存另一个指针的地址,那它就是通常所说是“指针的指针”了。

通常,指针的指针多用做(或指的是)函数指针或数据结构中有指针的情况。初学者搞不清,就不要勉强了。

第六,空指针可怕吗?

我们知道,一个变量要赋值后才能用,指针也是一样。指针不赋值(不给定地址)就拿来用,是不是也要出错。这个就是空指针。一般把指针赋值为Null,就是表明这个指针是空的,不能用。所以程序中一定要经常判别指针不是Null才能用。

最后,初学C语言,最好把复合的语句拆开来看,比较容易理解。像 int *a=&b; 这种,拆成

int * a;

a=&b;

就好理解了。

 

上面所说的,是指针比较基础的东西,希望能帮助初学者学习理清思路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值