C语言指针的学习总结

指针是什么?

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
画图来加深理解
这里写图片描述
展示代码:

#include<stdio.h>
int main()
{
    int a = 10;//在内存中开辟一块空间
    int *p = &a;//将a的地址存放在p变量中,p就是一个指针变量
    return 0;
}

所以,简单地说,指针就是变量,用来存放地址的变量(存放在指针中的值都被当成地址处理)。

总结:
  • 指针是存放地址才出现的,地址是为了标识一块地址空间的。
  • 指针让地址有地方存放,指针让内存的访问更加方便。
  • 指针的大小在32位平台上是4个字节,在64位平台上是8个字节。

指针类型

char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;

可以看出指针是有类型的,类型是:type + *的方式。
char *类型的指针是为了存放char类型变量的地址。
short *类型的指针是为了存放short类型变量的地址。
int *类型的指针是为了存放int类型变量的地址。

以此类推。
代码演示

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int n = 0;
    char *pc = (char*)&n;
    int *pi = &n;
    printf("%p\n", &n);
    printf("%p\n", pc);
    printf("%p\n", pc+1);
    printf("%p\n", pi);
    printf("%p\n", pi+1);
    system("pause");
    return 0;
}

这里写图片描述
可以看到char*类型的地址相差1,而int*类型的地址相差4。

总结:

指针的类型决定了指针向前或者向后走一步有多大(距离)。同时也决定了对指针解引用的时候有多大的权限(能操作几个字节)。如char*的指针解引用就只能访问一个字节,而int*的指针解引用就能访问四个字节。

二级指针

指针变量也是变量,是变量就有地址,那么指针变量的地址存放在哪里呢?
这就是二级指针。画图来加深理解。
这里写图片描述
二级指针的解引用

  • *ppa通过对ppa中的地址进行解引用,这样找到的是pa,*ppa其实访问的就是pa。
  • **ppa先通过*ppa找到pa,然后对pa进行解引用操作:*pa,那找到的是a

指针数组

指针数组是数组,是一个存放指针的数组。
看代码:

int main()
{
    char *arr[4] = { "hello", "world", "hehe", "haha" };
    //arr就是一个指针数组,它有四个元素,每个元素是一个char *类型的指针,这些指针存放着其对应字符串的首地址。
    system("pause");
    return 0;
}

这里写图片描述
可以看到数组中的每个元素都是对应字符串的首元素地址,是chr*类型的指针,指向对应的字符串。

数组指针

数组指针是指向数组地址的指针,其本质为指针。

int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整形的数组。
//所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*的,所以必须加上()来保证p先和*结合。

应用

void print(int(*p)[5], int x, int y)
//int(*p)[5]是数组指针,二维数组传参传的是第一行的地址,第一行也就相当于一个一维数组
//所以p是数组指针,指向一个大小为5个整形的数组
{
    int i = 0;
    int j = 0;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 5; j++)
        {
            printf("%d ", *(*(p + i) + j));
        }
        printf("\n");
    }
}
int main()
{
    int arr[3][5] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 6, 7, 8, 9, 0 };
    print(arr, 3, 5);//利用数组指针打印二维数组
    system("pause");
    return 0;
}

这里写图片描述
了解了【数组指针】和【指针数组】,我们看看下面的代码:

int arr[5];//这是定义了一个一维数组,有五个元素,每个元素是整形
int *parr1[10];//这是一个指针数组,有十个元素,每个元素的类型是int*
int (*parr2)[10];//这是一个数组指针,指向大小为10个整形的数组
int (*parr3[10])[5];//这是一个数组指针,parr3是指针数组,含有10个元素
//每个元素是一个数组指针,该指针指向一个数组,该数组有五个元素,每个元素的类型是整形。

函数指针

首先看一段代码:

void test()
{
    printf("hello\n");
}
int main()
{
    printf("%p\n", test);
    printf("%p\n",&test);
    system("pause");
    return 0;
}

这里写图片描述
输出的是两个地址,这两个地址是test函数的地址。
函数指针就是用来存放函数的地址。
举个例子:

int Add(int x, int y)
{
    return x + y;
}
int main()
{
    int(*p)(int, int) = Add;
    printf("%d\n", (*p)(2, 3));
    system("pause");
    return 0;
}

这里写图片描述
int (*p)(int,int)是函数指针,指向Add函数。
看两段有趣的代码:

//代码1
(*(void(*)())0)();//这是什么意思呢?

分析如下:
第一步:void(*)(),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
第二步:(void(*)())0,这是将0强制转换为函数指针类型,0是一个地址,也就是说一个函数存在首地址为0的一块区域内。
第三步:(*(void(*)())0),这是取0地址开始的一段内存里面的内容,其内容就是保存在首地址为0的一段区域内的函数。
第四步:(*(void(*)())0)(),这是函数调用。
最后加了一个分号,就变成了原来的样子,所以最终的结果是,这是一条函数调用语句。

//代码2
void (*signal(int,void(*)(int)))(int);

分析如下:
第一步:signal(int,void(*)(int)),可以看出signal是个函数,它有两个参数,一个是整形int,一个是函数指针类型void(*)(int),该函数指针指向的函数参数为int,返回值类型为void
第二步:signal函数的返回类型为一个函数指针,该指针指向参数为int,返回值类型void
所以最终结果是,这是signal的一次函数声明。
下面用typedef进行简化:

   typedef void(*pfun_t)(int);
   pfun_t signal(int,pfun_t);

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

函数指针数组
把函数的地址存到一个数组中,这个数组就叫做函数指针数组。那如何定义呢?

int (*parr1[10])();
int *parr2[10]();
int (*)()parr3[10];

正确定义是parr1
parr1先和[]结合,说明parr1是数组,数组内容是int(*)()类型的函数指针。
举例子(计算器):用函数指针数组实现(转移表)

#include<stdio.h>
#include<stdlib.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 chu(int a, int b)
{
    return a / b;
}
void menu()
{
    printf("####################################\n");
    printf("##      1.add       2.sub         ##\n");
    printf("##      3.mul       4.chu         ##\n");
    printf("##            0.exit              ##\n");
    printf("####################################\n");
}
int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;
    int(*p[5])(int, int) = { 0, add, sub, mul, chu };//转移表
    do
    {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        if (input > 0 && input <= 4)
        {
            printf("请输入两个数:");
            scanf("%d%d", &x, &y);
            ret = p[input](x, y);
            printf("%d\n", ret);
        }
        else if (input == 0)
        {
            printf("退出程序\n");
            break;
        }
        else
        {
            printf("输入错误\n");
        }
    } while (input);
    system("pause");
    return 0;
}

int(*p[5])(int, int)是函数指针数组,存放函数的地址,通过地址找到函数并调用。这比用switch语句写简洁的多。
运行效果
这里写图片描述
这里写图片描述
指向函数指针数组的指针
指向函数指针数组的指针是一个指针
指针指向一个数组,数组的元素都是函数指针
定义一个指向函数指针数组的指针:

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;
}

不足之处,还请多多指教!

猜你喜欢

转载自blog.csdn.net/dongying_/article/details/81292790