指针困扰了一些学习编程的人,或许你的老师会告诉你,指针比较难理解。

我当时被老师的话唬住所以学习指针那章的时候都没心情听课。(说得像讲别的内容时我听了似的,开玩笑)

导致了学习链表的时候各种卧槽。

*************************** 基本介绍 **************************

《C语言程序设计与实践》中这样写到:

程序中数据的使用往往是以变量的形式出现,每个变量都对应若干存储单元,变量的值存储在存储单元中,通过对变量的引用和赋值就可以使用或修改存储在存储单元中的数据。

如果一个变量的地址存放在另一个变量中,则存放地址的变量叫指针变量。

指针不是地址,指针是存放地址。指针运算就是根据指针存放的地址得到那个地址下存放的数据。

常用的是一级指针,二级指针一般比较少用。

*************************较为形象的理解 ************************

比如:你拿着一张纸,纸上写着A214,让你去教学楼A214教室找一个人a,A214就是这个人所在的地址,你拿着的纸就相当于一个一级指针,你有地址,你就能找到这个人。

   类似的,二级指针则是:纸上写的A214教室里没有这个人,但是门上贴着另一个地址:A215,然后你来到A215,找到了这个人,那么这张纸就相当于一个二级指针。

     因为你要经过两次寻找,才能找到这个人。

**************************** 进入正题 *************************

指针的声明及初始化:

  //第一种:声明并初始化

  ;
  int *p = a;
 /* 定义指针变量 p 为指向整型数据 a 的指针,这一句写成 int *p = &a 同样是对的,一个意思,这里的 *号只是表示P是一个指针,并不是做指针运算。      但是在DEVC++环境下像第四行代码那样写(不写&符号)会有警告。*/

  //第二种:仅声明

  float *p;    //定义一个指向单精度型数据的指针变量 p

   //  书面一些讲, 定义一个指针变量的一般形式是:
   //  <类型>  *<变量标识符>
   //  变量标识符,就像上面的 p
   //  指针的英文是pointer

一段简单的代码

 #include <stdio.h>
 int main()
 {
     int x,y;
     int *px,*py,*p;
     px = &x;  // &是取地址符号
     py = &y;
     x = ;
     y = *px;
     //指针运算,即取出 px 这个地址(也就是 x 的地址)对应的值赋给 y

     p = &y;
     printf("%d,%d,%d,%d,%d\n",*px,*py,x,y,*p);
     ;
 }

     /*  输出结果:分别是对 px 做指针运算得到的21,对 py 做指针运算得到的
     21,x的值21,y的值21,对p做指针运算得到y的值21  */

注意事项:

1. 指针不能指向一个没有被定义的变量,这也是第一行代码存在的原因。

2. 指针不能指向具体的数值,例如:int *p = 3,这是错误的。

3. 分清楚当前指针是在做指针运算还是表明该指针指向某个变量。

第三条个人认为非常重要,不然会理解不了很多东西,不要一看到 * 号就觉得是在做指针运算(即把那个地址对应的值给取出来),因为有的时候只是表明那个变量的指针身份,表指向而已。

***************************** 指针与数组 ******************************

数组的名字就是它的首地址,这个首地址是不能被改变的,数组可以转换成指针。

 ];
 ; i<; i++)
 {
     *(a+i) = i;
     //  *(a) = i; a++;  这样写是错误的,a是指针常量,常量不能被修改
 }

***************************** 指针常量 *******************************

形如:int * const pa = a;

仔细观察一下,*号在const前面,说明pa是常量。

 ;
 ;
 int *const pa = &a;
 a = b;   //  这句话也可以用*pa = b来代替
 //  如果写 pa = &b 显然是不行的,因为pa这个指针是常量指针
 printf("%d",*pa);
 //  输出结果是3

说白了,不能写pa = xxxx,但是可以写*pa = xxxx

***************************** 常量指针 ****************************

形如:int const *pa = a;

这里int和const谁前谁后都是一样的

 ;
 ;
 const int *pi = &a; //仔细观察可以看出 pi 是指向常量的指针
 pi=&b;     //注意这里,pi可以在任意时候重新赋值一个新内存地址
 b = ;    //如果这里用*pi=30,是错误的
 printf( “%d”, *pi ) ; 
  // 输出是30

有人会疑惑,这明明是指向常量的指针,为什么指向的那个数据却从2变成了30?

好吧,是因为pi存的地址是可以变的,其实只是说不能写*pi = xxxx 而已,不能这样改变,但是可以用别的方式改变。

对比上面两端代码看看,应该就明白了。

************************ 为什么指针变量要分类型 **********************

有的人疑惑,如果初始化指针指向一个已经声明过的变量,是不是可以不用写指针类型?因为指针类型就是它指向的数据的类型啊?

 ;
 ; //如果这里写'1'后面会输出49,因为是ASCII码,ASCII码中的字符1对应十进制49

 char *p = &ch;
 printf("%d",*p); 

请记住:指针的类型决定这个指针会读多少个字节的数据出来。(至于怎么读,从低往高读还是从高往低读,数据存储是从高到低还是从低到高,我还没有理解清楚)

想了解的话可以到这里看:

数据变量在内存中的存储方式:http://blog.sina.com.cn/s/blog_abc091cc0101h0a3.html

堆区,栈区,静态存储区详解:http://my.oschina.net/lxrm/blog/513794

所以声明了类型会更安全。

p读取一个字节的数据,也就是ch的内容 :1

然而如果把第四行改成int *p = &ch,最后输出的就不一定是1了,这要看内存分配,这个指针会读取4个字节(32位平台下int型占4个字节)的数据,非常危险。

************************** 指针在函数中的运用 *************************

请看下面一段代码

值传递:

 #include <stdio.h>

 void fun(int a1,int b1)
 {
     int temp = a1;
     a1 = b1;
     b1 = temp;
 }

 int main()
 {
     ;b = ;
     fun(a,b);
     printf("%d %d",a,b);
     ;
 }

 // 输出结果:3 4   // a1 = 4,b1 = 3,a = 3,b = 4

观察这个函数,简洁明了啊,就是把两个参数交换。但是为什么后面的输出并没有把a,b的值交换?
是这样的:在给函数传递参数的时候,隐式地做了赋值,把调用函数那一句话括号里面的参数赋给了形参。

fun(a,b);    //a1 = a; b1 = b;

进入函数以后,操作数是被赋值过后的a1和b1,所以只是把被a,b分别赋值后的a1,b1作交换,并不是a,b。

这是初次学习函数的同学们常常犯的错误。

看看下一段代码
地址传递:

 1 #include <stdio.h>
 2
 3 void fun(int *pa,int *pb)
 4 {
 5     int temp = *pa;
 6     *pa = *pb;
 7     *pb = temp;
 8 }
 9
10 int main()
11 {
12     int a=3,b = 4;
13     fun(&a,&b);
14     printf("%d %d",a,b);
15     return 0;
16 }
17

传给函数作为参数的是a,b的地址,pa = &a, pb = &b,从这两个地址下取出来的值*pa,*pb当然就是a,b的值了。

然后再交换,所以交换成功。

引用传递:

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

int main()
{
    ,b = ;
    fun(a,b);
    printf("%d %d",a,b);
    ;
}
// 运行结果:4 3

// 这段代码在DEVC++环境下(codeblock也是这样,别的环境我没有试过,可能都会这样),如果选择建立.c而不是.cpp文件,使用引用传递会报错,因为引用传递严格来说不属于C语言的内容

这三段代码中的第二段可以视作是C语言的模拟引用传递

*******************************指向函数的指针******************************

某一数据变量的内存地址可以存储在相应的指针变量中,函数的首地址也可以存储在某个函数指针变量里,这样就可以通过这个函数指针变量来调用所指向的函数。

函数指针变量和别的变量一样,也是需要声明后才能用的,相似于函数void fun(int)的声明,指向这个函数的指针要写成void (*pfun)(int)

于是:void fun(int);

void (*pFun)(int);

这两行就完成了一个函数指针的声明。

 #include <stdio.h>

 void fun(int x)  // 如果此处只是声明,不写实现,x可以不写,只用写参数数据类型
 {
     printf("%d\n",x);
 }

 int main()
 {
     void (*pFun)(int); //括号内也可以写int x
     pFun = &fun;   // 令pFun函数指针指向fun函数
     fun();    // 直接调用函数
     (*pFun)() ;   //  通过指针调用函数
     ;
 }

 // 运行结果:10
             

看到这里,希望觉得自己不懂指针的人会感觉指针并不是那么难懂,虽然我写的都是很浅层次的内容,水平有限请谅解。

说说对C语言指针的理解的更多相关文章

  1. 对C语言指针的理解

    一个小程序引发对于C语言指针的思考: #include <bits/stdc++.h> using namespace std; void my_swap (int* a,int* b) ...

  2. C 语言指针怎么理解?

    对于程序员来说内存可以简化成这样一种东西:&lt;img src="https://pic1.zhimg.com/4d060c3f67c22cd4b07273db00f64708_b ...

  3. C语言指针的理解以及指针的指针的理解

    指针指向的是内存地址编号,内存地址编号指向的是对应的内容. 我们需要一个变量,来储存内存地址编号,这个变量的值是一个内存地址编号,但是我们可以通过修改变量的值,来不断的改变内存地址编号. 但是,我们如 ...

  4. 深入理解C语言 - 指针使用的常见错误

    在C语言中,指针的重要性不言而喻,但在很多时候指针又被认为是一把双刃剑.一方面,指针是构建数据结构和操作内存的精确而高效的工具.另一方面,它们又很容易误用,从而产生不可预知的软件bug.下面总结一下指 ...

  5. C语言教学--二维数组和指针的理解

    对于初学者对二维数组和指针的理解很模糊, 或者感觉很难理解, 其实我们和生活联系起来, 这一切都会变得清晰透彻. 我们用理解一维数组的思想来理解二维数组, 对于一维数组,每个箱子里存放的是具体的苹果, ...

  6. 深入理解C语言 - 指针详解

    一.什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址.CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位.这里,数据对象是指存储在 ...

  7. C语言指针转换为intptr_t类型

    1.前言 今天在看代码时,发现将之一个指针赋值给一个intptr_t类型的变量.由于之前没有见过intptr_t这样数据类型,凭感觉认为intptr_t是int类型的指针.感觉很奇怪,为何要将一个指针 ...

  8. [转]C语言指针学习经验总结浅谈

    指针是C语言的难点和重点,但指针也是C语言的灵魂 . 这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较 ...

  9. 不可或缺 Windows Native (7) - C 语言: 指针

    [源码下载] 不可或缺 Windows Native (7) - C 语言: 指针 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 指针 示例cPointer.h #i ...

随机推荐

  1. 如何在Linux桌面环境下自动启动程序?

    大多数Linux桌面环境有各自的图形用户界面(GUI),让用户可以配置针对特定用户的自动启动程序或服务.本文将介绍如何在各种Linux桌面环境下,自动启动某个程序的方法. AD:WOT2014:用户标 ...

  2. C#_Ajax_分页

    using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MvcTe ...

  3. 微软Hololens学院教程- Holograms 100: Getting Started with Unity【微软教程已经更新,本文是老版本】

    这是老版本的教程,为了不耽误大家的时间,请直接看原文,本文仅供参考哦!原文链接:https://developer.microsoft.com/EN-US/WINDOWS/HOLOGRAPHIC/ho ...

  4. 电商ERP常见功能模块

      电商ERP是适用企业卖家的专业电子商务ERP,支持淘宝.天猫.京东.1688.当当.苏宁.拍拍.唯品会.亚马逊.独立B2C等多网络销售渠道:也包括 异地多仓..货位管理.智能配货等专业的WMS(仓 ...

  5. BIEE Setup

    ORACLE 出品的产品绝对都可以称得上装X神器:安装文件一定要大(小水管不让你下个三天三夜那都不叫oracle),系统内存必须得大.硬盘空间必须足够多.安装时间必须足够长.各种配置必须足够复杂.学习 ...

  6. 【转】Split strings the right way – or the next best way

      I know many people are bored of the string splitting problem, but it still seems to come up almost ...

  7. 深入理解计算机系统第二版习题解答CSAPP 2.18

    将32位补码表示的数转换为10进制数. 32位补码 十进制 0x1b8 0x14 0xFFFFFE58 -424 0xFFFFFE74 -396 0x44 0xFFFFFEC8 -312 0x10 0 ...

  8. Android 自学之拖动条SeekBar

    拖动条(SeekBar)和进度条非常相似,只是进度条采用颜色填充来表明进度完成的程度,而拖动条则通过滑块的位置来标识数值----而且拖动条允许用户拖动滑动块来改变值,因此拖动条通常用于对系统的某种数值 ...

  9. Java项目打包在CMD或者Linux下运行

    Java项目打包在CMD或者Linux下运行 1.在CMD下运行 在eclipse中将项目export成jar包,然后用压缩软件解压

  10. div嵌套内层div的margin-top转移给外层div的解决办法

    在web开发过程中,有些情况下给内层div设置的margin却被莫名奇妙的转移给了外层div.百度之后,发现它竟然是css2.1中盒模型中规定的内容: In this specification, t ...