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语言中,利用函数名作为实参,函数指针作为形参,完成传递传递函数。
例:利用函数指针实现 其中 计算(按照梯形进行近似运算)
#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));
}
赞同 222 条评论
分享
收藏感谢
收起
金融业
115 人赞同了该回答
相当于windows桌面上常见的快捷方式。
快捷方式可以指向某个游戏,这是普通指针。
快捷方式不介意它指向的是什么,也可以指向另一个快捷方式,这就是指向指针的指针。
也可以指向某个文件夹,这是指向数组的指针。
某天你不想下床,告诉室友“打开桌面上叫”音乐“的快捷方式,播放第3首歌”,这就是在指针的基础上加了一个偏移量。
快捷方式可以指向一个文件夹,进而可以进一步指向某一类里的具体某一个,比如“游戏”文件夹里存了很多个游戏的快捷方式,你用一个指向“游戏”文件夹的快捷方式就很容易调用它们。这就是 指向指针数组的指针。
赞同 11510 条评论
分享
收藏感谢
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;
就好理解了。
上面所说的,是指针比较基础的东西,希望能帮助初学者学习理清思路