指针的进阶

我们先来复习下指针的概念:

  1. 指针就是地址,不过我们平常说的指针指的是指针变量,指针变量可以存放地址,地址唯一标识一块内存空间

  1. 指针在32位平台上是4个字节的大小,而在64位平台上是8个字节的大小

  1. 指针类型的意义:一是决定指针加减整数的步长大小,二是指针解引用的访问权限

  1. 指针与指针可以进行相减,相减的结果等于指针所指向的两个元素间的距离

接来下我们深入的了解下指针

  1. 字符指针

字符指针一般用来存放字符的地址,但是它还可以在字符串上使用,如下

#include<stdio.h>
int main()
{
char* p="hello world";
return 0;
}

乍一看这不是将字符串放到指针里了,但我们知道指针是用来存地址的,所以这种说法错的。

实际上p里面存的是字符串第一个字符的地址,在上述代码中也就是h的地址

知道了第一个字符串的地址我们就可以访问这个字符串的内容了

访问这里面的内容有两种方式,如下

#include<stdio.h>
#include<string.h>
int main()
{
    char* p = "hello world";
    int i = 0;
    int len = 0;
    len = strlen(p);
    for (i = 0; i < len; i++)
    {
        printf("%c", *(p+i));
        //printf("%c", p[i]);
    }
    return 0;
}

一种是指针方式,另一种是数组方式,其实当我们对指针和数组的理解深入后,就会发现在一些情况下,指针和数组可以相互转换,就如上面这个代码

小提示:字符指针虽然可以指向字符串,但是我们不可以使用这个指针去修改字符串里面的内容,因为该字符串是常量字符串不允许被修改,所以用这个字符指针时可以在前面加上个const,防止字符串被修改

接下来我们来看一道面试题

#include<stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
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和str2两个字符数组分别用同样的内容进行赋值,虽然内容相同,但是终究是两个数组,他们分配到的地址一定是不相同的,就像双胞胎,是两个人,所以str1和str2的地址不同

而str3和str4是两个字符指针指向相同的字符串,这里关键在于我们的系统是否会给出两块空间创建相同的字符串呢?答案是不会的,因为前面说过常量字符串是不允许被修改的,所以字符串一定不变的,那么有什么必要将相同的字符串放到两块空间呢?所以这里str1和str2是指向同一个字符串的,所以str1和str2所存地址相同

  1. 指针数组

字符指针存放字符,整型指针存放整型,类比过来,我们可以得知指针数组存放指针

int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
......

指针数组可以用来模拟二维数组的实现,如下

#include<stdio.h>
int main()
{
    int arr1[] = { 1,2,3,4,5 };
    int arr2[] = { 6,7,8,9,10 };
    int arr3[] = { 11,12,13,14,15 };
    int* p[3] = { arr1,arr2,arr3 };
    {
        int i = 0;
        int j = 0;
        for (i = 0; i < 3; i++)
        {
            for(j=0;j<5;j++)
              printf("%2d ",p[i][j]);
            printf("\n");
        }
        
    }
    return 0;
}

因为我们知道数组名是首元素的地址,int的地址类型就是int*,所有我们可以用整型指针数组接收,然后我们用p[i][j]找到每个元素,这里的原理是,我们先用p[i]找到每个数组的数组名,然后用数组名[j]去找到数组中的每个元素

前面说过在一些情况下,数组和指针可以相互转换,上面这个print也可以写成下面的形式

printf("%2d ", *(*(p + i) + j));

这里给大家个公式p[i]=*(p+i)

从这里也可以看出数组访问操作符的实质不就是指针嘛

  1. 数组指针

3.1数组指针的定义

通过类比的方法我们也容易得出,数组指针就是指向数组的指针,指针里面存的是数组的地址

那么数组指针该怎么写呢?下面展示整形数组指针的写法

int (*p)[10]
//解释:p与*号结合代表p是一个指针,向左右看可以看出指针p指向的类型是一个大小为10个整型的数组
//注意:[]的优先级高于*号,所以必须加上()保证p先与*结合

3.2&数组名与数组名的区别

对于下面这个数组

int arr[10]

arr和&arr的区别是什么呢?

我们来看看下面这段代码和它的运行结果

#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}

我们可以发现arr和&arr的值是一样的,它们+1跳过的大小却不一样

这是因为arr的类型是int*,+1跳过一个整形的大小

而&arr的类型是int(*)[10],+1跳过10*int,也就是整个数组的大小

从这里也可以看出arr和&arr[0]是等价的,也就是我们常说的数组名就是首元素的地址

3.3数组指针的使用

数组指针一般用来接收二维数组的数组名,以此来访问二维数组中的元素,代码如下

#include<stdio.h>
void print(int(*p)[5], int r, int c)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < r; i++)
    {
        for (j = 0; j < c; j++)
        {
            printf("%d ", p[i][j]);
        }
        printf("\n");
    }
}
int main()
{
    int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
    print(arr, 3, 5);
    return 0;
}

用int(*p)[5]接收arr,是因为在二维数组里数组名代表首行数组的地址,也就是这里的arr的类型为int(*)[5],可以用数组指针来接收

  1. 函数指针

来看一段代码和它的结果

#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}

这说明函数也有地址,而且函数名和&函数名是等价的,

既然是地址就可以用指针保存,这里的指针就是函数指针,如下

int add(int x,int y)
{
return x+y;
}
int main()
{
int (*p)(int,int)=&add
return 0;
}

p与*结合表示p代表指针,向左右看可以看出p指向的是函数返回类型为int,函数参数为int和int的函数

  1. 函数指针数组

函数指针数组,就是类型为函数指针的数组,因此我们在函数指针的形式上稍加改造就可以得到函数指针数组,如下

int(*p[10)(int,int)

p先与[10]结合表示p是数组,往外看可以看出p的类型为int(*)(int,int)

函数指针数组可以作为转移表使用,来简化代码,计算机的实现就可以使用函数指针数组,如下

#include<stdio.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)
{
    return x / y;
}
void menu()
{
    printf("****************************************\n");
    printf("**************1.add  2.sub**************\n");
    printf("**************3.mul  4.div**************\n");
    printf("****************************************\n");
    printf("请选择\n");
}
int main()
{
    int input = 1;
    int x = 0;
    int y = 0;
    int ret = 0;
    int(*p[5])(int, int) = { 0,add,sub,mul,div };
    do
    {
        menu();
        scanf("%d", &input);
        if (input <= 4 && input >= 1)
        {
            printf("请输入操作数\n");
            scanf("%d%d", &x, &y);
            ret = (p[input])(x, y);
        }
        else
        {
            printf("输入有误\n");
        }
        printf("ret=%d\n", ret);
    } while (input);
    return 0;
}
  1. 指向函数指针数组的指针

来看一段代码,我们将所学知识融合下

void test(const char *str)
{
printf("%s\n",str);
}
int main()
{
  void(*pfun)(const char *str)=&test;
  //函数指针
  void(*pfunarr[5])(const char *str); 
  pfunarr[0]=test;
  //函数指针数组
  void(*(*ppfunarr[]5))(const char *str)=&pfunarr;
  //指向函数指针数组的指针
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值