上一篇(初探指针(1))从指针的概念开始入手,依次是:什么是指针>>指针传递>>多级指针>>指针函数。尽管指针远不于此,但寻常应付想来足够了。这一次想谈谈关于指针易混淆的地方。
倒不知是否翻译之故,一度出现了指针函数和函数指针,指针数组和数组指针,指针常量和常量指针,字符指针数组和二维字符数组……通通被我认为属于一个群体——不用就会忘记系列。其实抛开考试中会故意找茬,实际运用到都算不上太难。但这些术语名字嘛……想起我还小的时候,范冰冰李冰冰傻傻分不清。数组
首先咱们须要理清指针与数组的关系:数组不等于指针,但在某些状况下两者等价。函数
定义一个一维数组int a[i] = {...};
,当咱们使用a[i]
的时候,实际上编译器作了转换处理,变成了*(a+i)
。ui
一维数组是方便理解的,那么二维数组呢?
a[i][j]
与*(*(a+i)+j)
具备等价效果,对后者分析以下:spa
有了这个铺垫,咱们就能够愉快的开始下面的旅程了。翻译
前面说到在指针函数中,return的时候注意不要是局部变量的地址,这句话自己是有瑕疵的。但若是就这样作了,保准儿程序不会出错。可是等一等,咱们须要这般畏首畏尾吗?指针
与“猥(畏)琐(缩)”说不!
通常说来,存放在栈中的变量不能够被return地址。这是因为栈中的数据由系统产生,也由系统回收,很难说咱们在return栈中的某个数据的地址时,系统是否是早一步把它干掉了。code
但这几个数据是能够的:全局变量,静态变量,常量数据,堆中的数据。一个运行的程序里,这些数据并不存放在栈中,具体在哪里涉及到内存分配的问题,这里不作延伸了。regexp
函数指针的根本是指针,它可以指向一个函数。游戏
定义:int (*p)(int, int);
表示p是一个可以指向函数的指针,而且它指向的函数是int类型,且这个函数有两个参数,这个两个参数都是int类型。内存
#include <stdio.h>
int add_xy(int x, int y)
{
return x+y;
}
int main(int argc, char **agrv)
{
int (*p)(int, int);
p = &add_xy;
int result = p(1, 2);
printf("%d\n", result);
return 0;
}
// 输出
>>3
触类旁通:char *(*p)(int, char *)
表示p是一个指向函数的指针,这个函数有两个参数:一个int类型,一个char*类型(char*的指针),而且这个函数返回char *的指针
必定要区分: char *p(int, char *)
,当没有小括号把指针运算符和指针变量包起来的时候,表示指针函数的声明,函数的名字为p
指针数组的根本是数组,即:一个数组里的成员都是指针
定义:int *p[];
按照优先级顺序,这里p[]
是一个总体,因此能够看做:int *
—> p[]
示例:
#include <stdio.h>
int main(int argc, char **agrv)
{
int a=1, b=2, c=3;
int *p[3] = {&a, &b, &c}; // 定义一个指针数组,而且初始化
int i = 0;
for(i=0;i<3;i++){
printf("%d\n", *(*(p+i)));
// printf("%d\n", *p[i]); 也能够这样写
}
return 0;
}
// 输出
>>1
>>2
>>3
数组指针的根本是指针,即:一个指向数组的指针
定义:int (*p)[];
按照优先级顺序,*p
是一个总体,因此能够看做int *p
—> []
示例:
#include <stdio.h>
int main(int argc, char **argv)
{
int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int (*p)[3] = a; // 定义一个数组指针,而且初始化
int i = 0, j = 0;
int row = 3, col = 3;
for(i=0; i<row; i++){
for(j=0; j<col; j++){
printf("%d ", *(*(p+i)+j));
// printf("%d ", (*(p+i))[j]); 也能够这样写
}
}
return 0;
}
// 输出
>>1 2 3 4 5 6 7 8 9
指针常量表示:这个指针为“只读”(从始至终只能指向一个地址,能够经过指针运算符来修改所指向的地址上的数据,必定在定义的时候赋初值)
定义:int *const p;
指针(*)常量(const),也就是说指针运算符在const的前面;const修饰的是p,因此p不能被赋值
#include <stdio.h>
int main(int argc, char **argv)
{
int a = 1;
int *const p = &a;
printf("%d\n", a);
*p = 2;
printf("%d\n", a);
return 0;
}
// 输出
>>1
>>2
常量指针表示:这个指针指向的地址上的数据为“只读”(该指针能够随意更改指向的地址,但不能经过取地址运算符来修改所指向的地址上的数据)
定义:int const *p;
常量(const)指针(*),也就是说,const在指针运算符的前面;而且能够看作const 修饰的是*p,因此*p不能被赋值
示例:
#include <stdio.h>
int main(int argc, char **argv)
{
int const *p = NULL;
int a = 1, b = 2;
p = &a;
printf("%d\n", *p);
p = &b;
printf("%d\n", *p);
return 0;
}
// 输出
>>1
>>2
字符指针数据,也就是一个数组,里边的成员都是char *的指针,即:char *str[] = {"...", "...", ...};
二维字符数组,一个二维数组,里边成员都是char类型的字符,即:char str[][n] = {"...", "...", ...};
#include <stdio.h>
int main(int argc, char **agrv)
{
char *names[] = {"Tom", "Alice", "Bob"};
int i = 0;
for(i=0; i<3; i++){
printf("%s ", *(names+i));
// printf("%s ", names[i]); 也能够这样写
}
printf("\n");
char hobbies[][10] = {"swimming", "sleeping", "reading"};
for(i = 0; i<3; i++){
printf("%s ", *(hobbies+i));
// printf("%s ", hobbies[i]); 也能够这样写
}
printf("\n");
return 0;
}
// 输出
>>Tom Alice Bob
>>swimming sleeping reading
在使用上,两者是没差的,但从内存上分析就存在区别。
字符指针数组中的成员都是一个char *的指针,也就是说,它们都是地址,真正的字符串存放在常量区;二维字符数组里边存放的都是一个个char类型的字符。建议使用前者,由于字符指针数组存储字符串要比二维字符数组开销小。
我记得有一个送命游戏叫作:韩国女星连连看。其实对我重度脸盲患者,岂止韩国女星,即使是如今的许多大陆女星,我也着实难分辨(其实如今的新兴男星我也基本分不清了)。好在能不能区分出明星仍是其次,重要的是区分如下的表达式。若是可以轻易识别,指针也算入门了。
int *p[n];
int (*p)[n];
int (*p)(int, int);
int (*p[])(int, int);
int p(int, int);
int *p(int, int);
int const *p;
int *const p;
int const * const p;