指针及数组深度解析

指针

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 <stdio.h>
#include <windows.h>

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 = 1;
    int ret = 0;
    int(*p[5])(int x, int y) = { 0, Add, Sub, Mul, Div };   //转移表
    const char *op = " +-*/";
    while (input)
    {
        printf("*****************************************\n");
        printf("********1.Add***************2.Sub********\n");
        printf("********3.Mul***************4.Div********\n");
        printf("****************************0.quit*******\n");
        printf("*****************************************\n");
        printf("请选择:");
        scanf("%d", &input);
        if (input >= 1 && input <= 4)
        {
            printf("请输入操作数:");
            scanf("%d %d", &x, &y);
            ret = (*p[input])(x, y);
        }
        else if (input == 0)
        {
            printf("quit!\n");
            break;
        }
        else
        {
            printf("输入参数错误!\n");
            continue;
        }
        printf("result:%d %c %d = %d\n", x, op[input], y, ret);
    }
    system("pause");
    return 0;
}

2.函数指针数组的指针:
  指向函数指针数组的指针是一个指针
  指针指向一个数组,数组的元素都是函数指针

#include <stdio.h>

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)[10])(const char*) = &pfunArr;
    system("pause");
    return 0;
}

  以上是我对数组及指针的一些理解,相关例题深度解析请看下篇!

  未完待续。。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值