【C语言】指针篇-一篇搞定不同类型指针变量-必读指南(3/5)

简介: 【C语言】指针篇-一篇搞定不同类型指针变量-必读指南(3/5)

一、字符指针变量

对于字符和字符串,C语言统一使用char类型来表达式。对此字符指针变量有两种表示方法:

  1. 第一种: char ch='a'; char *p=&ch;
  2. 第二种: 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

解释】:

str3str4指向的是一个同一个常量字符串。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语言中有所帮助!

相关文章
|
1月前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
86 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
24天前
|
存储 编译器 C语言
【C语言】C语言的变量和声明系统性讲解
在C语言中,声明和定义是两个关键概念,分别用于告知编译器变量或函数的存在(声明)和实际创建及分配内存(定义)。声明可以多次出现,而定义只能有一次。声明通常位于头文件中,定义则在源文件中。通过合理组织头文件和源文件,可以提高代码的模块化和可维护性。示例包括全局变量、局部变量、函数、结构体、联合体、数组、字符串、枚举和指针的声明与定义。
47 12
|
1月前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
56 9
|
1月前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
45 7
|
1月前
|
C语言
【C语言】全局搜索变量却找不到定义?原来是因为宏!
使用条件编译和 `extern` 来管理全局变量的定义和声明是一种有效的技术,但应谨慎使用。在可能的情况下,应该优先考虑使用局部变量、函数参数和返回值、静态变量或者更高级的封装技术(如结构体和类)来减少全局变量的使用。
36 5
|
2月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
2月前
|
存储 算法 C语言
用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容
本文探讨了用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容,旨在为开发者提供全面的指导和灵感。
52 2
|
2月前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
44 1
|
存储 C语言
C语言的类型提升
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。 作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net  每天都会看CU的博客,尤其是CU首页上面的博客。
926 0
|
1月前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
66 10