MENU

C语言指针浅谈(持续更新中)

April 4, 2020 • 算法

最近连续写了好几天的Python和kotlin,头都要炸了,今天来写写C语言的指针清醒一下

我前几天就已经写过一篇指针的博客了,但是因为当时对指针的理解不够深刻,比较多的借鉴了别人的额博文,为了避嫌,我已经将它隐藏了,现在重新写一份新的,如果其中有错还希望大家帮忙指正

什么是指针变量

在电脑中,为了方便,操作系统将软件与硬件相结合,将原有的物理内存虚拟化为虚拟内存,这使得内存中的每一个地址都有了一个编号,这个编号就是所谓的内存地址,指针变量就是指向内存地址的变量,在32位的操作系统中,指针变量的大小为4个字节

指针变量有什么优点么

其一:操作快

我们先来看一个程序

#include<bits/stdc++.h>
using namespace std;
int main(){
    int a=9;
    int *p=&a;
    printf("%d\n",a);    //output 9
    printf("%d\n",*p);    //output 9
    return 0;
}

在这个程序中,a这个值被称为左值,它指定了一个对象,其实是引用了内存中的地址

左值是C语言中的术语,用于标识特定的数据对象名称或者表达式,因此,对象指的是实际的数据存储,而左值是用于标识或定位存储位置的标签——《C Primer Plus(第六版)》

看不懂没关系,我来给大家解释下,就是说,我们访问a这个变量的时候,实际是先通过a,找到a所对应的数据的内存地址,再通过内存地址来返回一个实际的值,一共是三步

那么,当我们访问p的时候会发生什么呢?

因为p本事就是一个指针变量,其存储的就是一个地址,当我们加上(*)(取值符号,相当于是取地址符&的逆运算)的时候,就可以直接访问该内存地址所存储的变量,一共是两步

指针的值是他所指向对象的地址——《C Primer Plus(第六版)》

其二:可以操作函数外的变量

众所周知,在C语言中,是无法向函数内直接传递数组的(这一点必须要表扬Python,因为是基于万物皆对象的思想开发的语言,所以什么阿猫阿狗都能向函数里面传递),想要向里面传递数组,唯一的办法就是向里面传递地址变量,而数组本身的变量名实际就是一个地址,这也是为什么向函数里面“传递数组”时不需要指定数组大小的原因

我们来看这个程序

#include<bits/stdc++.h>
using namespace std;
//auto increment
void auto_increment(int *p){
    (*p)++;
}
int main(){
    int a=9;
    auto_increment(&a);
    cout<<a<<endl;    //output 10
    return 0;
}

在这个程序中,我们通过向auto_increment函数传入一个指针变量来对a的值进行了更改

再看一下下面这个程序,这个程序通过对两个变量的值进行交换从而给出了指针的几种常见的错误

#include<bits/stdc++.h>
using namespace std;
//Correct exchange of values
void swap_true(int *a,int *b){
    int tmp=*a;
    *a=*b;
    *b=tmp;
}
//Incorrect exchange of values
void swap_false1(int a,int b){
    int tmp=a;
    a=b;
    b=tmp;
}
//Incorrect exchange of values
void swap_false2(int *a,int *b){
    int *tmp=a;
    a=b;
    b=tmp;
}
int main(){
    int a=12,b=21;
    swap_false1(a,b);
    cout<<a<<" "<<b<<endl;    //output 12 21
    swap_false2(&a,&b);
    cout<<a<<" "<<b<<endl;    //output 12 21
    swap_true(&a,&b);
    cout<<a<<" "<<b<<endl;    //output 21 12
    return 0;
}

在第一个函数swap_true中,通过直接操作地址,我们可以很正常的交换两个变量的初值

在第二个函数swap_false1中,因为向函数内传入的是形参,所以没有办法正确的交换两个变量的值

那为什么第三个函数swap_false2也不行呢?

其实原理和第二个类似,毕竟指针变量也是变量嘛……这里传入的被当做形参处理了

我们将第三个的代码稍作修饰,便可得出以上结论

#include<bits/stdc++.h>
using namespace std;
void swap_false2(int *a,int *b){
    int *tmp=a;
    a=b;
    b=tmp;
}
int main(){
    int a=12,b=21;
    printf("%p %p\n",&a,&b);    //output 0xffffcc1c 0xffffcc18
    swap_false2(&a,&b);
    printf("%p %p\n",&a,&b);    //output 0xffffcc1c 0xffffcc18
    return 0;
}

这里还讲一些操作指针的高级操作

字符指针

#include<bits/stdc++.h>
using namespace std;
int main(){
    char str[500]="chicken,you are so beautiful";
    char *s="chicken,you are so beautiful";
}

第一种方法是直接在栈上开辟了一个能容纳"chicken,you are so beautiful"字符串大小的一个空间,然后str存储的是首地址的值

第二种方法是生成了一个指针,然后这个指针直接指向了存放"chicken,you are so beautiful"字符串的这片空间(值得一提的是,在ISO C++11 中,这种方法是不被允许的,所以有些基于c++11标准的编译器会报错也是很正常的了)

我们输出他们的地址看看

#include<bits/stdc++.h>
using namespace std;
int main(){
    char str[500]="chicken,you are so beautiful";
    char *s="chicken,you are so beautiful";
    printf("%p\n",&str);    //0xffffca10
    printf("%p\n",s);        //0x1004030a8
    cout<< sizeof(s)<<endl;    //8 我是64位的操作系统所以是8
    return 0;
    //博主的测试环境是Clion2019.3+cygwin+C++14标准
}

指针数组和数组指针

这玩意儿听起来有点绕口

  1. 指针就是由一堆指针变量组成的数组
  2. 数组指针就是一个指针,指向的是一个数组
作者:NorthCity1984
出处:https://grimoire.cn/algorithm/pointer.html
版权:本文《C语言指针浅谈(持续更新中)》版权归作者所有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

Last Modified: April 10, 2020