目录
- 字符指针;
- 数组指针/指针数组;
- 数组传参/指针传参;
- 函数指针/函数指针数组;
- 指向函数指针数组的指针;
- 回调函数
一,字符指针
指向字符类型的指针,即字符指针(char*);
char ch = 'a';
char* pch = &ch;
int main()
{
//注将字符串赋值予字符串指针,其实时将首元素的地址赋予指针
char* pch = "abcd";
printf("%c\n", *pch);
return 0;
}
//结果:a
//参考<<剑指offer>>
int main()
{
//str1,str2分别开辟内存空间,地址不同
char str1[] = "abcd";
char str2[] = "abcd";
//pch1、pch2都是指向常量"abcd"的地址,相同
char* pch1 = "abcd";
char* pch2 = "abcd";
if (str1 != str2)
printf("str1 != str2 ");
if (pch1 == pch2)
printf("pch1 == pch2 ");
return 0;
}
//结果:str1 != str2 pch1 == pch2
二,指针数组
存放指针的数组,即数组元素为指针类型;
int main()
{
int a = 1;
int b = 2;
int c = 3;
int* parr[] = { &a,&b,&c }; //指针数组
return 0;
}
int main()
{
int a[] = { 1,2,3,4,5 };
int b[] = { 2,3,4,5,6 };
int c[] = { 3,4,5,6,7 };
int* parr[] = { a,b,c };//指针数组
int i = 0;
for (i; i < 3; i++)
{
int j = 0;
for (j; j < 5; j++)
{
printf("%d ", parr[i][j]); //或*(parr[i]+j)或*(*(parr + i) + j)
}
printf("\n");
}
return 0;
}
三,数组指针
是指针,存放指向整个数组地址的指针,如整型指针、字符指针;
int arr[5] = { 1,2,3,4,5 };
int(*parr)[5] = &arr; //数组指针
char str[5] = "abcd"; //字符数组
char* arr[5] = { arr,arr + 1,arr + 2,arr + 3 }; //指针数组
char* (*parr)[5] = &arr; //数组指针
数组名含义
- arr,数组首元素的地址;
- &arr,取整个数组的地址;
- sizeof arr,取的也是整个数组;
注,+1跳过的元素是不一样的;
int main()
{
int arr[5] = { 1,2,3,4,5 };
printf("%p %p\n", arr, arr + 1);
printf("%p %p\n", &arr[0], &arr[1]);
printf("%p %p\n", &arr, &arr + 1);
return 0;
}
//结果:
//00EFFD60 00EFFD64
//00EFFD60 00EFFD64
//00EFFD60 00EFFD74
int arr[5]; //是一个包含5个元素的数组,每个元素为整型
int* parr[10]; //是一个包含5个元素的数组,每个元素为整型指针
int(*parr)[10]; //是一个数组指针,指针一个包含10个整型元素的数组
int(*parr[5])[10]; //是一个指针数组,包含5个元素,每个元素为数组指针,该数组包含10个整型元素
四,数组传参/指针传参
数组传参,即将数组作为函数的参数;
//一维数组传参
void test1(int arr1[10]) //或int arr1[],或int* p1
{}
void test2(int* arr2[10]) //或int* arr2[],或int** p2
{}
int main()
{
int arr1[10] = { 0 };
int* arr2[10] = { 0 };
test1(arr1); //arr1,首元素地址,即指针
test2(arr2); //arr2,首元素地址,即指针
return 0;
}
//二维数组传参
void test(int arr[3][5]) //或int arr[][5],或int(*arr)[5]
{}
int main()
{
int arr[3][5] = { 0 };
test(arr); //arr,首元素地址,即指针
return 0;
}
指针传参,即将指针作为函数的参数;
//一级指针传参
void print(int* p, int sz)
{
int i = 0;
for (i; i < sz; i++)
{
printf("%d ", *(p + i));
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int* p = arr;
int sz = sizeof arr / sizeof arr[0];
print(p, sz);
return 0;
}
//二级指针传参
void print(int(**pp)[6], int sz)
{
int i = 0;
for (i; i < sz; i++)
{
printf("%d ", *(**pp + i));
}
}
int main()
{
int arr[6] = { 1,2,3,4,5,6 };
int(*p)[6] = &arr;
int(**pp)[6] = &p;
int sz = sizeof arr / sizeof arr[0];
print(pp, sz);
return 0;
}
五,函数指针
- 是指向函数的指针;
- &函数名 == 函数名,都是函数的地址;
void test()
{
;
}
int main()
{
printf("%p ", test);
printf("%p ", &test);
return 0;
}
//结果:006A1253 006A1253
int Add(int x, int y)
{
return x + y;
}
int main()
{
//函数指针
int(*pfun)(int, int) = Add; //或 &Add
printf("%d ", Add(1, 2));
printf("%d ", (*pfun)(1, 2)); //调用函数(*可省略或写多个*均可);
printf("%d ", pfun(1, 2));
return 0;
}
//结果:3 3 3
- 如上案例,*可省略,pfun == add,add == pfun;
- 可重定义函数指针类型,如 typedef int( *pfun_t )(int,int) ;
注:《C陷阱和缺陷》—— 案例;
//void(*)() - 函数指针
//(void(*)())0 - 强制将整型0,转换为函数指针类型
//*(void(*)())0 - 解引用函数指针
//(*(void(*)())0)() - 函数调用
(*(void(*)())0)();
//void(*)(int) - 函数指针类型,返回类型为void,参数类型为int
//signal - 函数返回类型void(*)(int),也是函数指针
//signal - 函数参数类型int, void(*)(int)
void(*signal(int, void(*)(int)))(int);
//可重定义函数指针,typedef void(*pfun_t)(int)
//则可简写为,pfun_t signal(int, pfun_t)
六,函数指针数组
存放同类型函数指针的数组,即函数指针数组;
//parr有10个元素,每个元素类型为int(*)()
int(*parr[10])();
函数指针数组的用途:转移表 ——《C和指针》
void menu()
{
printf("===========================\n");
printf("==1,Add 2,Sub 3,Mul 4,Div==\n");
printf("===========================\n");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
int main()
{
int input = 0;
int x = 0, y = 0;
int(*pfun[])(int, int) = { 0,Add,Sub,Mul,Div }; //函数指针数组
do
{
menu();
printf("请选择:");
scanf("%d", &input);
if (input >= 1 && input <= 4)
{
printf("请输入待运算的数:");
scanf("%d%d", &x, &y);
printf("%d\n", pfun[input](x, y));
}
else if (0 == input)
printf("退出\n");
else
printf("输入错误重新输入!\n");
} while (input);
}
七,指向函数指针数组的指针
- 是一个指针;
- 指针指向一个数组 ;
- 数组的元素为函数指针;
//函数指针
void(*pfun)(int);
//函数指针数组
void(*pfunArr[5])(int);
//指向函数指针数组的指针
void(*(*pfunArr)[5])(int);
八,回调函数
- 是指通过函数指针调用的函数;
- 函数指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,即称为回调函数;
- 不是直接调用函数,而是特定事件或条件发生时由另一方调用;
void menu()
{
printf("===========================\n");
printf("==1,Add 2,Sub 3,Mul 4,Div==\n");
printf("===========================\n");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
void calc(int(*pfun)(int, int)) //回调函数
{
int x = 0, y = 0;
printf("请输入待运算数:");
scanf("%d%d", &x, &y);
printf("%d\n", pfun(x, y));
}
int main()
{
int input = 0;
do
{
menu();
printf("请输入:");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出!\n");
break;
case 1:
calc(Add);
break;
case 2:
calc(Sub);
break;
case 3:
calc(Mul);
break;
case 4:
calc(Div);
break;
default:
printf("输入错误重新输入!\n");
break;
}
} while (input);
return 0;
}
九,练习
- 数组名代表首元素的地址;
- 但sizeof arr,&arr,其中arr表示整个数组;
//一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //16,整个数组大小
printf("%d\n", sizeof(a + 0)); //4/8,首元素地址大小
printf("%d\n", sizeof(*a)); //4,首元素大小
printf("%d\n", sizeof(a + 1)); //4/8,第二元素地址大小
printf("%d\n", sizeof(a[1])); //4,第二元素大小
printf("%d\n", sizeof(&a)); //4/8,整个数组地址大小
printf("%d\n", sizeof(*&a)); //16,整个数组大小
printf("%d\n", sizeof(&a + 1)); //4/8,整个数组地址的下一个地址大小
printf("%d\n", sizeof(&a[0])); //4/8,首元素地址大小
printf("%d\n", sizeof(&a[0] + 1)); //4/8,第二元素地址大小
//字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); //6,整个数组大小
printf("%d\n", sizeof(arr + 0)); //4/8,首元素地址大小
printf("%d\n", sizeof(*arr)); //1,首元素大小
printf("%d\n", sizeof(arr[1])); //1,第二元素大小
printf("%d\n", sizeof(&arr)); //4/8,整个数组地址大小
printf("%d\n", sizeof(&arr + 1)); //4/8,整个数组地址的下一个地址大小
printf("%d\n", sizeof(&arr[0] + 1)); //4/8,第二元素地址大小
//size_t strlen( const char *string ),参数为字符指针
printf("%d\n", strlen(arr)); //随机,首元素地址
printf("%d\n", strlen(arr + 0)); //随机,首元素地址
printf("%d\n", strlen(*arr)); //err,首元素
printf("%d\n", strlen(arr[1])); //err,第二元素
printf("%d\n", strlen(&arr)); //随机,首元素地址(由整个数组地址类型转换)
printf("%d\n", strlen(&arr + 1)); //随机,整个数组地址的下个地址
printf("%d\n", strlen(&arr[0] + 1)); //随机,第二元素地址
//字符串
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); //7,整个数组大小
printf("%d\n", sizeof(arr + 0)); //4/8,首元素地址大小
printf("%d\n", sizeof(*arr)); //1,首元素大小
printf("%d\n", sizeof(arr[1])); //1,第二元素大小
printf("%d\n", sizeof(&arr)); //4/8,整个数组地址大小
printf("%d\n", sizeof(&arr + 1)); //4/8,整个数组地址的下一个地址大小
printf("%d\n", sizeof(&arr[0] + 1)); //4/8,第二元素地址大小
//size_t strlen( const char *string ),参数为字符指针
printf("%d\n", strlen(arr)); //6,首元素地址
printf("%d\n", strlen(arr + 0)); //6,首元素地址
printf("%d\n", strlen(*arr)); //err,首元素
printf("%d\n", strlen(arr[1])); //err,第二元素
printf("%d\n", strlen(&arr)); //6,首元素地址(由整个数组地址类型转换)
printf("%d\n", strlen(&arr + 1)); //随机,整个数组地址的下个地址
printf("%d\n", strlen(&arr[0] + 1)); //5,第二元素地址
//字符指针
char* p = "abcdef";
printf("%d\n", sizeof(p)); //4/8,字符指针大小
printf("%d\n", sizeof(p + 1)); //4/8,字符指针+1大小
printf("%d\n", sizeof(*p)); //1,字符'a'大小
printf("%d\n", sizeof(p[0])); //1,字符'a'大小
printf("%d\n", sizeof(&p)); //4/8,字符指针的地址大小
printf("%d\n", sizeof(&p + 1)); //4/8,字符指针地址的下个地址大小
printf("%d\n", sizeof(&p[0] + 1)); //4/8,字符'b'的地址大小
//size_t strlen( const char *string ),参数为字符指针
printf("%d\n", strlen(p)); //6,从字符'a'开始
printf("%d\n", strlen(p + 1)); //5,从字符'b'开始
printf("%d\n", strlen(*p)); //err,首元素
printf("%d\n", strlen(p[0])); //err,首元素
printf("%d\n", strlen(&p)); //随机,字符指针的地址
printf("%d\n", strlen(&p + 1)); //随机,字符指针+1的地址
printf("%d\n", strlen(&p[0] + 1)); //5,字符'b'地址
//二维数组
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); //48,整个数组的大小
printf("%d\n", sizeof(a[0][0])); //4,首元素的首元素的大小
printf("%d\n", sizeof(a[0])); //16,首元素的大小,a[0]代表整个首元素数组
printf("%d\n", sizeof(a[0] + 1)); //4/8,首元素的第二个元素地址大小
printf("%d\n", sizeof(*(a[0] + 1))); //4,首元素的第二个元素大小
printf("%d\n", sizeof(a + 1)); //4/8,第二元素的地址大小
printf("%d\n", sizeof(*(a + 1))); //16,第二元素的大小
printf("%d\n", sizeof(&a[0] + 1)); //4/8,第二元素地址大小
printf("%d\n", sizeof(*(&a[0] + 1))); //16,第二元素的大小
printf("%d\n", sizeof(*a)); //16,首元素的大小
printf("%d\n", sizeof(a[3])); //16,第四元素的大小(越界访问)
//虽然看起来是越界访问,但sizeof只计算类型属性,括号内表达式并未实际访问或计算;
//a[3]的类型为int [4];
//sizeof只计算类型属性,括号内表达式并未实际访问或计算
short s = 5;
int a = 4;
printf("%d\n", sizeof(s = a + 6)); //2
printf("%d\n", s); //5