1 字符指针
int main (){char ch = 'w' ;char * pc = & ch ;* pc = 'w' ;return 0 ;}
还有一种写法:
int main (){const char* pstr = "hello bit." ; // 这里是把一个字符串放到 pstr 指针变量里了吗?printf ( "%s\n" , pstr );return 0 ;}
有以下面试题:
#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/C++ 会把 常量字符串 存储到 单独的一个内存区域。 当几个指针,指向同一个字符串的时候,他们实际会指向同一块内存。 但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1 和 str2 不同, str3 和 str4 不同。
2 指针数组
指针数组是数组!
字符数组:存放字符的数组
整型数组:存放整形的数组
指针数组:存放指针(地址)的数组,存放在数组中的元素都是指针类型的
使用指针数组,模拟一个二维数组。
int main()
{
//使用指针数组,模拟一个二维数组
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
//指针数组
int* arr[] = { arr1, arr2, arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
3 数组指针
3.1 数组指针的定义
数组指针是指针?还是数组?答案是:指针。
整形指针: int * pint ; 能够指向整形数据的指针。浮点型指针: float * pf ; 能够指向浮点型数据的指针。那数组指针应该是:能够指向数组的指针。
数组名是数组首元素的地址
1. sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
2. &数组名,这里的数组名表示整个数组,取出的是 数组的地址
3.2 &数组名 VS 数组名
int main()
{
int arr[10];
printf("%p\n", arr);//int*
printf("%p\n", arr+1);
printf("%p\n", &arr[0]);//int*
printf("%p\n", &arr[0] + 1);
printf("%p\n", &arr);//
printf("%p\n", &arr+1);
//指针类型决定了指针+1,到底+几个字节
return 0;
}
前两个都+4,最后一个+了40.
int ( * p )[ 10 ]; (方框里的数字不可省略)// 解释: p 先和 * 结合,说明 p 是一个指针变量,然后指着指向的是一个大小为 10 个整型的数组。所以 p 是一个指针,指向一个数组,叫数组指针。// 这里要注意: [] 的优先级要高于 * 号的,所以必须加上()来保证 p 先和 * 结合。
int main()
{
int arr[10] = {0};
int (*p)[10] = &arr;
}
int main()
{
char* arr[5];
char* (*pc)[5] = &arr;
}
3.3 数组指针的使用
一维数组:(不推荐)
#include <stdio.h>int main (){int arr [ 10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 };int ( * p )[ 10 ] = & arr ; // 把数组 arr 的地址赋值给数组指针变量 p// 但是我们一般很少这样写代码return 0 ;}
数组指针一般运用在二维数组上:
这是最常见的一种写法,二维数组传参,形参也用了二维数组的形式
void print(int arr[3][5], int r, int c)
{
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; 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 arr[], int sz) //形参是数组的形式
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);//arr[i]===>*(arr+i)
}
}
void print(int* arr, int sz) //形参是指针的形式
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);//arr[i]===>*(arr+i)
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz);
return 0;
}
二维数组的数组名可以表示为数组首元素的地址,而二维数组的首元素地址表示的就是第一行的地址。
回过头来看上面二维数组的传参,可以写为
void print(int (*p)[5], int r, int c)
{
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", p[i][j]); // p[i] <==> *(p+i)加1就跳过一行
}
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;
}
形参部分为指针。
以上就是数组指针的真正用法。
3.4 总结
说说以下代码的意义。
int arr [ 5 ];
int * parr1 [ 10 ];
int ( * parr2 )[ 10 ];
int ( * parr3 [ 10 ])[ 5 ];
parr3是一个数组,是存放数组指针的数组,这个数组里面有10个元素,int(*)[5] 。 存放的这个数组指针,指向的数组有5个元素,每个元素是int类型。
4 数组参数、指针参数
4.1 一维数组传参
#include <stdio.h>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? √{}int main(){int arr[10] = {0};int *arr2[20] = {0};test(arr);test2(arr2);}
数组传参,形参可以写成数组形式,也可以是指针形式。数组传参的本质是,传递了数组首元素的地址。
4.2 二维数组传参
void test(int arr[3][5]) //ok? √{}void test(int arr[][])//ok?× 行可以省略,列不能省略{}void test(int arr[][5])//ok? √{}//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。//这样才方便运算。void test(int *arr)//ok?×{}void test(int* arr[5])//ok? × 这是一个指针数组,肯定不对。这里要不是一个指针,要不就是一个二维数组{}void test(int (*arr)[5])//ok?√{}void test(int **arr)//ok?×{}int main(){int arr[3][5] = {0};test(arr);}
4.3 一级指针传参
#include <stdio.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 };int * p = arr ;int sz = sizeof ( arr ) / sizeof ( arr [ 0 ]);//一级指针 p ,传给函数print ( p , sz );return 0 ;}
这是很简单的,但是我们真正需要思考的问题是
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
例如
void test1 ( int * p ){}//test1 函数能接收什么参数?
void test2 ( char* p ){}//test2 函数能接收什么参数?
4.4 二级指针传参
#include <stdio.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 );return 0 ;}
思考
当函数的参数为二级指针的时候,可以接收什么参数?
void test(int ** p)
{}
int n = 10;
int *p = &n;
int **pp = &p;
int* arr[6];
test(&p);
test(pp);
test(arr); //arr表示首元素地址,也就是int*的地址
5 函数指针
数组指针 - 指向数组的指针 - 存放的是数组的地址 - &数组名就是数组的地址
函数指针 - 指向函数的指针 - 存放的是函数的地址 - 怎么得到函数的地址呢?&函数名?
答案是:是的。
需要注意的是:
&函数名 就是函数的地址
函数名 也是函数的地址并且他们类型相同。
int Add(int x, int y)
{
return x + y;
}
int (*pf1)(int, int) = Add; //pf1就是函数指针变量
int (*pf2)(int, int) = &Add;
写法和数组指针非常的类似。
int main()
{
//&函数名就是函数的地址
//函数名也是函数的地址
printf("%p\n", &Add);
printf("%p\n", Add);
int (* pf2)(int, int) = &Add;
int ret = (* pf2)(2, 3); //这里的*可以不用写,可以直接写为pf2(2,3);
//因为我们最开始的写法就是Add(2,3) ,这里的Add就是函数名也就是一个地址,所以在这里可以将*省略。
printf("%d\n", ret); //5
return 0;
}
6 函数指针数组
int * arr [ 10 ];// 数组的每个元素是 int*char *arr[5];//字符指针数组 - 数组 - 存放的是字符指针char*那么,函数指针数组数组 - 存放的是函数指针 - 存放的是函数的地址
6.1 函数指针数组的定义
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) = ⋐
//数组中存放类型相同的多个元素
int (* pfArr[4])(int, int) = {&Add, &Sub};//pfArr 是函数指针数组 - 存放函数指针的数组
//pfArr中的每一个元素都是一个函数指针,存放函数的地址
return 0;
}
int ( * pfarr [ 10 ])(); [ ]比*优先级高parr1 先和 [] 结合,说明 pfarr 是数组,数组的内容是什么呢?是 int (*)() 类型的函数指针。
6.2 函数指针数组的用途
函数指针数组的用途:转移表
例子:模拟计算机
初始写法
void menu()
{
printf("****************************\n");
printf("*** 1. add 2. sub ***\n");
printf("*** 3. mul 4. div ***\n");
printf("*** 0. exit ***\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;
int y = 0;
int ret = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = Add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = Sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = Mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出计算器\n");
break;
default:
printf("选择错误, 重新选择\n");
break;
}
} while (input);
return 0;
}
用函数指针数组:
void menu()
{
printf("****************************\n");
printf("*** 1. add 2. sub ***\n");
printf("*** 3. mul 4. div ***\n");
printf("*** 0. exit ***\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;
int y = 0;
int ret = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
//函数指针数组 - 转移表
int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};
// 0 1 2 3 4
if (0 == input)
{
printf("退出计算器\n");
}
else if (input >= 1 && input <= 4)
{
printf("请输入2个操作数:");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("ret = %d\n", ret);
}
else
{
printf("选择错误,重新选择!\n");
}
} while (input);
return 0;
}
这种写法很巧妙,但是有一个约束,就是只能往int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};传参数为int,int的函数,并且返回类型也为int。
7 指向函数指针数组的指针
首先我们先写指向整型指针数组的指针
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
int c = 0;
int* arr[] = { &a,&b,&c };//整形指针数组
int* (*p)[3] = &arr;//p是指针,是指向整形指针数组的指针
return 0;
}
类比的,我们可以写出指向函数指针数组的指针
#include<stdio.h>
int main()
{
int(*arr[5])(int, int) = { NULL,&Add,&Sub,&Mul,&Div }; //arr是函数指针数组
p = &arr;//存放函数指针数组的指针
int(*(*p)[5])(int, int) = &arr;
return 0;
}
p就是能够指向函数指针数组的一种指针。
无论是函数指针&函数指针数组&函数指针数组的指针等等,可以一直延申下去!
我们在书写变量时,首先将变量p写出来,在添加其类型。
也可以从简单的函数指针的基础上修改!