指针的进阶
1.指针的概念
- 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
- 指针的大小是固定的4/8个字节(32位平台/64位平台)。
- 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
- 指针的运算。
2.字符指针(char*)
int main()
{
char ch = 'w';
char*pc = &ch;//pc的类型就是char*,存放的是ch的地址
*pc = 'w';//对地址进行解引用就能得到该地址存储的内容,即('w')
return 0;
}
这是一般的写法,那么我们看一下另一段代码并试着理解一下是什么意思。
int main()
{
const char*p = "hello world";
printf("%s\n",p);
return 0;
}
打印出来的结果如下:
上述代码的意思是:把一个常量字符串的首字符 h 的地址存放到指针变量 p 中。打印字符串的时候从首字符开始,读取到’\0’,停止打印。
我们结合之前的一道面试题再来深刻理解一下:
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;
}
结果是:
为什么会得到这样的结果呢?
C/C++会把常量字符串存储到单独的一个内存区域。
用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。而当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。
3.指针数组
定义:指针数组是一个存放指针的数组。
int*arr1[10];//整形指针数组
//arr1先与[]结合,表明这是一个数组,10代表这个数组有十个元素,每个元素的类型是int*
char*arr2[4];//一级字符指针数组
char**arr3[4];//二级字符指针数组
4.数组指针
1.定义:数组指针是一个指针,指向的是数组的地址。
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int(*p)[10] = &arr;//p就是一个数组指针
//p先与*结合,表示p是一个指针,指向的是有十个元素的整型数组
//把数组arr的地址赋值给数组指针变量p
//但是一般很少这样写代码
return 0;
}
2.数组指针的使用
void print_arr1(int*p,int sz)
{
int i = 0;
for(i = 0;i < 10;i++)
{
printf("%d ",*(p + i));
}
}
int main()
{
int arr1[10] = {1,2,3,4,5,6,7,8,9,10};
int*p = arr1;//数组名代表的是数组首元素的地址
int sz = sizeof(arr1)/sizeof(arr1[10]);
print_arr1(arr,sz);
return 0;
}
5.数组参数
5.1一维数组传参
void test(int arr[])//1
{}
void test(int arr[10])//2
{}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
test(arr);
return 0;
}
5.2二维数组传参
void test(int arr[3][5])//1
{}
void test(int arr[][])//err
{}
void test(int arr[][5])//2
{}
int main()
{
int arr[3][5] = {0};
test(arr);//arr代表数组首元素的地址。当数组为二维数组时,首元素的地址代表的是第一行的地址。
return 0;
}
5.3一级指针传参
void test(int*p)
{}
int main()
{
//int arr[10] = {1,2,3,4,5,6,7,8,9,10};
//int*p = arr;
//test(p);//1
//test(arr);//2
int a = 0;
int*p = &a;
test(p);//1
test(&a)//2
return 0;
}
5.4二级指针传参
void test(int**pp)
{}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = arr;
int **pp = &p;
test(pp);//1
test(&p);//2
return 0;
}
6函数指针
6.1定义:指向函数的指针
6.2
我们先写一段目前学过的Add函数
int Add(int x,int y)
{
return x + y;
}
int main()
{
int ret = Add(2,3);
printf("%d\n",ret);
return 0;
}
int(*pf)(int,int) = Add;//就是函数指针变量
//pf先与*结合,表明pf是一个指针,然后与()结合表示是一个函数,然后()内写上函数参数的类型(参数都是int),然后最前面是函数的返回值类型(int)。
int Add(int x,int y)
{
return x + y;
}
int main()
{
int(*pf)(int,int) = Add;
int ret = pf(2,3);
printf("%d\n",ret);
return 0;
}
6.3阅读两段有趣的代码加深理解
//代码1
(*(void(*)())0)();
//void(*p)() p是一个函数指针。函数无参,返回值是int
//所以 void(*)() 是一个函数指针类型
//( void(*)() ) (类型)表示强制转化为括号中的类型
// ( void(*)() ) 0 表示将0原本的int类型强制类型转化为一个函数指针类型,这就意味着0的地址存放一个返回类型是void,无参的一个函数
// *( void(*)() ) 0 对0进行解引用,得到函数
// (*( void(*)() ) 0)() 调用0地址处的这个函数(函数无参)
void (* signal(int, void(*)(int)) )(int);
//signal是一个函数的声明
//signal函数的参数,第一个是int类型的,第二个是void(*)(int)的函数指针类型
//signal函数的返回值类型也是:void(*)(int)的函数指针
//void(*)(int) signal(int, void(*)(int));//err
//这样写我们容易理解,但是这样的写法是不对的
//简化代码
typedef void(*pfun_t)(int);//给函数指针类型void(*)(int)重新起名叫:pf_t
pfun_t signal(int, pfun_t);
7.函数指针数组
int (*parr1[10])();
//parr1 先和 [] 结合,说明 parr1是数组,数组的内容是 int (*)() 类型的函数指针。9
int (*pf1)(int, int) = Add;
int (*pf2)(int, int) = Sub;
int (*pf3)(int, int) = Mul;
int (*pf4)(int, int) = Div;
7.1函数指针的使用
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(*pf[4])(int,int) = {Add,Sub,Mul,Div};
int i = 0;
for(i = 0;i < 4;i++)
{
int ret = pf[i](8,2);
printf("%d\n",ret);
}
return 0;
}
函数指针数组的用途:转移表
例子:(计算器)
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
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;
}
这个代码一看就很冗长,同样的代码重复出现。这时我们就可以用函数指针数组对其进行优化。
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void menu()
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
int(*pf[])(int x,int y) = {0,Add,Sub,Mul,Div};
do
{
menu();
printf("请选择:>");
scanf("%d",&input);
if(input == 0)
{
printf("退出计算器\n");
}
else if(input >= 1 && input <= 4 )
{
printf("请输入两个操作数:>");
scanf("%d %d",&X,&y);
ret = (*pf[4])(int,int);
printf("ret = %d\n",ret);
}
else
{
printf("选择错误\n");
}
}while(input);
return 0;
}