一 , 字符指针变量
在我们所了解的指针类型中,我们知道有一种指针类型为 字符指针 char*
int main()
{
char a = 'a';
char* pa = &a;
printf("%c\n", *pa);
return 0;
}
一般来说,我们是这样去存放字符地址
//字符指针变量
int main()
{
//char a = 'w';
//char* pa = &a;
//char* pa = "abcdef";
//这种赋值是把字符串首字符的地址赋值给pa --- 常量字符串
char arr[] = "abcdef";
char* p = arr;
//p 存放的数组首元素的空间
return 0;
}
我们看代码
char* pa = "abcdef";
这里并不是把字符串放在字符指针pa里,其本质是把字符串 abcdef 的首字符a 的地址放在了pa中。
例题1:
int main()
{
char* pa = "abcdef";
*pa = 'w';
return 0;
}
如果我们写下了这组代码,程序会崩,运行不了,因为常量字符串是不可以被更改!我们可以借助const 进行修改
int main()
{
const char* ps = "abcdef";
//限制了*ps
return 0;
}
例题2:
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/C++会把常量字符串存储到单独的一个区域,当几个指针指向同一个字符串的时候,他们实际上指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候,就会开辟出不同的内存块。
二 , 数组指针变量
2.1 数组指针变量是什么?
首先我们得先把数组指针与指针数组区分开:
指针数组: 存放指针(地址)的数组
数组指针: 存放的是数组指针(地址)
我们可以类比一下:
所以数组变量应该是:存放的是数组的地址,能够指向数组的指针变量
int main()
{
//字符指针
char a = 'a';
char* pa = &a;
//整型指针
int b = 10;
int* pb = &b;
//数组指针
int arr[10] = { 0 };
int(*pc)[10] = &arr;//数组的地址
//pc就是数组指针变量
char ch[5];
char(*pd)[5] = &ch;
int* arr2[6];
int*(*p1)[6] = &arr;
int arr[10] = { 0 };
int(*p)[10] = &arr;//p 是数组指针变量
//int (*) [10]
return 0;
}
哪一个是数组指针
int *p1[10];
int (*p2)[10];
标记红色的是数组指针,p先与*结合,说明 p 是一个 指针变量,然后指针指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
注意:[ ] 的优先级高于 * ,所以必须加上 ( ) 来保证p 先于 * 结合。
2.2 数组指针变量的初始化
首先,我们要获取数组的地址 : 通过 & (数组名)
然后,创建一个数组指针变量存放数组的地址
int (*p) [10] = &arr;
| | |
| | |
| | p指向数组元素的个数
| p是数组指针的变量名
p指向的数组的元素类型
三 ,二维数组传参的本质
在过去打印二维数组的时候,是这样写的:
void Print(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 };
Print(arr, 3, 5);
return 0;
}
然后我们发现,实参是数组名,然后根据:数组名是数组首元素的地址,我们可以联想到,实参是可以用指针变量 形式的。
随后我们思考,二维数组的首元素是什么?根据二维数组的定义,我们 其实可以把它看成是一维数组的数组,也就是二维数组的每个元素是一个一维数组。那么二维数组的首元素就是第一行,是一个一维数组。
所以,根据数组名是数组首元素的地址这个规则,二维数组的数组名,表示的就是第一行的地址 ,传递的是第一行这个一维数组的地址。所以形参可以写成指针形式。
void Print(int(*p)[5], int c, int l)//传递的是第一行一维数组的地址,然后第一行的有5个元素
{
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", p[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5, 3,4,5,6,7, 5,6,8,9,1};
Print(arr, 3, 5);
return;
}
四 ,函数指针变量
4.1 函数指针变量的创建
前面学习了整型指针,数组指针,,字符指针的时候,我们知晓指针变量是用来存放变量的地址的,方便未来使用。以此类推,我们不难得出结论,函数指针变量应该是用来存放函数地址,未来通过地址来调用函数。
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}
通过这着一串代码,我们知道,函数也是有地址的,而且与数组不同的是,&函数名与函数名的地址相同。
取到地址后,接下来就是创建变量存放地址了
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n", &Add);
printf("%p\n", Add);
int (*pa)(int, int) = Add;
int (*pa)(int x, int y) = &Add;//x,y或者省略都是可以的
return 0;
}
int (*pa) (int x, int ,y)
| | |
| | pa指向函数的参数类型和个数
| 函数变量名
pa指向函数的返回类型
去掉函数指针变量名就是函数指针变量的类型
int ( * )(int x ,int y)
4.2 函数指针变量的使用
int Add(int x, int y)
{
return x + y;
}
int main()
{
//函数指针变量的创建
int (*pa)(int, int) = Add;
//函数指针变量的调用
printf("%d\n", pa(10, 20));
printf("%d\n", (*pa)(20, 30));
return 0;
}
因为我们知道无论是 &函数名,还是 函数名 都可以用来表示函数的地址,所以在调用函数指针变量的时候, * 可写可不写。
4.3 两端有趣的代码
“来自《C陷阱和缺陷》的这本书”
(* ( void ( * ) ( ) ) 0 ) () ;
void ( *signal(int, void(*)(int) ) ) (int);
4.4 typedef 关键字
typedef 是用来类型重命名的,可以将复杂的类型,简单化
1. unisgned int 写起来不方便的时后 , 可以把它重命名成 XXX
typedef unsigned int unit;
int main()
{
//unsigned int a = 5;
unit a = 5;
//将unsigned 重命名为unit
return 0;
}
2. 如果是指针的类型,也是可以重命名的
typedef int* ptr_t;
int main()
{
//int* pa;
ptr_t pa;
return 0;
}
对于数组指针和函数指针的重命名,稍微要写区别,但是我们要记住一点,就是名字要放在 * 的右边!!!
3. 数组指针变量重命名
typedef void (*pa) [5];
4. 函数重命名
typedef void(*pa) ( int )
然后如果想要简化代码二:
typedef void ( * pa )(int);
pa signal( int , pa)
int* p1, p2; //p1 是int* , p2 是 int
prt_t p3, p4 //p3,p4都是int*
五 , 函数指针数组
存放函数指针的数组。
int Add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int main()
{
int (*p1)(int, int) = Add;
int (*p1)(int, int) = sub;
//把Add sub 这些函数的地址存放在一个数组中
//存放函数指针的数组 --- 函数指针数组
int (*pArr[4])(int, int) = { Add,sub };
//指针数组
char* ch[5];
int* arr[6];
return 0;
}
函数指针数组在函数指针变量的基础上,加了 [ ] 与数字,去掉变量名,就是函数指针数组的类型。