之前,简单的介绍过指针的基础知识,不外乎就是以下几点:
1、指针变量是个变量,用来存放地址,地址唯一标识一块内存空间,作用就是快速定位
2、指针的大小是固定的4 / 8个字节(32位平台 / 64位平台)
3、指针是有类型的,指针的类型决定了指针 ± 整数的步长时,指针解引用操作的权限
4、指针运算
详情跳转C指针(基础)
下面,我们讨论一下指针较为深层次的拓展:
字符指针:char*
#include<stdio.h>
#include<Windows.h>
int main()
{
char str1[] = "hello world.";
char str2[] = "hello world.";
char *str3 = "hello world.";
char *str4 = "hello world.";
if (str1 == str2){
printf("str1 == str2 \n");
}
else{
printf("str1 != str2 \n");
}
if (str3 == str4){
printf("str3 == str4 \n");
}
else{
printf("str3 != str4 \n");
}
system("pause");
return 0;
}
这里str3和str4指向的是同一个常量字符串。
c/c++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一字符串的时候,它们实际会指向同一内存区域,
但是用相同的常量字符串的数组初始化不同的数组的时候就会开辟出不同的内存块。
所以,str1与str2不相同,str3与str4不相同。
指针数组
顾名思义,指针数组就是一个存放指针的数组,其形如:
int* arr1[10]; //整形指针数组
char* arr2[10]; //一级字符型指针数组
char** arr3[10]; //二级字符型指针数组
数组指针
数组指针是指针,是能够指向数组的指针,其形如:
int (*p)[10]; //p先与*结合,说明p是一个指针变量,然后指向一个大小为10的整形数组。所以p是一个指针,指向一个数组
//需要注意:[]的优先级高于* ,所以必须加上()`来确保p先与*结合。否则的话会变成一个指针数组`
我们知道,数组名表示数组首元素的地址,那么,就有:
数组名只有在 &arr 和 sizeof(arr) 这两种情况下表示数组的地址,在其他情况中都表示数组首元素的地址,尽管这两种情况的结果在数值层面相等,但是含义完全不同
由此引出:
arr; //数组首元素地址
&arr; //数组的地址
arr+1; //数组第二个元素的地址
&arr+1; //下一个数组的地址
数组首元素的地址+1,表示数组中第二个元素的地址
数组的地址+1,表示跳过该整个数组的大小,下一个数组的大小,所以 &arr+1 相对于 &arr 的差值是 (元素类型*数组元素个数)
下面,通过代码验证一下:
#include<stdio.h>
#include<Windows.h>
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
printf("%p\n", arr);
printf("%p\n", arr + 1);
printf("%p\n", &arr);
printf("%p\n", &arr + 1);
system("pause");
return 0;
}
数组参数、指针参数
下面,将通过例题明确展示写代码时数组和指针传参的方法:
一维数组传参
#include<stdio.h>
#include<Windows.h>
/*
void test1(int arr[]){
}
void test1(int arr[10]){
}
*/
void test1(int *arr){
}
/*
void test2(int *arr[20]){
}
*/
void test2(int **arr){
}
int main()
{
int arr1[10] = { 0 };
int *arr2[20] = { 0 };
test1(arr1);
test2(arr2);
system("pause");
return 0;
}
二维数组传参
#include<stdio.h>
#include<Windows.h>
void test(int arr[3][5]){
}
void test(int arr[][]){ //错误
}
void test(int arr[][5]){
}
//二维数组传参,函数形参的设计只能省略第一个[]中的数字
//因为对一个二维数组而言,可以不知道有多少行,但必须知道有多少列
//二维数组传参降维成指向其内部元素类型(数组)的指针,即降维成一维数组指针
void test(int *arr){//错误
}
void test(int *arr[5]){ //错误,传进来的参数为指针数组,但是经过降维后,变成二级指针,与数组指针类型不匹配
}
void test(int **arr){ //错误,二级指针与数组指针类型不匹配
}
void test(int (*arr)[5]){
}
int main()
{
int arr[3][5] = { 0 };
test(arr);
system("pause");
return 0;
}
一级指针传参
#include<stdio.h>
#include<windows.h>
void print(int *p, int sz){
int i = 0;
for (i = 0; i < sz; i++){
printf("%d\n", *(p + i));
}
}
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int *p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
print(p, sz);
system("pause");
return 0;
}
二级指针传参
#include<stdio.h>
#include<Windows.h>
void test(int **ptr){
printf("num = %d\n", **ptr)
}
int main()
{
int n = 10;
int *p = &n;
int **pp = &p;
test(pp);
test(&p);
system("pause");
return 0;
}
函数指针
在这里,需要明确的是,函数也有地址,地址可以保存,所以,我们引入了函数指针的概念。
#include<stdio.h>
#include<windows.h>
void test()
{
printf("!!!!!");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
system("pause");
return 0;
}
这里输出的结果相同,都表示test函数的地址。
void (*pfun1)(); //函数指针。指向的函数无参数,返回值为void类型
void *pfun2(); //函数声明。返回值为void *类型,函数名为pfun2
函数指针数组
存放函数地址的数组称之为函数指针数组。
定义格式为:int (*parr[10])();
parr先与[]结合,说明parr为数组,数组的内容是int (*)() 类型的函数指针
函数指针数组的用途:转移表、回调函数
//转移表,两个数的四则运算实现
#include <stdio.h>
#include<Windows.h>
double add_(double x, double y) {
printf("x + y = %lf\n", x + y);
return x + y;
}
double sub_(double x, double y) {
printf("x - y = %lf\n", x - y);
return x - y;
}
double mul_(double x, double y) {
printf("x * y = %lf\n", x * y);
return x * y;
}
double div_(double x, double y) {
printf("x / y = %lf\n", x / y);
return x / y;
}
double(*oper_func[])(double x, double y) = { add_, sub_, mul_, div_ }; //转移表
int main(int argc, char* argv[])
{
double x = 1.1, y = 2.2;
for (int i = 0; i<4; i++) {
oper_func[i](x, y);
}
system("pause");
return 0;
}
指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针
其定义为:
#include<stdio.h>
#include<windows.h>
void test(const char *str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[10])(const char*) = &pfunArr;
system("pause");
return 0;
}
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来 调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。