C语言指针概念及基本使用

来源:互联网 发布:阿里云 和 aws 对比 编辑:程序博客网 时间:2024/06/06 02:58

C语言指针概念及基本使用


阅读目录:

1、指针到底是什么?

2、为什么需要指针?

3、指针使用三部曲

4、指针符号* 及 &

5、指针定义初始化的两种方式

6、左值与右值


1、指针到底是什么?

1.1、指针变量和普通变量的区别
(1)首先必须非常明确:指针的实质就是个变量,它跟普通变量没有任何本质区别。int 定义变量存放整形、char定义变量存放字符型、指针变量定义存放地址。指针完整的名字应该叫指针变量,简称为指针。

// a的实质其实就是一个编译器中的符号,在编译器中a和一个内存空间联系起来// 这个内存空间就是a所代表的那个变量。int a;          // 定义了int型变量,名字叫aint *p;         // 定义了一个指针变量,名字叫p,p指向一个int型变量a = 4;          // 可以操作p = 4;          // 编译器不允许,因为指针变量虽然实质上也是普通变量,但是它的                // 用途和普通变量不同。指针变量存储的应该是另外一个变量的地址                // 而不是用来随意存一些int类型的数。p = (int *)4;   // 我们明知道其实就是数字4,但是我强制类型转换成int *类型的4                // 相当于我告诉编译器,这个4其实是个地址(而且是个int类型变量                // 的地址),那么(int *)4就和p类型相匹配了,编译器就过了。return 0;

2、为什么需要指针?

(1)指针的出现是为了实现间接访问。在汇编中都有间接访问,其实就是CPU的寻址方式中的间接寻址。
(2)间接访问(CPU的间接寻址)是CPU设计时决定的,这个决定了汇编语言必须能够实现间接寻址,又决定了汇编之上的C语言也必须实现简介寻址。
(3)高级语言如Java、C#等没有指针,那他们怎么实现间接访问?答案是语言本身帮我们封装了。


3、指针使用三部曲:定义指针变量、关联指针变量、解引用

(1)当我们int *p定义一个指针变量p时,因为p是局部变量,所以也遵循C语言局部变量的一般规律(定义局部变量并且未初始化,则值是随机的),所以此时p变量中存储的是一个随机的数字。

(2)此时如果我们解引用p,则相当于我们访问了这个随机数字为地址的内存空间。那这个空间到底能不能访问不知道(也许行也许不行),所以如果直接定义指针变量未绑定有效地址就去解引用几乎必死无疑。

(3)定义一个指针变量,不经绑定有效地址就去解引用,就好象拿一个上了镗的枪随意转了几圈然后开了一枪。

(4)指针绑定的意义就在于:让指针指向一个可以访问、应该访问的地方(就好象拿着枪瞄准目标的过程一样),指针的解引用是为了间接访问目标变量(就好象开枪是为了打中目标一样)

int main(void){    // 演示指针的标准使用方式    // 指针使用分3步:定义指针变量、给指针变量赋值(绑定指针)、解引用    int a = 23;    // 第一步,定义指针变量    int *p;    printf("p = %p.\n", p);     // %p打印指针和%x打印指针,打印出的值是一样的    printf("p = 0x%x.\n", p);    // 第二步,绑定指针,其实就是给指针变量赋值,也就是让这个指针指向另外一个变量    // 当我们没有绑定指针变量之前,这个指针不能被解引用。    p = &a;             // 实现指针绑定,让p指向变量a    p = (int *)4;       // 实现指针绑定,让p指向内存地址为4的那个变量    // 第三步,解引用。    // 如果没有绑定指针到某个变量就去解引用,几乎一定会出错。    *p = 555;           // 把555放入p指向的变量中}

4、指针符号* 及 &

我们写的代码是给编译器看的,代码要想达到你想象的结果,就必要编译器对你的代码的理解和你自己对代码的理解一样。编译器理解代码就是理解的符号,所以我们要正确理解C语言中的符号,才能像编译器一样思考程序、理解代码。

4.1、星号*
(1)C语言中*可以表示乘号,也可以表示指针符号。这两个用法是毫无关联的,只是恰好用了同一个符号而已。

(2)星号在用于指针相关功能的时候有2种用法:第一种是指针定义时,*结合前面的类型用于表明要定义的指针的类型;第二种功能是指针解引用,解引用时*p表示p指向的变量本身

// 把*和指针变量放在一起,而不是和int挨着,是为了一行定义多个变量时好理解int *p5, *p6;       // 这样才是定义了2个int *指针变量p5、p6int *p5, p6;        // p5是int *指针,p6是int的普通变量int* p5, p6;        // p5是int *指针,p6是int的普通变量// 实际编译测试,p1到p4都没有警告,说明4种写法编译器认为是一样的,都是定义了// int *类型的指针pint a = 4;int *p1;            // *和int结果,表明p的类型是int *,也就是                    // p是指向int类型变量的指针   int* p2;int*p3;int * p4;p1 = &a;p2 = &a;p3 = &a;p4 = &a;

4.2、取地址符&
(1)取地址符使用时直接加在一个变量的前面,然后取地址符和变量加起来构成一个新的符号,这个符号表示这个变量的地址。

int a = 23;int b = 0;// 演示指针变量解引用int *p;                         // *p就是我们说的星号的第一种用法p = &a;b = *p;         // b = 23       // *p就是我们说的星号的第二种用法printf("b = %d.\n", b);int a;      // &a就表示a的地址。int *p;p = &a;     // 编译器一看到&a,就知道我们是要把变量a的地址赋值给指针变量p            // 因为变量a的地址是编译器分配的,所以只有编译器才知道a的地址            // 所以我们没法直接把a的地址的数字赋值给p,只有用符号&a来替代。// 理解&a,*p这样的符号,关键在于要明白当&和*和后面的变量结合起来后,就共同构成// 了一个新的符号,当作整体来理解,这个新的符号具有一定的意义。

5、指针定义并初始化、与指针定义然后赋值区别

(1)指针定义时可以初始化,指针的初始化其实就是给指针变量初值(跟普通变量的初始化没有任何本质区别)。
(2)指针变量定义同时初始化的格式是:int a = 32; int *p = &a;
(3)不初始化时指针变量先定义再赋值:int a = 32; int *p; p = &a;


6、左值与右值的概念
(1)放在赋值运算符左边的就叫左值,右边的就叫右值。所以赋值操作其实就是:左值 = 右值;
(2)当一个变量做左值时,编译器认为这个变量符号的真实含义是这个变量所对应的那个内存空间;当一个变量做右值时,编译器认为这个变量符号的真实含义是这个变量的值,也就是这个变量所对应的内存空间中存储的那个数。
(3)左值与右值的区别,就好象现实生活中“家”这个字的含义。譬如“我回家了”,这里面的家指的是你家的房子(类似于左值);但是说“家比事业重要”,这时候的家指的是家人(家人就是住在家所对应的那个房子里面的人,类似于右值)

int a = 3, b = 5;a = b;      // 当a做左值时,我们关心的是a所对应的内存空间,而不是其中存储的3b = a;      // 当a做右值时,我们关心的是a所对应空间中存储的数,也就是5

原创粉丝点击