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

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

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

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

《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. Java Annotations: Explored & Explained--转载

    原文地址:http://www.javacodegeeks.com/2012/08/java-annotations-explored-explained.html One of the many w ...

  2. 常用免费快递查询API对接案例

    现在许多电商公司和ERP都会寻找比较适用的集成快递查询接口,减少对接难度,现在整理一下常用的免费快递查询接口,并附上调用案例,如果有觉得不对的地方,望能够一起沟通探讨! 一.快递查询接口 目前有提供免 ...

  3. MySQL(15):Select-distinct(返回非重复的记录)

    1. 查询所有记录 和 查询 非重复记录 语法: SELECT    [ALL | DISTINCT ] All:返回所有记录 Distinct:返回非重复记录 针对获得的记录内的字段生效.   2. ...

  4. mysql 按月按周统计

    http://hi.baidu.com/liangjian1024/blog/item/6861541b1416094842a9ad7c.html 表finance有俩个字段如下 date date  ...

  5. HTML5 乱记

    HTML5 的文档类型声明可以触发所有具备标准模式的浏览器的标准模式,包括那些对 HTML5 一无所知的浏览器,也就是说,可以在任何网页中都使用 HTML5 文档类型声明 <!DOCTYPE h ...

  6. 1 前言:WPF之What&Why

    转载:http://blog.csdn.net/fwj380891124 自古以来,生产工具的先进程度就代表了生成力的先进程度-------生成力的发展要求人们不断的研发出新的生产工具,新生成工具的诞 ...

  7. c#调用c++ dll(二)

    当对c++几种调用方式有了解以后我们可以试着写个c++动态连接库了,我们现在来写个简单的c++求和函数并把它封装成dll,供以后的c#调用 我们写dll的时候,个人认为,要写就要把dll写好,写标准, ...

  8. Brackets 配置

    插件 Brackets Icons  左侧导航的文件图标 FuncDocr  注释工具 QuickDocsJS  js帮助文档 Beautify  格式化代码 Brackets Git  git支持 ...

  9. IOS 高级开发 KVC(一)

    熟练使用KVC 可以再开发过程中可以给我们带来巨大的好处,尤其是在json 转模型的时候,KVC让程序员摆脱了繁琐无营养的代码堆积.减少代码量就是减少出错的概率.KVC 用起来很灵活,这种灵活的基础是 ...

  10. NodeJS连接MongoDB数据库时报错

    今天第一次尝试连接MongoDB数据库,具体步骤也很简单. 首先,通过NodeJS运行环境安装MongoDB包,进入要安装的目录,执行语句 npm install mongodb 安装成功后,通过如下 ...