管窥C指针

作者: 不可思议的Mark | 来源:发表于2016-07-24 19:04 被阅读385次
基本概念

什么是指针?
我们把某个变量的地址称为“指向该变量的指针”。我们在写程序的时候怎么获得该变量的地址呢?很简单,只需要在变量的前面添加一个取地址运算符&就可以了:

    int a = 10;
    printf("变量a在内存空间当中的存储地址是:%p\\n",&a);
运行结果

这样我们就能在控制台中打印输出变量a在内存空间中的地址,也就是指向变量a的指针了。
正如我们习惯于用一个变量来存储数值一样,我们也用“指针变量”来存储指针:

    int a = 10;
    int* p;
    p = &a;
    printf("     指向变量a的指针: %p\\n",&a);
    printf("指针变量p中存储的指针: %p\\n", p);

运行结果

当指针变量p当中存储了变量a的地址(指向a的指针)后,我们称指针变量p指向了变量a。
那么当我们拿到一个指针变量的时候,怎么才能获得其指向的内容,也就是变量a呢,很简单,我们只需要在指针变量前添加一个运算符"*"即可:

    int a = 10;
    int* p;
    p = &a;
    printf("     指向变量a的指针: %p\\n",&a);
    printf("指针变量p中存储的指针: %p\\n", p);
    printf("             变量a: %d\\n",a);
    printf("指针变量p指向的内容是: %d\\n", *p);

运行结果
这里我们需要注意的是,用于取得指针变量所指向的内容的运算符""与声明指针变量时用的符号""虽然一样,但意义完全不同。声明指针变量时使用"*"只是用来说明声明的这个变量是一个指针变量。
指针变量,它也是一个变量,所以它自己也是有地址的,换句话说,指针变量也有指向它的指针变量。
    int a = 10;
    int* p;
    p = &a;
    printf("          指向变量a的指针: %p\\n",&a);
    printf("     指针变量p中存储的指针: %p\\n", p);
    printf("                  变量a: %d\\n",a);
    printf("     指针变量p指向的内容是: %d\\n", *p);
    printf("       指向指针变量p的指针:%p\\n",&p);
    printf("指向指针变量p的指针所指内容:%p\\n",*(&p));
    printf("被指针所指指针变量p所指内容:%d\\n",*(*(&p)));
运行结果

有点晕,但是尝试着写一下的话,也就不会晕了。

指针变量的自增运算

指针变量里面存储的是变量的地址,笔者前面的文章写到过,地址使用数字来标识的,就像门牌号一样。那么,作为数字,又是变量,也就说明能够进行运算了,这里只讨论自增运算,目的只有一个:指针变量的运算并不只是单纯的数字运算。明白了这一点后,其他的运算也就能举一反三了。

    int a[3] = {1,2,3};
    int* p = &a[0];
    printf(" p中存储的指针是:%d\\n",p);
    printf("指针p所指向的内容是:%d\\n",*p);
    printf("自增后p中的指针是:%d\\n",++p);
    printf("自增后p指向的内容是:%d\\n", *p);

运行结果

我们可以直观地看到,指针变量的值增加了4,而不是1,所以指针变量指向了这一片内存地址中所存储的下一个整数,而笔者前面的文章页写过整型变量占有四个字节,也就是四个内存地址。

    char a[3] = {'a','b','c'};
    char* p = &a[0];
    printf(" p中存储的指针是:%d\\n",p);
    printf("指针p所指向的内容是:%c\\n",*p);
    printf("自增后p中的指针是:%d\\n",++p);
    printf("自增后p指向的内容是:%c\\n", *p);

运行结果

而在这个例子中,指针变量指向的是char类型的变量,所以在增加一的时候确实只增加一,因为char类型的变量所占用的内存空间是一个字节。
由此,我们得出结论,指针变量在进行运算的时候值的改变根据其类型而有所不同。

指针与数组

先看一个例子:

    int a[3] = {1,2,3,4};
    printf("%p\\n",a);
    printf("%p\\n",&a[0]);
    printf("%p\\n",&a);
    printf("%d\\n",a[0]);
    printf("%d\\n",*a);

运行结果

我们发现,数组的名字就是数组的第一个元素的地址,换句话说,数组名是指向数组第一个元素的指针。
但是,我们还发现了一个问题,那就是a和&a打印出来的东西是相同的,真的是相同的吗?我们可以用代码来验证一下:

    int a[3] = {1,2,3};
    printf("%d\\n",a);
    printf("%d\\n",&a);
    printf("%d\\n",a+1);
    printf("%d\\n",(&a)+1);
运行结果

我们发现,在给a,加一的时候,a指向了数组第二个元素的地址,而在给&a加一的时候,&a指向了存储在数组之外的东西,也就是成了数组之外元素的地址。由此我们得到结论:a是指向数组首元素的指针,而&a则是指向整个数组的指针。
那么二维数组呢?依此类推,用相似的方法写出代码即可验证。

指针变量作函数的参数

先来看一个经典的例子:

    void exchange(int a, int b){
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int a = 10;
    int b = 20;
    exchange(a, b);
    printf("a的值是:%d, b的值是:%d\\n", a, b);  
}

运行结果

我们发现a与b的值并没有发生交换。因为函数在传参数的时候,传入的是变量的副本,然后你在函数中不论对副本作什么改变,都不会影响到其本身的值。
那如果传入指向变量的指针呢:

void exchange(int* a, int* b){
    int temp = *a;
    *a = *b;
    *b = temp;
}
int main() {
    int a = 10;
    int b = 20;
    exchange(&a, &b);
    printf("a的值是:%d, b的值是:%d\\n", a, b);   
}
运行结果
可以看到,a,b的值相互交换了。为什么呢?因为传入的是指针的副本,所以副本依然是分别指向这两个变量的指针,所以在通过运算符来获取这两个变量的值的时候,获取到的就是原来的那两个变量,当用运算符获得原来的变量再通过赋值运算符来给两变量赋值的时候,改变的就是原来的变量的值。
const关键字
    int a = 10;
    int b = 20;
    const int *p;
    p = &a;
    *p = 20;

我们会发现,在给*p赋值的时候编译器会报错

错误提示

因为const int *意为定义一个指向整型常量的指针,而int const *与其意义相同,不过这种写法不被推荐。而int * const的意义则是定义一个指向整型的指针常量:

    int a = 10;
    int * const p;
    p = &a;
错误提示

既然是常量,在定义的时候就应该被赋值,然后在接下来的程序中就不能再改变指针常量中所存储的指针的值,但是指针常量所指向的整型变量的值还是可以被改变的

    int a = 10;
    int b = 20;
    int * const p = &a;
    *p = b;
    printf("a的值为: %d\\n", a);
    
运行结果

如果再加一个const呢?

    int a = 10;
    int b = 20;
    const int * const p = &a;
    *p = b;

编译器会再度报错:


错误提示

由此我们验证了const int * const的含义:一个指向整型常量的指针常量
以上就是对C语言指针的一个基本的了解,光看别人写出来的总结是不够的,如果我们想要真正的理解并且达到完全不会被绕晕的状态,还是需要多多实践,多写多用。

相关文章

  • 管窥C指针

    基本概念 什么是指针?我们把某个变量的地址称为“指向该变量的指针”。我们在写程序的时候怎么获得该变量的地址呢?很简...

  • C语言

    C 指针、指针变量、函数指针、指针函数、指针数组、数组指针、C 数组

  • 02-C语言的指针

    02-C语言的指针 目标 C语言指针释义 指针用法 指针与数组 指针与函数的参数 二级指针 函数指针 指针在C中很...

  • C语言05- 指针

    C语言05- 指针 13:指针 指针是C语言中的精华,也是C语言程序的重点和难点。 13.1:指针定义与使用 指针...

  • 带小白学C语言指针

    C语言里指针才是C语言的开始和指针;C语言里基本所有东西都是由指针演变而成; 指针是指向地址的变量,类型就是指针...

  • 学习笔记3(指针运算,函数参数与指针,数组指针,二级指针)

    一、指针运算 二、数组与指针 三、指针和函数参数 java中: C/C++中: 四、指针数组 五、 二级指针 六、...

  • C++知识点

    C++基本方法: C++ memcpy C++基本特性: C++引用(vs指针) C++指针 C++封装: 将...

  • 再学C语言之指针要点

    C之字符数组 C之指针引用字符串 C语言之数组指针 数组指针:首先它是一个指针,它指向数组指针数组:首先它是一个数...

  • 智能指针

    指针的危害 指针未初始化 野指针 内存泄漏 参考阅读C/C++指针使用常见的坑 智能指针分类 本质:将指针封装为类...

  • 管窥C入门经典

    学C,通常看上一两本入门书足矣(水货如我,就看过一本谭浩强),而书之好坏无所谓,怎么都能把你整入门了,而你一入门,...

网友评论

  • 9fe46a2661d1:楼主那个char *p = &a[0];多此一举了。这句话的意思可以看作是 char *p = & * a;进一步可以看作是char *p = a;如果数组下标是x,可以写为char *p = a + x;c/c++里的内置数组可以当做指针使用
  • Beacon丶:楼主 在写char类型所占的字节的时候,有点小失误,估计是手误。。
    不可思议的Mark:@青锋素笔难画你丶 已改,谢啦
  • 68518eb473db:很详细😗😗😗
  • 倪良伯:请问 能不能再介绍一下 c 指针的具体应用呢?
    9fe46a2661d1:@倪良伯 函数指针,写网络协议处理的时候经常用,还有一些函数的参数作为输出也有用指针,就比如楼主文中交换值的那个函数,虽然也有用引用&的,但毕竟栈空间还是比较宝贵的
    bc732ddc660a:@倪良伯 链表
    不可思议的Mark:@倪良伯 纯粹用C语言没实践过,不过OC中倒是处处都是指针,但是讨论OC的话就是iOS开发相关的东西了

本文标题:管窥C指针

本文链接:https://www.haomeiwen.com/subject/bbfgjttx.html