一、字符指针
在指针中,我们知道有一类指针类型为字符指针char*;
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'w';
return 0;
}
还有一种使用方式如下:
上述代码中,本质是把hello的首字符的地址放到了pstr中。即把一个常量字符串的首字符h的地址存放到指针变量pstr中。
其中,%s是格式化,是打印字符串,传入字符串的首地址打印字符串,遇到\0停止。
让我们来学习一下下面这道《剑指offer》的题目
这里的str3和str4指向的是同一个常量字符串。当几个指针指向同一个字符串的时候,他们实际会指向同一块内存,但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
二、数组指针变量
2.1数组指针变量是什么?
之前我们学习了指针数组,指针数组是一种数组,数组中存放的是地址(指针)
那么数组指针 就是指针变量。
我们已经熟知:
- 整型指针:int * pint ;存放的是整型变量的地址,能够指向整型数据的指针。
- 浮点型指针:float * pf;存放浮点型变量的地址,能够指向浮点型数据的指针。
那数组指针应该是:存放的应该是数组的地址,能够指向数组的指针变量。
思考下面的代码分别是什么?
1 int *p1[10];
2 int (*p2)[10];
第一个是上一节所学的指针数组,第二个则是数组指针。
解释:p先和*结合,说明p就是一个指针变量,然后指针指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
这里要注意:[ ]的优先级要高于*的,所以必须加上()来保证p和*先结合。
2.2 数组指针变量怎么初始化
如果要存放个数组的地址,就得存放在数组指针变量中,如下:
int (*p)[10] = &arr;
int (*p) [10] = &arr;
| | |
| | |
| | p指向数组的元素个数
| p是数组指针变量名
p指向的数组的元素类型
三、二维数组传参的本质
有了数组指针的理解,我们就能够讲解一下二维数组传参的本质了
过去,我们有一个二维数组需要传参给一个函数的时候,我们是这样写的:
void test(int arr[3][5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j =0;j < c; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
这里实参是二维数组,形参也写成二维数组的形式,那还有什么其他的写法吗?
首先,我们再次理解一下二维数组,二维数组其实可以看做是每个元素都是一维数组的数组,也就是二维数组的每个元素都是一个一维数组。那么二维数组的首元素就是第一行,是一个一维数组。
所以,根据数组名是数组首元素的地址这个规则,二维数组的数组名表示的就是第一行的地址,是一个一维数组。根据上面的例子,第一行的一维数组的类型是int [5],所以第一行的地址的类型就是数组指针类型int (*)[5]。那就意味着二维数组传参的本质也是传递了地址,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针形式的 。如下:
void test(int (*p)[5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j =0;j < c; j++)
{
printf("%d ", *(*(p+i)+j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
总结:二维数组传参,形参部分可以写成数组,也可以写成数组指针形式。
四、函数指针变量
4.1 函数指针变量的创建
通过类比关系,我们也可以知道函数也存在着地址。
输出结果相同,确实打印出了地址。函数名就是函数的地址,也可以通过&函数名的方式获得函数的地址。
其中函数指针的写法与数组指针的写法相似。
int (*p)( int int ) = Add;
int (*pf) (int x, int y)| | |
| | |
| | pf指向函数的参数类型和个数的交代
| 函数指针变量名为pf
pf指向函数的返回类型
int (*) (int x, int y) //pf函数指针变量的类型
4.2 函数指针变量的使用
4.3 typedef关键字
typedef是用来类型重命名的,可以将复杂的类型简单化。
typedef unsigned int uint;
//将unsigned int 重命名为uint
但是对于数组指针和函数指针稍微有点区别。
比如:有数组指针类型int (*)[5] ,需要重命名为parr.
那么就可以这样写:
typedef int(*parr)[5];//新的类型名必须写在*的右边
函数指针也是一样的道理
typedef int (*pfunc)(int);
函数数组指针
int (*p[5)(int x ,int y )