热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

指针及数组深度解析

指针1.指针是什么?  在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points

指针


1.指针是什么?
  在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为”指针”。意思是通过它能找到以它为地址的内存单元。
  如图所示:

内存
对应代码:

#include
int main()
{int a = 10; //在内存中开辟一块空间int *p = &a; //取出变量a的地址并将其存放在p变量中,p就是一个指针变量return0;
}

2.指针内存布局:

int *p;

内存布局

3.指针表达式:

char ch = 'a';
char *cp = &ch;

  初始化如下:
初始化
  图中还显示了ch后面的那个内存位置,因为我们所求值得有些表达式将访问它(尽管是在错误情况下才会对它进行访问)。由于我们并不知道它的初始值,所以用一个问号来替代。
  上面代码牵扯到左值和右值问题,那什么时候做左值,什么时候做右值呢?
  同一变量做左值和做右值有所不同:
  ①做左值,变量名对应的是存储空间
  ②做右值,变量名表示该变量对应的内容
原生类型既可以做左值又可做右值。
下面有几个例子在上面代码基础上具体分析左值右值问题:

&ch; //地址变量,不可做左值,但可做右值
cp; //是变量,可做左值,也可做右值
&cp; //地址变量,不可做左值,但可做右值
*cp+1; //表达式结果是常量,不可做左值,但可做右值
*(cp+1); //表示ch之后的一块空间,可做左值,也可做右值
++cp; //前置++,不可做左值,可做右值
cp++; //后置++,不可做左值,可做右值
*++cp; //ch的下一块空间,可做左值,也可做右值
*cp++; //可做左值,也可做右值
++*cp; //*cp的前置++,不可做左值,可做右值
(*cp)++; //*cp的后置++,不可做左值,可做右值
++*++cp; //对ch的下一块空间的内容前置++,不可做左值,可做右值
++*cp++ //对ch的的内容前置++,同时对cp后置++,不可做左值,可做右值

数组


1.数组是什么?
  所谓数组,是无序的元素序列。 若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式。 这些无序排列的同类数据元素的集合称为数组。

2.数组内存布局:

int a[5];

内存分配
  如上图所示,当我们定义一个数组a时,编译器根据指定的元素个数和元素类型分配确定大小(元素类型大小*元素个数)的一块内存,并将这块内存的名字命名为a。名字a一旦与这块内存匹配就不能改变。a[0],a[1]为这块内存中的元素,但并非元素的名字。数组的每一个元素都是没有名字的。

指针数组


  首先,我们要明白数组就是数组,指针就是指针,它们之间没有关系。
  在C语言和C++语言中,数组元素全为指针的数组称为指针数组。一维指针数组的定义形式为:”类型名 *数组标识符[数组长度]”。
例如,一个一维指针数组的定义:

int *p1[10];

指针数组

数组指针


  我们已经熟悉:
  整型指针:int *p;能够指向整型数据的指针。
  浮点型指针:float *p;能够指向浮点型型数据的指针。
  字符型指针:char *p;能够指向字符型型数据的指针。
  。。。
  数组指针,指的是数组名的指针,即数组首元素地址的指针。即是指向数组的指针。例:

int (*p2)[10];

p即为指向数组的指针,又称数组指针。

数组指针

指针与数组的定义、声明


  指针与数组的定义与声明存在两大误区:
1.定义为数组,声明为指针

//文件1中定义
char a[]="abcdefg";
//文件2中声明
extern char *a;

  定义数组内存分配图:

定义

  声明为指针内存分配图:

声明

  由此可见,这样做是错误的。

2.定义为指针,声明为数组

//文件1中定义
char *p="abcdefg";
//文件2中声明
extern char p[];

  声明为数组时内存分配图:

声明

  由此可见,这样做也是错误的。

指针与数组对比


对比
对比

指针传参:(形成临时变量,4字节)

  凡是在函数内部使用参数、指针,是想在函数外部使用,即在函数内部操纵函数之外的一些数据。

数组传参:

  1.发生降维,降维成指针
  2.将数组看成线性一维数组
  3.数组传参降维成指向其内部元素的指针

函数指针


  函数指针是指向函数的指针变量。 因而”函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。函数指针有两个用途:调用函数和做函数的参数。
通过下面一个例子,基本可以理解函数指针的使用:

(*(void (*)())0)();

  分析如下:
  第一步:void(*)(),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
  第二步:(void (*)())0,这是将0强制转换为函数指针类型,0是一个地址,也就是说一个函数存在首地址为0的一段区域内。
  第三步:(* (void ( * )())0),这是取0地址开始的一段内存里的内容,其内容就是保存在首地址为0的一段区域内的函数。
  第三步:(* (void ( * )())0)(),这是函数的调用。

函数指针数组、函数指针数组的指针


1.函数指针数组:
  函数指针数组是一个其元素是函数指针的数组。那么也就是说,此数据结构是是一个数组,且其元素是一个指向函数入口地址的指针。
  根据分析:首先说明是一个数组:数组名[]
  其次,要说明其元素的数据类型指针:*数组名[]
  再 次,要明确这每一个数组元素是指向函数入口地址的指针:函数返回值类型 (* 数组名[])()。请注意,这里为什么要把“* 数组名[]”用括号扩起来呢?因为圆括号和数组说明符的优先级是等同的,如果不用圆括号把指针数组说明 表达式扩起来,根据圆括号和方括号的结合方向,那么 *数组名 说明的是什么呢?是元素返回值类型为指针的函数数组。有这样的函数数祖吗?不知道。所以必须括起来,以保证数组的每一个元素是指针。
函数指针数组的实现:

//利用函数指针数组实现简易计算器#include
#include 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) //除
{if (y == 0){printf("除数为零,出现错误!\n");}else{return x / y;}
}
int main()
{int x, y;int input &#61; 1;int ret &#61; 0;int(*p[5])(int x, int y) &#61; { 0, Add, Sub, Mul, Div }; //转移表const char *op &#61; " &#43;-*/";while (input){printf("*****************************************\n");printf("********1.Add***************2.Sub********\n");printf("********3.Mul***************4.Div********\n");printf("****************************0.quit*******\n");printf("*****************************************\n");printf("请选择&#xff1a;");scanf("%d", &input);if (input >&#61; 1 && input <&#61; 4){printf("请输入操作数&#xff1a;");scanf("%d %d", &x, &y);ret &#61; (*p[input])(x, y);}else if (input &#61;&#61; 0){printf("quit!\n");break;}else{printf("输入参数错误&#xff01;\n");continue;}printf("result:%d %c %d &#61; %d\n", x, op[input], y, ret);}system("pause");return 0;
}

2.函数指针数组的指针&#xff1a;
  指向函数指针数组的指针是一个指针
  指针指向一个数组&#xff0c;数组的元素都是函数指针

#include void test(const char* str)
{printf("%s\n", str);
}
int main()
{//函数指针pfunvoid (*pfun)(const char*) &#61; test;//函数指针数组pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] &#61; test;//指向函数指针数组pfunArr的指针ppfunArrvoid(*(*ppfunArr)[10])(const char*) &#61; &pfunArr;system("pause");return 0;
}

  以上是我对数组及指针的一些理解&#xff0c;相关例题深度解析请看下篇&#xff01;

  未完待续。。。


推荐阅读
  • 在编程笔试和面试中,全排列算法因其适中的难度而备受青睐,不仅能够考察应聘者的算法基础,还能测试其对递归和回溯的理解。本文将深入解析全排列算法的实现原理,探讨其应用场景,并提供优化建议,帮助读者更好地掌握这一重要算法。 ... [详细]
  • 深入解析C语言中的动态规划算法:以背包问题为例
    本文深入探讨了C语言中动态规划算法的应用,以经典的背包问题为例进行详细解析。通过实例分析,展示了如何利用动态规划解决复杂优化问题,并提供了高效的代码实现方法。文章不仅涵盖了算法的基本原理,还讨论了其在实际编程中的应用技巧和优化策略,为读者提供了全面的理解和实践指导。 ... [详细]
  • 单链表的高效遍历及性能优化策略
    本文探讨了单链表的高效遍历方法及其性能优化策略。在单链表的数据结构中,插入操作的时间复杂度为O(n),而遍历操作的时间复杂度为O(n^2)。通过在 `LinkList.h` 和 `main.cpp` 文件中对单链表进行封装,我们实现了创建和销毁功能的优化,提高了单链表的使用效率。此外,文章还介绍了几种常见的优化技术,如缓存节点指针和批量处理,以进一步提升遍历性能。 ... [详细]
  • 2012年9月12日优酷土豆校园招聘笔试题目解析与备考指南
    2012年9月12日,优酷土豆校园招聘笔试题目解析与备考指南。在选择题部分,有一道题目涉及中国人的血型分布情况,具体为A型30%、B型20%、O型40%、AB型10%。若需确保在随机选取的样本中,至少有一人为B型血的概率不低于90%,则需要选取的最少人数是多少?该问题不仅考察了概率统计的基本知识,还要求考生具备一定的逻辑推理能力。 ... [详细]
  • 题目链接: Caninepoetry问题概述:给定一个仅包含小写字母的字符串,允许将任意位置的字符修改为任意其他小写字母。目标是通过最少次数的修改,使字符串中所有长度大于1的子串均满足特定条件。本文详细分析了该问题,并运用思维与贪心算法,提出了一种高效解决方案。通过对字符串的深入解析,我们探讨了如何在最小化修改次数的同时,确保所有子串符合要求。 ... [详细]
  • 本文深入解析了Java面向对象编程的核心概念及其应用,重点探讨了面向对象的三大特性:封装、继承和多态。封装确保了数据的安全性和代码的可维护性;继承支持代码的重用和扩展;多态则增强了程序的灵活性和可扩展性。通过具体示例,文章详细阐述了这些特性在实际开发中的应用和优势。 ... [详细]
  • 在洛谷 P1344 的坏牛奶追踪问题中,第一问要求计算最小割,而第二问则需要找到割边数量最少的最小割。通过为每条边附加一个单位权值,可以在求解最小割时优先选择边数较少的方案,从而同时解决两个问题。这种策略不仅简化了问题的求解过程,还确保了结果的最优性。 ... [详细]
  • 具备括号和分数功能的高级四则运算计算器
    本研究基于C语言开发了一款支持括号和分数运算的高级四则运算计算器。该计算器通过模拟手算过程,对每个运算符进行优先级标记,并按优先级从高到低依次执行计算。其中,加减运算的优先级最低,为0。此外,该计算器还支持复杂的分数运算,能够处理包含括号的表达式,提高了计算的准确性和灵活性。 ... [详细]
  • 无论是计算机专业学生还是非计算机专业的学习者,在掌握C语言的过程中可能会遇到诸多挑战,不清楚从何入手。为此,本文系统地梳理了2019年福建省C语言的核心知识点,并结合最新的技术进展进行了详细总结,旨在为初学者提供全面的学习指导。文章不仅涵盖了基础语法和数据结构,还深入探讨了指针、内存管理和算法优化等高级主题,帮助读者快速提升编程能力。 ... [详细]
  • 寒假作业解析:第三周 2月12日 第7题
    尽快完成之前的练习任务!每日一练2.1 Problem A Laurenty and Shop 的题目要求是选择两条不同的路线以最小化总的等待时间。简要分析:通过对比不同路线的等待时间,可以找到最优解。此问题可以通过动态规划或贪心算法来解决,具体取决于路线的复杂性和约束条件。 ... [详细]
  • 本文对常见的字符串哈希函数进行了全面分析,涵盖了BKDRHash、APHash、DJBHash、JSHash、RSHash、SDBMHash、PJWHash和ELFHash等多种算法。这些哈希函数在不同的应用场景中表现出各异的性能特点,通过对比其算法原理、计算效率和碰撞概率,为实际应用提供了有价值的参考。 ... [详细]
  • 本文详细介绍了在 Vue.js 前端框架中集成 vue-i18n 插件以实现多语言支持的方法。通过具体的配置步骤和示例代码,帮助开发者快速掌握如何在项目中实现国际化功能,提升用户体验。同时,文章还探讨了常见的多语言切换问题及解决方案,为开发人员提供了实用的参考。 ... [详细]
  • 如何在Lua中调用C语言编译的动态链接库
    本文介绍了如何在Lua中调用C语言编译的动态链接库。通过详细步骤和示例代码,帮助开发者理解和掌握这一技术。参考了《Lua编程入门》一书中的相关内容,并结合实际操作经验,提供了更加清晰和实用的指导。此外,还探讨了在不同操作系统下编译和链接Lua的方法,为跨平台开发提供了有价值的参考。 ... [详细]
  • 掌握PHP编程必备知识与技巧——全面教程在当今的PHP开发中,了解并运用最新的技术和最佳实践至关重要。本教程将详细介绍PHP编程的核心知识与实用技巧。首先,确保你正在使用PHP 5.3或更高版本,最好是最新版本,以充分利用其性能优化和新特性。此外,我们还将探讨代码结构、安全性和性能优化等方面的内容,帮助你成为一名更高效的PHP开发者。 ... [详细]
  • 在C语言中,定义一个包含学号、姓名和年龄的学生信息结构体,并遵循严格的命名规范。首先,初始化结构体变量的所有成员为默认值,然后将其学号设为88,姓名设为“liming”,年龄设为25。最后,在控制台上输出该结构体变量的详细信息,以验证数据的正确性。例如,使用 `typedef struct Student` 定义结构体类型。 ... [详细]
author-avatar
孤独小舟9
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有