(以下为本人的一些学习心得,有错误希望大家能指出来)
C语言指针详解
一.指针初解
在初步了解指针之前,我们先看一下指针的定义(来自《C语言入门经典第五版》):
可以存储地址的变量叫做指针(pointers)。
我们都知道在计算机中,程序都是存储在内存中的,CPU通过地址对其进行读取和写入数据。平时所用到的变量名,都只是一层包装,是为了方便开发人员而设置的。(毕竟正经人谁会记得自己变量的地址啊,我不会记,你会记嘛?下贱哈哈哈)
将这层包装撕开,得到其实就是地址以及地址中的内容。但有时候我们又有一些高级操作涉及到内存地址,这个时候指针就为我们提供了一个操作性更强的选择。
其中* 运算符,即取消引用运算符(我更喜欢叫它取消引用运算符,原因后面就知道了)和&运算符,即寻址运算符是指针的精髓。
1.“ * ” 运算符(取消引用运算符)
首先我们先了解这么一段话:
"* "运算符,我们称为取消引用符号,意思就是告诉编译器这个变量里的内容不是我要找的(第一层),变量里内容即某个地址上的内容才是我(第二层)。
这个时候就有一个问题,编译器去找了那个地址的内容,因为只有起始地址,所以编译器会很迷惑,你只要这一个地址空间的内容还是连续几个的?我拿到了以后咋办呢?我又不知道它是什么类型的数据。所以我们在声明指针的时候不仅需要* 运算符还需要指定其类型,这样编译器根据类型知道读取几个字节,以及如何处理这个数据。然而有一个特殊的类型,那就是void类型。是空类型,就是啥类型都不是?我们在声明一个变量的时候不可能不声明其类型,因为编译器需要分配一块空间给这个变量,但是没有类型,编译器就不知道应该给多大的空间。那么为什么指针类型的变量可以声明为void型呢?编译器不是也要分配空间吗?理由其实也很简单,我们考虑一下指针的值是什么?是一个地址。在计算机中,无论是32位还是64位的操作系统,内存的地址长度肯定是固定的(32位的为4个字节,64位的为8个字节)。也就是说指针的大小是固定的,只不过需要在使用指针指向的内容之前编译器要知道数据是什么类型的,所以可以暂时把指针声明为void型,而编译器依旧会分配一块固定大小的空间给这个指针。
2.&运算符(寻址运算符)
然后我们再来了解一下另一个运算符:
"&"运算符,我们称为寻址运算符,毕竟咱有了指针了,不知道地址也没法用啊。所以通过这个运算符我们就可以得到变量在内存中的地址。
我相信很多人第一次见到这个运算符都是在scanf()函数中遇到的,当时大家可能会好奇,也可能就当作语法记住了。现在回头一看是不是豁然开朗?你输入一个数据,编译器得知道往哪去存对吧,那编译器不想自己去根据变量名猜地址,可不就让你自己来嘛。(滑稽)所以我们通过&运算符加一个变量,这样就给了scanf()函数一个参数即要存入的地址。这样一看,scanf()也只不过是一个很简单的void型函数,有两个参数,第一个是字符串类型,说明你要怎么输入数据,输入什么样子的数据,第二个参数就是要存放的地址。至于源码嘛,还没看。。。
3.指针的用法
好的,我们了解了指针的原理以后就很容易明白指针的用法了。
1)声明和初始化
我们在声明指针的时候一定得记得初始化!我们在声明指针的时候一定得记得初始化!我们在声明指针的时候一定得记得初始化!毕竟编译器随机分配的空间里谁也不知道原来放的是什么内容,访问这个不知道是什么值的地址以后也不知道会拿到什么数据,所以最好初始化为NULL,表示里面的值现在啥都没指(其实就是0)这样就不会意外覆盖内存,很安全。当然,想直接指向某个变量也可以初始化为其地址(用寻址运算符)
2)使用时不加* 的用法
不加取消引用运算符的时候指针看起来就是一个很乖巧的普通变量,也就是简简单单的第一层,就把它当作一个普通变量就好了。只不过这个变量里面存放的值是一个地址。你对它进行算术操作,它的值也会变,也就是说地址的值变了,这个时候指向的内存的内容就不是原来的那块地址了。对了,这里对指针的运算我们每次加1其实是加一个指针类型的字节数,即char* 类型的指针每次++,就是加一个字节;换成int* 类型就是加4个字节(奉劝大家不是连续的内存地址访问的话,别瞎搞,容易出事)
3)使用时加* 的用法
加了* 运算符即取消引用运算符,那么就是处在了第二层(之前埋下的伏笔来了),那么这个时候作为一个整体看作一个变量的话,* pointer等同于指针指向的那个变量的内容,这个时候修改* pointer是直接在内存级修改的,所以也相当于修改指向的变量的内容。
注意:在指针的运算中," * "和++的优先级相同,所以使用*pointer++的本意是指针指向的内容+1,但是实际却是指针的值先++,然后再引用指针指向的内容,所以大家尽量记得加括号。