1、字符指针变量
在指针的类型中我们知道有一种指针类型是char*;
一般使用:
int main() {
char ch = 'w';
char* pc = &ch;
*pc = 'w';
return 0;
}
还有一种使用方法:
int main(){
const char* pstr = "hello bit";
printf("%s\n",pstr);
return 0;
}
需要注意的是:使用这段代码很容易误认为把整个字符串放进了指针pstr当中了,实际上是将字符串的首字符的地址放在了pstr当中,下面是一道例题
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
这里的str3 和 str4 指向的是一个同一个常量字符串。c语言会把常量字符串存放到单独的一个内存区域,当几个指针指向的是同一个字符串的时候,他们实际上会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同
2、数组指针变量
这里需要和之前学习的指针数组的概念进行区分,指针数组是一种数组,数组中存放的是地址(指针)。所以说,数组指针是指针变量
数组指针变量举例
int(*p)[10];
解释:p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整形的数组。所以p是一个指针,指向的是一个数组,叫数组指针。
关于数组指针变量的初始化
数组指针变量是用来存放数组地址的,那怎么获得数组的地址呢?就是&数组名
int arr[10] = {0};
&arr;
如果要存放整个数组的地址,就得存放在数组指针变量中,如下:
int(*p)[10] = &arr;
通过调试发现,p和&arr的类型是完全一样的
3、二位数组传参的本质
之前我们没有学习指针的时候,有一个二维数组需要传参给一个函数的时候,我们是这样写的:
#include <stdio.h>
void test(int a[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 ", a[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] 。那就意味着二维数组传参本质也就是传递了地址,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针形式的。如下:
#include<stdio.h>
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、函数指针变量
函数指针变量就是用来存储函数的地址的,能够通过地址调用函数的;
比如我们写了一个函数test(),使用这两种方式打印吃来的地址是一样的:
printf("test: %p\n", test);
printf("&test: %p\n", &test);
所以函数是有地址的,函数名就是函数的地址,当然也可以通过&函数名的方法获得函数的地址
如果想要把函数的地址存放起来,就需要用到函数指针变量了,与数组指针的写法非常类似:
int Add(int x, int y) {
return x + y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的
关于函数指针变量的使用
通过函数指针调用指针指向的函数
#include<stdio.h>
int Add(int x, int y) {
return x + y;
}
int main() {
int(*pf3)(int, int) = Add;
printf("%d\n", (*pf3)(2, 3));//5
printf("%d\n", pf3(3, 5));//8
return 0;
}
两端有趣的代码
(*(void (*)())0)();
void(*)( );函数指针类型,上面的代码就是一次函数调用
1、把0这个整数值,强制类型转化成一个函数的地址,这个函数没有参数,返回类型是void
2、然后再去调用0地址处的函数
void(*signal(int , void(*)(int)))(int);
signal是一个函数
signal函数的参数有两个,第一个是int类型,第二个是函数指针类型,该指针指向的函数参数是int,返回类型是void,signal函数的返回类型是这种类型的void(*)(int)函数指针,该指针指向的函数参数是int,返回类型是void。
关于typedef关键字
typedef是用来类型重命名的,可以将复杂的类型简单化
比如:
typedef unsigned int uint
如果是指针类型,能否重命名吗?其实也是可以的,比如,将int*重命名为ptr_t,可以这样写
typedef int* ptr_t;
对于数组指针和函数指针稍微有点区别:
比如我们有数组指针类型int(*)[5],需要重命名为parr_t,可以这样写:
typedef int(*parr_t)[5];//新的类型名必须放在*的右边
函数指针的重命名也是一样的比如,将void(*)(int)类型重命名为pf_t,那可以这样写:
typedef void(*pf_t)(int);//新的类型名必须写在*的右边