一、字符指针
char arr[] = "hello";
char* p = "hello"; //p中存的是"hello"字符串首元素的地址
printf("%c\n",*arr); //h
printf("%c\n", *p); //h
printf("%s\n", arr); //"hello",以字符串格式打印,从首元素开始,遇到'\0'结束
printf("%s\n", p); //"hello"
*arr = 'x'; //存放在数组中,可以修改
//*p = 'y'; //err,char* p = "hello";为常量,不可修改
printf("%c\n", *arr); //x
//printf("%c\n", *p);
1.1练习题
char str1[] = "hello bit.";
char str2[] = "hello bit.";
char* str3 = "hello bit.";
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");
//str1 and str2 are not same
//str3 and str4 are same
str1为自己数组中首元素的地址, str2为自己数组中首元素的地址, str1≠str2;即内存中保存有两份"hello bit.",但存放的地址不同;
str3和str4中对应的字符串均为常量(不可修改),内容一样,又不可修改,在内存中只保留一份,即str3和str4均指向同一个字符串"hello bit."的首字母地址。
二、指针数组
//指针数组
//是数组-数组中存放的是指针(地址)
//int* arr[3];存放整形指针的数组
int a[5] = {1,2,3,4,5};
int b[5] = {2,3,4,5,6};
int c[5] = {3,4,5,6,7};
int* arr[3] = {a,b,c};
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
printf("%d ", *(arr[i] + j));//arr[i]+j<=>arr[i][j]
printf("\n");
}
三、数组指针
3.1 书写方式
//数组指针 - 是指向数组的指针
int a = 1;
int* pa = &a;
char b = 'w';
char* pb = &b;
int arr[10] = {1,2,3,4,5};
//arr,数组名,是首元素arr[0]的地址
//&arr,取出的是数组的地址
int (*parr)[10] = &arr;//parr数组指针,指向一个数组,存放的是数组的地址
//(*parr)确保parr是指针,[10]确保是是数组指针,int:指针指向的数组元素类型为整形
double* d[5];
double* (*pd)[5] = &d;//(*pd)确保pd是指针,[5]确保是数组指针,double*:指针指向的数组元素类型为浮点型指针
3.2 arr和&arr区别
int arr[10] = {0};
int* p1 = arr;
int(*p2)[10] = &arr;
printf("%p\n",arr); //0053F9E4
printf("%p\n", &arr); //0053F9E4
printf("%p\n", p1); //0053F9E4
printf("%p\n", p2); //0053F9E4
printf("%p\n", p1+1); //0053F9E8,比0053F9E4多4
printf("%p\n", p2+1); //0053FA0C,比0053F9E4多40
3.3 注意
数组名是数组首元素地址
但有2个例外:
1.sizeof(数组名):计算整个数组的大小,单位是字节
2.&数组名:取出的是整个数组的地址
3.4 &arr进阶
3.4.1 一维数组
int arr[5] = {1,2,3,4,5};
int(*p)[5] = &arr; //p是指向数组arr的指针
for (int i = 0; i < 5; i++)
printf("%d ", *((*p) + i)); //*p是数组首元素地址(*p=*(&arr)=arr)
//*((*p) + i)<=>*(arr + i)<=>arr[i]
p是指向数组的数组指针,存放的是整个数组的地址;*p解引用表示整个数组首元素的地址;
int arr1[5] = { 1,2,3,4,5 };
int a = 0;
int* pa = &a;
int* arr2[2] = {pa}; //数组中有2个元素,都是整形指针类型
int(*p1)[5] = &arr1;
int* (*p2)[2] = &arr2;
printf("%d\n",**p1); //1,*p1=arr1,**p1=*arr1=数组首元素
printf("%d\n", **(arr2)); //0,*arr2=pa=&a数组首元素,**arr2=*pa
printf("%d\n", ***p2); //0 *p2 = arr2
3.4.2 一维数组传参
void test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int *arr)//ok
{}
void test2(int *arr[20])//ok
{}
void test2(int **arr)//ok,arr2是数组首元素(地址)的地址,即是地址的地址
{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
test(arr);
test2(arr2);
}
3.4.3 二维数组
//arr[3][5]
//1.arr表示二维数组首元素地址,但此首元素为第一行的一维数组,是一个数组指针
//即,arr是一个指向第一行一维数组的数组指针
//2.&arr表示指向整个二维数组的指针
//3.*arr表示指向第一行一维数组首元素的地址《=》arr[0]
//4.**arr表示指向第一行一维数组首元素 <=>*arr[0]<>=arr[0][0]
int arr[3][5] = {{1,2,3,4,5},
{22,33,44,55,66},
{333,444,555,666,777}};
printf("%p\n",*arr); //第一行一维数组首元素地址,即1的地址
printf("%p\n", arr[0]); //第一行一维数组首元素地址,即1的地址
printf("%p\n", arr + 1);//第二行一维数组地址
printf("%p\n", *(arr + 1)); //第二行一维数组地址首元素地址
printf("%p\n", arr[1]); //第二行一维数组地址首元素地址
printf("%d\n", **arr); //取出数字1
printf("%d\n",*arr[0]);//1
printf("%d\n", *(*arr + 1)); //2
printf("%d\n", *(arr[0] + 1)); //2, =>arr[0][1]
printf("%d\n", *arr[1]); //22,=>arr[1][0]
printf("%d\n", **(arr + 1));//22
3.4.4二维数组传参
void test(int arr[3][5])//ok
{}
void test(int arr[][])//err
{}
void test(int arr[][5])//ok
{}
void test(int *arr)//err,整形指针
{}
void test(int* arr[5])//err,指针数组
{}
void test(int (*arr)[5])//ok
{}
void test(int **arr)//err,二级指针
{}
int main()
{
int arr[3][5] = {0};
test(arr); //arr是数组指针
}
四、函数指针
4.1 书写方式
//1.指向函数的指针
//2.存放函数地址的指针
//数组名 !=& 数组名
//函数名 == &函数名(意义相同,写法不同)
int add(int x,int y)
{
return x+y;
}
int main()
{
printf("%p\n",add); //001613CA
printf("%p\n",&add); //001613CA
//pf就是一个函数指针变量
int (*pf)(int,int) = &add;
//或者 int (*pf)(int,int) = add; pf<==>add
int ret = add(3,5);
int ret1 = (*pf)(3,5); //*只是一个摆设
int ret2 = pf(3,5);
printf("%d ",ret1); //8
printf("%d ",ret2); //8
return 0;
}
4.2 练习题
//题一
int main()
{
( * ( void ( * ) ( ) ) 0 ) ( );
//调用0地址处的函数,该函数无参,返回类型是void
//1.void (*) () - 函数指针类型;//void (*p) () - 函数指针变量
//2.( void (*) () ) 0 - 对0进行强制类型转换,被解释为一个函数地址
//3. *( void (*) () ) 0 - 对0地址进行解引用操作
//4.( * ( void ( * ) ( ) ) 0 ) ( ); - 调用0地址处的函数
return 0;
}
//题二
void (* signal(int, void (*) (int) ) ) (int);
//简化理解:void (*) (int) signal(int, void (*) (int));
//1.signal先和()结合,说明signal是函数名
//2.signal函数的第一个参数类型是int,第二个参数类型是函数指针
//该函数指针,指向一个参数为int,返回类型是void的函数
//3.signal函数的返回类型也是一个函数指针
//该函数指针,指向一个参数为int,返回类型是void的函数
五、函数指针数组
//函数指针数组
//存放函数指针的数组
int add(int x,int y)
{
return x+y;
}
int sub(int x, int y)
{
return x - y;
}
int main()
{
int (*pf1)(int, int) = add;
int (*pf2)(int, int) = sub;
int (*pfArr[2])(int, int) = {add,sub}; //pfArr是函数指针数组
return 0;
}
int arr[5];
int (*p1)[5] = &arr; //p1是指针,指向整形数组的指针
int* arr[5];
int* (*p2)[5] = &arr; //p2是指针,指向【整型指针数组】的指针
int (*p)(int,int) //p是指针,函数指针
int (*p2[5])(int,int) //p2与[5]先结合,是数组,函数指针数组
int (*(*p3)[5])(int,nit) = &p2; //取出的是函数指针数组的地址
//p3是一个指向【函数指针数组】的指针
六、回调函数
6.1 定义
void menu()
{
printf("1.add 2.sub\n");
printf("0.exit\n");
}
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
//回调函数
int calc(int(*pf)(int,int))
{
printf("请输入两个数字");
int x = 0;
int y = 0;
scanf_s("%d %d",&x,&y);
return pf(x,y);
}
int main()
{
int input = 0;
do {
menu();
int ret = 0;
printf("请选择:");
scanf_s("%d",&input);
switch (input)
{
case 1:
ret = calc(add);
printf("ret=%d\n", ret);
break;
case 2:
ret = calc(sub);
printf("ret=%d\n", ret);
break;
case 0:
break;
default:
printf("输入错误,请重新选择\n");
}
} while (input);
return 0;
}