目录
(注意:字符串常量本来就是常量,只是用const修饰过更加准确)
指针的概念:
1.指针就是变量,用来存放地址,地址唯一标识一块内存空间
2.指针的大小固定4/8个字节(32位平台/64位平台)
3.指针是有类型,指针的类型决定了指针的+-整数的步长,解引用操作的时候的权限。
1.字符指针
在指针的类型中我们知道有一种指针类型为 char*
一般使用:
int main()
{
char ch = 'w';
char *pc = &ch;
return 0;
}
这里的 *pc就是指针变量,具体来说叫做字符指针变量。
还有一种使用方法如下:
int main()
{
const char* pstr = "hello word.";//这里是把一个字符串放到pstr指针变量里了吗?
printf("%s\n", pstr);
return 0;
}
特别注意这里不是吧字符串hello word放到字符指针 pstr 里,而是把字符串首字符的地址放到了pstr中。
一道经典的面试题:
#include <stdio.h>
int main()
{
char str1[] = "hello word.";
char str2[] = "hello word.";
const char* str3 = "hello word.";
const char* str4 = "hello word.";
if (str1 == str2)
printf("str1 and str2 相同\n");
else
printf("str1 and str2 are 不同\n");
if (str3 == str4)
printf("str3 and str4 相同\n");
else
printf("str3 and str4 are 不同\n");
return 0;
}
最终输出结果:
这里str3和str4被const修饰过了,拥有了常属性,是一个常量字符串,C/C++会把常量字符串存储到单独的一个内存区域,当几个指针,指向同一个字符串的时候,他们实际会指向同一块内存,所以它两相同。
str1和str2创建的时候,用的是相同的常量字符串去初始化不同的数组的时候,就会开辟出不同的内存块,也就是不同的空间,这里比的不是字符串里面的内容,而是两块空间首字母的地址,所以它两不同。
(注意:字符串常量本来就是常量,只是用const修饰过更加准确)
2.指针数组
在之前的博客中我们已经学过了指针数组
顾名思义,指针数组就是一个存放指针的数组。
int* arr1[10];
//数组名arr1,里面有10个元素,每个元素是int*,是一个存放整形指针的数组
char *arr2[4];
//数组名arr2,里面有4个元素,每个元素是cahr*,是一个存放字符型形指针的数组
char **arr3[5];
//数组名arr3,里面有5个元素,每个元素是cahr**,是一个存放二级指针的数组
3. 数组指针
3.1 数组指针的定义
数组指针是指针?还是数组?
答案是:指针。
整型指针——存放整型地址的指针——指向整型的指针 int*
字符指针——存放字符地址的指针——指向字符的指针 char*
浮点型指针——存放浮点型地址的指针——指向浮点型的指针 int*
数组指针——存放数组地址的指针——指向数组的指针
int (*p)[10];
解释:p先和*结合,说明p是一个指针变量,然后指针指向的是一个大小为十个整形的数组,所以p是一个指针,指向的是一个数组,叫数组指针。
注意:这里[ ]的优先级高于*号,所以必须加上()来保证p先和*结合。
3.2 &数组名VS数组名
首先我们先来看看&数组名和数组名以地址的形式在屏幕上打印出来的结果。
我们发现他们的结果是一样的,证明他们取出的值都是一样的,那么他们到底有什么不一样的呢?我们现在把它加一看看。
总结: 数组名是首元素的地址
&数组名取出的是整个数组的地址
数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样
3.3 数组指针的使用
使用数组指针打印一个二维数组的内容。
#include<stdio.h>
void print(int(*p)[4], int r, int c)
{
int i = 0;
for (i = 0; i < r; i++)
{
int j = 0;
for (j = 0; j < c; j++)
{
printf("%d",(*(p+i))[j]);
}
printf("\n");
}
}
int main()
{
int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
print(arr, 3, 4);
//数组名是首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr是第一行的的地址,是一维数组的地址
//所以用数组指针来接收
return 0;
}
4. 数组参数、指针参数
在写代码的时候难免会把【数组】或者【指针】传递给函数,那函数的参数该如何设计呢?
4.1 一维数组传参
我们来分析一下下面这段代码。
上面五种传参方式都是正确的,下面我们再来分析一下二维数组传参。
4.2 二维数组传参
总结:二维数组传参,函数形参的设计只能省略第一个[ ]的数字,也就是行可以省略,列不不能省略。
4.3 一级指针传参
总结:假设一个函数的参数是一个一级指针,只要方法正确,我们不仅仅能传一级指针上去,还能传一维数组的数组名和整形变量。
4.4 二级指针传参
总结:假设一个函数的参数是一个二级指针,只要方法正确,我们可以传二级指针变量和一级指针变量名和指针数组的数组名。
5. 函数指针
整型指针——存放整型地址的指针——指向整型的指针 int*
字符指针——存放字符地址的指针——指向字符的指针 char*
浮点型指针——存放浮点型地址的指针——指向浮点型的指针 int*
数组指针——存放数组地址的指针——指向数组的指针
函数指针———存放函数的指针——指向函数的指针
数组指针的写法:
int arr[10];
1.&arr 取出arr的地址;
2.*p=&arr 赋值给一个p,p是一个指针
3.*p[10]=&arr 指针p指向的是一个数组,数组里面有十个元素。
4.int (*p)[10]=&arr; 数组里面有十个元素,每个元素市整形
函数指针的写法:
ADD(int x,int y)
{
}
1.&ADD 取出函数的地址
2.*p=&ADD 赋值给一个p,p是一个指针
3.(*p)(int,int )=&ADD 指针p指向的是一个函数
4.int (*p)(int,int)=&ADD 返回类型是int
想要使用函数指针,直接调用即可。
6. 函数指针数组
数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组, 比如:
int *arr[10];
//数组的每个元素是int
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
是 int (*)() 类型的函数指针。
例子:(计算器)‘
#include<stdio.h>
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 = 0;
int y = 0;
int input = 0;
int ret = 0;
do
{
printf("***********************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*5:退出程序************\n");
printf("***********************\n");
printf("请选择:");
scanf_s("%d", &input);
switch (input)
{
case 1:
printf("请输入操作数;");
scanf_s("%d %d", &x, &y);
ret=add(x, y);
printf("%d\n", ret);
break;
case 2:
printf("请输入操作数;");
scanf_s("%d %d", &x, &y);
ret = sub(x, y);
printf("%d\n", ret);
break;
case 3:
printf("请输入操作数;");
scanf_s("%d %d", &x, &y);
ret = mul(x, y);
printf("%d\n", ret);
break;
case 4:
printf("请输入操作数;");
scanf_s("%d %d", &x, &y);
ret = div(x, y);
printf("%d\n", ret);
break;
case 0:
printf("退出程序");
break;
default:
printf("选择错误");
break;
}
} while (input);
return 0;
}
上述代码中我们发现有很多重复的代码,如果这个时候用上我们刚学的知识,就能简化代码。
#include<stdio.h>
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(*pf[5])(int,int) = { 0,add,sub,mul,div };
//函数指针数组
//转移表
int main()
{
int x = 0;
int y = 0;
int input = 0;
int ret = 0;
do
{
printf("***********************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*5:退出程序************\n");
printf("***********************\n");
printf("请选择:");
scanf_s("%d", &input);
printf("请输入操作数;");
scanf_s("%d %d", &x, &y);
ret = pf[input](x, y);
printf("%d", ret);
} while (input);
return 0;
}
7. 指向函数指针数组的指针
指向函数指针数组的指针是一个 指针
指针指向一个 数组 ,数组的元素都是 函数指针
如何定义?
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)[5])(const char*) = &pfunArr;
return 0;
}