C语言指针个人见解

最近有研究C语言的指针,网上资料很多都是针对底层以及部分基础应用,导致有很多不明所以的地方,于是自我研究了一段时间,现分享如下:

1:指针是什么?

C语言是面向过程语言,他不存在像java中的对象,引用类型等等,所以就有一个指针一样的东西,说到底指针和值的关系就是一对键值对,了解过hash和散列表的人应该比较熟悉键值对是什么

K-V,通过键能够检索到具体的值,像在java中的hashmap就是这个原理,而指针原则上的逻辑也是这样,通过一个地址访问到实际的数据,地址可以看做是键,数据是值

2:具体语法

C语言中,指针可以被定义为一个变量,叫指针变量,如:

int *a;

这就是一个指针变量,“*”号为标准格式

获取一个数据的指针则通过“&”符号进行获取,如:

int *a = &b;

这句话就是将b变量的指针赋值给了a指针变量,然后后继操作a指针变量就可以操作b

3:详解(文尾附完整测试代码)

3.1 简单指针

接下来看几个例子详解:

首先简单格式:

int a=10; //实际的数据
    int *b = &a;//指针,且b变量这个地方定义出来时一个指针
    int *c = a;//把数赋值给指针,相当于c指向了一个地址为10的地方,但是那个地方在我们这个程序里没有数据
    int *d ;//
    d = a;//这两句话写法跟c是一样的效果
    printf("========================\n");
    printf("值 a=%d\n",a);//打印值
    printf("========================\n");
    printf("指针变量 b=%p\n",b);//这里打印的就是地址,b是一个指针变量,他不是值
    printf("========================\n");
    printf("通过指针获取值 *b=%d\n",*b);//打印值,因为使用了*号对&a进行了一个寻址,找到了实际的数据
    printf("========================\n");
    printf("值的指针 &a=%p\n",&a);//这里是最开始最原始的指针,&a->a
    printf("========================\n");
    printf("直接把值赋给了指针 d=%p\n",d);//这里因为赋值的是10,所以这个数据打印的指针是另一个地方
    printf("直接把值赋给了指针 c=%p\n",c);//同理
    printf("========================\n");

由例子可以看出,这个地方定义了a=10,那么a变量的值就是10,b为指针变量,并将a变量的指针赋值给了b,c和d也是指针变量,不过写法不一。

打印结果如下:

由打印结果可以看出,直接打印a得到的是值10,而b因为是一个指针变量,所以打印出来的实际上是指针地址

第三步有一个对指针变量再次“*”号,这个是对指针进行了一个寻址操作,也就是通过指针获取到了实际的数据,所以打印为10,

后续c和d,因为是一个指针变量,但是因为在前文赋值的时候是直接将a=10赋值给了指针变量,也就是 “指针变量=值变量”,这个地方,我认为他是将10作为了一个指针地址,所以打印出来的完全不一样,而10这个地址上面并没有任何东西,也就打印不出来数据(有不对的欢迎指正)

自此,简单的指针也就到此结束,也是很好理解的。

3.2 结构体指针

使用结构体编程的时候往往指针都是必备的,下面几个例子详细讲解结构体指针的变化和使用

typedef struct {
    int data;
    struct LinkedNode* next;
}LinkedNode;
void printNodeA(LinkedNode *node) {
    printf("这里是打印方法,数据通过指针传入 data=%d\n",node->data);
}
void printNodeB(LinkedNode   node) {
    printf("这里是打印方法,数据通过结构体传入 data=%d\n",node.data);
}
void updateNodeA(LinkedNode *node) {
    //传入指针变量,修改指针变量指向的数据值
    //这里因为传入的是指针,并没有将实际的数据传入,所以通过指针去进行寻址找到数据,然后修改了数据
    node->data=100;
}
void updateNodeB(LinkedNode node) {
    //传入实际的结构体变量,修改结构体变量
    //这里实际上只是传入了一个node结构体的备份数据,在这个地方的形参和实参实际上不是一个东西
    node.data = 101;
}

首先,我定义了一个结构体,这里是简单定义了一个单向链表的一环

四个方法,分别直接输入值和输入指针

代码如下:

 LinkedNode node;//定义了一个结构体
    LinkedNode *pNode;//定义了一个结构体指针
    node.data = 1;
    node.next = NULL;
    pNode = &node;//把结构体的指针赋给指针变量
    // pNode->next = node;
    printf("========================\n");
    printf("通过结构体直接用.读出数据 node.data=%d\n",node.data);//通过结构体直接用.读出数据
    int *data = &node.data;//定义了一个int指针
    printf("========================\n");
    printf("指针变量 *data = %d\n",data);//打印出来的是指针变量
    printf("========================\n");
    printf("通过指针变量获取的值 **data = %d\n",*data);//将指针变量进行寻址得到实际数据
    printf("========================\n");
    printf("结构体指针 pNode= %d\n",pNode);//打印的是结构体整体
    printf("========================\n");
    printf("结构体 node= %d\n",node);//这个也是打印的结构体整体
    printf("========================\n");
    printf("通过.直接获取结构体里面的数据 node.data= %d\n",node.data);
    printf("========================\n");
    printf("通过->获取结构体指针的寻址后的实际数据 pNode->data= %d\n",pNode->data);//进行了一次寻址操作
    printf("========================\n");

    printNodeA(&node);//传参打印
    printNodeB(node);//传参打印
    updateNodeB(node);//传参修改
    printf("========================\n");
    printf("通过直接传参的方式修改后的node变量 node.data=%d\n",node.data);
    printf("通过直接传参的方式修改后的node变量的指针指向的变量 pNode->data= %d\n",pNode->data);//在其他方法里面修改了后再次打印看效果
    printf("========================\n");
    //通过指针的方式进行修改
    updateNodeA(&node);
    printf("========================\n");
    printf("通过指针传参的方式修改后的node变量 node.data=%d\n",node.data);
    printf("通过指针传参的方式修改后的node变量的指针指向的变量 pNode->data= %d\n",pNode->data);
    printf("========================\n");

由上述例子可以看到,主要是研究"."和“->”的不同

打印结果如下:

1:如果不使用指针,直接用结构体进行读取内部数据的话,就需要使用“.”作为连接符,这样可以直接通过结构体获得里面具体的值

2:如果将结构体内部的数据的指针拿出来赋值给一个指针变量,那么打印出来的也就是指针地址,这个和简单指针逻辑并无区别

3:同2理对指针变量再次"*"就可以得到值

4:如果是将整个结构体的指针赋值给了指针变量,那么就需要用“->”作为连接符,而且获取到的也是直接的值,不是结构体内部的数据的指针

5:传参:

       传参主要有两种方式(据说还有第三种但是我不会)一是直接传值,二是传地址

        这两种有一个很大的区别,在最后几句的日志中也可以看出来,如果是直接传值进入方法体内部,那么在方法体内部的数据是独立出来的,方法体在执行时,形参接收到的实际上是实参的拷贝值,并不是实际的实参,所以无论方法体内部怎么改变,外部都不会有任何的变化。

        但是如果是传入的指针,则在方法体里面是直接可以修改外部实参的值的,因为方法体内部形参接收到的是外部实参的指针,而两个指针均指向同一个数据,所以当使用指针进行修改数据的时候,数据会变化,并且另一个指针进行寻址数据最终也是获得的变化后的数据。

第三种:这个方法我是在数据结构书上看到的大体写法如下:

void test(LinkedNode &node) {
    printf("%d",node);
}

但是这种方式编译不通过,因为我也不是研究C语言的,所以不太懂这种格式,有大佬可以在评论区解答一下。

3.3 数组指针

数组指针相对更加简单,具体如下:

 int num[] = {1,2,3,4,5};
    int *p=num;
    //指针虽然指向了数组,但是实际通过指针寻址找到的是数组的第一位
    printf("通过指针获取数组下标数据 *p=%p,数据=%d\n",p,*(p));
    for (int i = 0; i < 5; ++i) {
        printf("====================================\n");
        printf("循环通过指针获取数组下标数据 *p=%p,数据=%d\n",p,*(p+i));
        printf("循环通过指针直接获取数据 *p=%p,数据=%d\n",p,p[i]);

    }

定义一个数组,然后定义一个指针,将指针指向数组,就可以直接根据指针进行获取数组数据,这个其实跟数组下标使用区别不大,唯一需要注意的是,*(p+1)这样写就是获取当前指针所在位置的后1位数据,而当前位置基本都是第0位。

打印如下:

总的来说,指针是一个非常有意思的东西,如果要深入理解他的运行方式,则需要学习计算机组成等课程,本文主要是对一些常用方式进行分析理解,如有不对之处,欢迎大佬予以指正。可执行源码放在下面:

#include<stdio.h>
#include <stdlib.h>

typedef struct {
    int data;
    struct LinkedNode* next;
}LinkedNode;
void printNodeA(LinkedNode *node) {
    printf("这里是打印方法,数据通过指针传入 data=%d\n",node->data);
}
void printNodeB(LinkedNode   node) {
    printf("这里是打印方法,数据通过结构体传入 data=%d\n",node.data);
}
void updateNodeA(LinkedNode *node) {
    //传入指针变量,修改指针变量指向的数据值
    //这里因为传入的是指针,并没有将实际的数据传入,所以通过指针去进行寻址找到数据,然后修改了数据
    node->data=100;
}
void updateNodeB(LinkedNode node) {
    //传入实际的结构体变量,修改结构体变量
    //这里实际上只是传入了一个node结构体的备份数据,在这个地方的形参和实参实际上不是一个东西
    node.data = 101;
}

int main() {
    int a=10; //实际的数据
    int *b = &a;//指针,且b变量这个地方定义出来时一个指针
    int *c = a;//把数赋值给指针,相当于c指向了一个地址为10的地方,但是那个地方在我们这个程序里没有数据
    int *d ;//
    d = a;//这两句话写法跟c是一样的效果
    printf("========================\n");
    printf("值 a=%d\n",a);//打印值
    printf("========================\n");
    printf("指针变量 b=%p\n",b);//这里打印的就是地址,b是一个指针变量,他不是值
    printf("========================\n");
    printf("通过指针获取值 *b=%d\n",*b);//打印值,因为使用了*号对&a进行了一个寻址,找到了实际的数据
    printf("========================\n");
    printf("值的指针 &a=%p\n",&a);//这里是最开始最原始的指针,&a->a
    printf("========================\n");
    printf("直接把值赋给了指针 d=%p\n",d);//这里因为赋值的是10,所以这个数据打印的指针是另一个地方
    printf("直接把值赋给了指针 c=%p\n",c);//同理
    printf("========================\n");
    // printf("*c=%d\n",*c);//这里*对10进行了一个寻址,但是那个位置啥都没有,所以什么都不会打印,甚至还报错
    // printf("*d=%p\n",*d);//同理
    LinkedNode node;//定义了一个结构体
    LinkedNode *pNode;//定义了一个结构体指针
    node.data = 1;
    node.next = NULL;
    pNode = &node;//把结构体的指针赋给指针变量
    // pNode->next = node;
    printf("========================\n");
    printf("通过结构体直接用.读出数据 node.data=%d\n",node.data);//通过结构体直接用.读出数据
    int *data = &node.data;//定义了一个int指针
    printf("========================\n");
    printf("指针变量 *data = %d\n",data);//打印出来的是指针变量
    printf("========================\n");
    printf("通过指针变量获取的值 **data = %d\n",*data);//将指针变量进行寻址得到实际数据
    printf("========================\n");
    printf("结构体指针 pNode= %d\n",pNode);//打印的是结构体整体
    printf("========================\n");
    printf("结构体 node= %d\n",node);//这个也是打印的结构体整体
    printf("========================\n");
    printf("通过.直接获取结构体里面的数据 node.data= %d\n",node.data);
    printf("========================\n");
    printf("通过->获取结构体指针的寻址后的实际数据 pNode->data= %d\n",pNode->data);//进行了一次寻址操作
    printf("========================\n");

    printNodeA(&node);//传参打印
    printNodeB(node);//传参打印
    updateNodeB(node);//传参修改
    printf("========================\n");
    printf("通过直接传参的方式修改后的node变量 node.data=%d\n",node.data);
    printf("通过直接传参的方式修改后的node变量的指针指向的变量 pNode->data= %d\n",pNode->data);//在其他方法里面修改了后再次打印看效果
    printf("========================\n");
    //通过指针的方式进行修改
    updateNodeA(&node);
    printf("========================\n");
    printf("通过指针传参的方式修改后的node变量 node.data=%d\n",node.data);
    printf("通过指针传参的方式修改后的node变量的指针指向的变量 pNode->data= %d\n",pNode->data);
    printf("========================\n");





    int num[] = {1,2,3,4,5};
    int *p=num;
    //指针虽然指向了数组,但是实际通过指针寻址找到的是数组的第一位
    printf("通过指针获取数组下标数据 *p=%p,数据=%d\n",p,*(p));
    for (int i = 0; i < 5; ++i) {
        printf("====================================\n");
        printf("循环通过指针获取数组下标数据 *p=%p,数据=%d\n",p,*(p+i));
        printf("循环通过指针直接获取数据 *p=%p,数据=%d\n",p,p[i]);

    }




return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值