C语言学习记录——深入理解指针(4)

     OK,这一篇主要是讲我学习的3种指针类型。

正文开始:

一.字符指针

    所谓字符指针,顾名思义就是指向字符的指针。一般写作  " char* "

直接来说说它的使用方法吧:

(1)一般使用情况:

int main()
{
	char a = 'A';
	char* p = &a;//取出a的地址
	printf("%c\n", *p);

	return 0;
}

打印结果:

(2)特殊使用情况:

int main()
{
	char s[] = "hello world";
	const char* p = s;
	printf("%s\n", p);

	return 0;
}

先看看运行情况:

这里是创建了一个字符数组写的字符串,然后用p接收了字符串首字符的地址。  注意喔,这里不是p接收了整个字符串的地址,这里打印出整个字符串容易让人误会是p接收了整个字符串的地址,实际上是因为打印字符串有首元素的地址就可以全部打印了。

验证一下:

这里就可以看出,这里p和字符串首元素的地址是一模一样的

好的,字符指针结束。
 

二.数组指针

   我之前还学习过指针数组,刚开始经常把它与数组指针搞混淆,这两者是完全不一样的,指针数组是存放指针的数组,而数组指针是指向数组的指针。也就是说,数组指针是指针变量

我们已经熟悉:

• 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。

• 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。

那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。

   

答案是第二个,因为在第一个语句中  []  的优先级比   *   更高 ,会优先与p1结合,所以必须用()像(*p2)这样括在一起。那么p2先和*结合,说明p2是⼀个指针变量,然后指针指向的是⼀个⼤⼩为10个整型的数组。所以p2是 ⼀个指针,指向⼀个数组,叫 数组指针。

好的,接下来介绍一下数组指针的用法。

(1)数组指针的初始化:

int main()
{
    int arr[10] = { 0 };
    &arr;//取出arr的地址。
    int (*p)[10] = &arr;
    return 0;
}

这里就取出了整个数组的地址(&arr)保存到数组指针 p 中

我们调出监视查看:

可以发现arr和p的类型是完全一样的。

那我们就可以得到下面的结论:

(2)二维数组传参的本质

   知道了数组指针的概念,就可以讲一下数组传参的本质了。

我之前写二维数组传参代码是这样写的:

void func(int arr[3][5], int r, int c)
{
    int i = 0;
    for (i = 0;i < r;i++)
    {
        int j = 0;
        for (j = 0;j < c;j++)
        {
            printf("%d ", arr[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 };
    func(arr, 3, 5);
    return 0;
}

可以看到形参接收的时候也是使用(arr[3][5])这样的二维数组的形式来接受的,那当我学习了数组名是数组首元素的地址和数组指针后我知道了一种新的写法。

⾸先我们再次理解⼀下⼆维数组,⼆维数组其实可以看做是每个元素是⼀维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组

如下图:

所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀ 维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类 型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀ ⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:

void func(int(*p)[5], int r, int c)
{
    int i = 0;
    for (i = 0;i < r;i++)
    {
        int j = 0;
        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 };
    func(arr, 3, 5);
    return 0;
}

形参就用数组指针来接收,打印也用解引用地址来打印,

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

这一行可能不太好理解,本人也没那个能耐就用文字说清楚,不懂的朋友可以用AI去解释,只要熟悉指针了,那还是很好理解的。

好的,数组指针就到这里。

三.函数指针

   接下来就是最后的函数指针了。

那么什么是函数指针变量呢? 根据前⾯学习整型指针,数组指针的时候,我们的类⽐关系,我们不难得出结论: 函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。 那么函数是否有地址呢?

很简单,做个测试就知道了。

测试出来函数果然也是有地址的,并且使用(函数名)和使用(&函数名)打印出来的地址是一样的,这和数组有点像,但不同的是:  数组名!= &数组名,但是 函数名==&函数名,也就是说,函数比数组还好记一点。

那么初始化函数指针的方式也就类比着数组的来:

int Add(int x, int y)
{
    return x + y;
}
int main()
{

int(*pf1)(int, int) = Add;
int(*pf2)(int x, int y) = &Add;//x和y写上或者省略都是可以的
    return 0;
}

这就把函数(Add)的地址保存到指针变量(pf)中了。

接下来是使用函数指针变量,毕竟初始化了肯定要使用一下的嘛。

int Add(int x, int y)
{
    return x + y;
}
int main()
{
    int(*pf3)(int, int) = Add;
    printf("%d\n", (*pf3)(2, 3));//使用函数指针来调用函数
    printf("%d\n", pf3(3, 5));//(*pf3)==pf3,不需要使用*符号
    return 0;
}

注意看我写在代码中的注释,运行结果如下:

这下函数指针的调用也学会了。

四.最后

不知不觉写了两个多小时了,学习的时间过得很快,希望现在和未来学的技术都能够让我在以后获得收获吧。

应该……会的吧,管他呢,反正有问题就解决,人生不就是被问题堆起来的嘛,不出现问题人生反而会有些索然无味吧。

说多了也说偏了,总之谢谢大家的阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值