一、字符指针变量
对于字符和字符串,C语言统一使用char类型来表达式。对此字符指针变量有两种表示方法:
- 第一种:
char ch='a'; char *p=&ch;
- 第二种:
const char *pstr="Hellow world";
问题:关于const char *pstr="Hellow world"
是将整个字符串放到字符指针里面了吗?
解释:本质是字符串"Hellow world"
首字符(H)的地址放到字符指针变量pstr
中.
【面试题:剑指offer】:
int main() { char str1[] = "hellow world"; char str2[] = "hellow world"; const char *str3 = "hellow world"; const char *str4 = "hellow world"; 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; } 结果: str1 and str2 are not same str3 and str4 are same
【解释】:
str3
和str4
指向的是一个同一个常量字符串。C/C++会把常量字符串储存到单独的一个内存区域(常量区),当多个指针指向同一个字符串时,实际是指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时就会开辟不同的内存块,更直白说就算将常量字符串从常量区拷贝一份到数组中,那么在数组初始化时就会在栈中开辟不同的内存块。
使用指针初始化的"hello world"
是一个常量字符串,str3
指向首字符地址,但该地址在于进程的text段,text段可以保存常量还可以执行代码,是不允许可写权限,只有可读、可执行权限(了解即可)
char *p="hellow"; p[1]='d';//*(p+1)='d';//出现段错误
二、数组指针变量(指针数组,这里需要注意偏正)
- 整形指针变量:
int *p
(存放的是整形变量的地址,能够指向整形数据的指针) - 字符指针变量:
char *p
(存放的是字符或字符串首元素的地址,能够指向字符或者字符串数据的指针)
依次类推
数组指针变量:存放的是数组的地址,能够指向数组的指针变量
那个是数组指针变量?那个是指针数组变量? int *p1[10]; int (*p2)[10];
注:[]的优先级高于*。
- p1和[]结合形成一个数组,指向数组中int *类型的元素,是指针数组。
- p2和 *加上了()保证了p2先和 *结合形成一个指针,指向一个大小为10个整形的数组(指向一个多大的数组),是数组指针(存放的是数组的地址)
2.1 数组指针的初始化
int (*p) [10] = &arr; | | | | | | | | []:p指向数组的元素个数 | p:数组指针变量名 int:p指向的数组的元素类型
2.2 二维数组传参的本质
二维数组是由多个一维数组组成的,也是连续存放数据。这样说明:二维数组的每个元素都是一个一维数组,根据数组名是数组首元素的地址这个规则,二维数组的数组名表示是第一行的地址(一维数组的地址)
对此第一行的地址的类型是数组指针类型int(*)[5]。意味着跟一维数组传参本质一样,也是传递地址,传递的是第一行这个一维数组的地址
小总结:二维数组传参,形参的部分可以采用数组或者指针形式
void test1(int nums[][5]);//数组类型 void test2(int (*nums)[5]);//指针类型 int main() { int nums[][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}}; test1(nums); test2(nums); return 0; }
三、函数指针变量
函数指针变量:存放函数地址的,通过函数的地址来调用函数
问题:那么如何得到函数的地址呢?
void test(); int main() { printf("&test==%p",&test()); printf("test==%p",test()); return 0; } 结果: &test==00991253 test==00991253
通过输出的结果:函数是有地址的,函数名就是函数的地址,&函数名的方式也可以获得函数的地址
void (*pf1)() = &test; void (*pf2)() = test;
3.1 函数指针变量初始化
int (*pf3) (int x, int y) | | ------------ | | | | | pf3指向函数的参数类型和个数的交代 | 函数指针变量名 pf3指向函数的返回类型 int (*) (int x, int y) //pf3函数指针变量的类型
函数指针变量的使用(通过函数指针调用指针指向的函数)
int Add(int x,int y) { return x+y; } int main() { int (*p)(int x,int y)=Add;//如果需要存放多个函数地址,需要使用到函数指针数组 printf("%d\n",(*p)(a,b)); printf("%d\n",p(a,b)); return 0; }
说明:p(a,b)
跟(*p)(a,b)
效果是相同的,因为函数名本身就是函数指针变量
四、函数指针数组
如果我们需要把多个函数的地址存在数组中,函数名是一个函数指针变量,就需要利用到指针数组和函数配合了
存放函数的地址的指针数组
int (*)()(类型的函数指针): void (*p[3])(void x,void y)={Add,Sub}; 方便理解: p[] = x; void (*x)(void x,void y)={Add,Sub};
说明:p先和[]结合,说明p是数组,数组的内容void (*)()类型的函数指针。
五、转移表
函数指针数组的用途:转移表
比如:使用函数指针数组实现计算器
实现函数功能
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, y = 0;//操作数 int input = 0;//选择功能 //前面加个0,方便输入数字得到相对函数功能 int (*computer[5])(int x,int y) = {0,add,sub,mul,div }; do { mune(); scanf("%d", &input); if (0 < input && input <= 4) { printf("输入两个操作数\n"); scanf("%d %d", &x, &y); int ret = (*computer[input])(x, y); printf("结果为%d\n", ret); } else if (input == 0) { printf("退出计算机\n"); } else { printf("非法输入\n"); } } while (input); return 0; }
说明:根据函数指针数组的结构,我们可以通过两个部分,选择数组中函数,并且为调用函数选择函数参数。
缺点:这么好用的东西也有他自己的美中不足,不知道你有没有发现,这些函数的功能的参数部分,返回值类型是一样的,如果需要使用函数指针算组,建议需要实现的函数参数部分和返回值类型保持一致。
六、typedef 关键字
typedef:用于类型重命名的(将复杂的类型进行简单化)(typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换)
语法:typedef 类型(自定义取名)
例子:
typedef int it; typedef int * intt; 对于数组指针和函数指针有一些区别 typedef int(*part)[5];//数组指针类型int (*)[5] typedef int (*part)(int;)//函数指针类型 int (*)(int);
关于typedef
相关的知识点,这里只是简单介绍。比如:在使用typedef遇到const对象就可能会出现问题
如果大家对typedef这个知识点感兴趣,可以参考这份资料:typedf
这里就简单分享一个typedef遇到const的陷阱
typedef char* Ptr int compare(const Ptr e1 , const Rtr e2)
说明:typedef它不是简单的字符串替换,先分析const Ptr
意味着Ptr整体被const
赋予了常量性,那么typedef
以后应该是char * const e1
保证Ptr
指针本身的常量性、不能修改指针本身。
以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二C语言笔记,希望对你在学习C语言中有所帮助!