说说对C语言指针的理解
指针困扰了一些学习编程的人,或许你的老师会告诉你,指针比较难理解。
我当时被老师的话唬住所以学习指针那章的时候都没心情听课。(说得像讲别的内容时我听了似的,开玩笑)
导致了学习链表的时候各种卧槽。
*************************** 基本介绍 **************************
《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语言指针的理解的更多相关文章
- 对C语言指针的理解
		一个小程序引发对于C语言指针的思考: #include <bits/stdc++.h> using namespace std; void my_swap (int* a,int* b) ... 
- C 语言指针怎么理解?
		对于程序员来说内存可以简化成这样一种东西:<img src="https://pic1.zhimg.com/4d060c3f67c22cd4b07273db00f64708_b ... 
- C语言指针的理解以及指针的指针的理解
		指针指向的是内存地址编号,内存地址编号指向的是对应的内容. 我们需要一个变量,来储存内存地址编号,这个变量的值是一个内存地址编号,但是我们可以通过修改变量的值,来不断的改变内存地址编号. 但是,我们如 ... 
- 深入理解C语言 - 指针使用的常见错误
		在C语言中,指针的重要性不言而喻,但在很多时候指针又被认为是一把双刃剑.一方面,指针是构建数据结构和操作内存的精确而高效的工具.另一方面,它们又很容易误用,从而产生不可预知的软件bug.下面总结一下指 ... 
- C语言教学--二维数组和指针的理解
		对于初学者对二维数组和指针的理解很模糊, 或者感觉很难理解, 其实我们和生活联系起来, 这一切都会变得清晰透彻. 我们用理解一维数组的思想来理解二维数组, 对于一维数组,每个箱子里存放的是具体的苹果, ... 
- 深入理解C语言 - 指针详解
		一.什么是指针 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址.CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位.这里,数据对象是指存储在 ... 
- C语言指针转换为intptr_t类型
		1.前言 今天在看代码时,发现将之一个指针赋值给一个intptr_t类型的变量.由于之前没有见过intptr_t这样数据类型,凭感觉认为intptr_t是int类型的指针.感觉很奇怪,为何要将一个指针 ... 
- [转]C语言指针学习经验总结浅谈
		指针是C语言的难点和重点,但指针也是C语言的灵魂 . 这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较 ... 
- 不可或缺 Windows Native (7) - C 语言: 指针
		[源码下载] 不可或缺 Windows Native (7) - C 语言: 指针 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 指针 示例cPointer.h #i ... 
随机推荐
- android的界面编程
			主要是用View以及ViewGroup,同时ViewGroup又是View的子类,充当容器. 主要有两种方法控制view的行为: 1.在XML布局文件中通过XML属性进行控制 2.在Java代码中通过 ... 
- java_IO读写模版
			InputStream in = null; OutputStream out = null; try{ in = new FileInputStream(); int len=0; byte buf ... 
- android平台获取手机IMSI,IMEI ,序列号,和 手机号的方法
			1)获取运营商sim卡imsi号, String IMSI =android.os.SystemProperties.get( android.telephony.TelephonyPropertie ... 
- Android_sharePreference
			/** * Android的四中数据存储方式: * 1.SharePreferences * 2.SQLite * 3.Content Provider * 4.File * * SharePrefe ... 
- 我的开发框架(WinForm)3
			今天继续给大家介绍核心库的IOC的使用,在我的框架里,IOC使用的比较简单,主要是用于解除模块间的耦合和实例化接口. 1.接口说明,IocContainer接口比较简单只有3个方法,但是是系统中用的最 ... 
- web项目设计与开发——DBHelper3
			本次学习的内容为根据DBHelper对数据库里的数据进行增删改查 具体内容为: 一.编写程序 1.创建工程——Mangage 2.在src目录下创建五个包,分别为DAO,DBHelper,Ent ... 
- epoll讲解--转自”知乎“
			http://my.oschina.net/dclink/blog/287198 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象. 不管是文件,还是套 ... 
- Linux:返回上一次目录 / 返回上次命令目录
			返回上一次目录命令: cd - 该命令等同于cd $OLDPWD,关于这一点在bash的手册页(可使用命令man bash访问其手册页)中有介绍:An argument of - is equiva ... 
- [转].net 调用oracle存储过程返回多个记录集
			本文转自:http://www.netwinform.com/articleinfo.aspx?id=17 存储过程: CREATE OR REPLACE PROCEDURE p_query_cs ( ... 
- MSP430常见问题之开发工具类
			Q1:我自己做了一块MSP430F149的试验板,以前用下载线进行调试没有出现过问题,但是,最近我每次make后用下载线调试时,总是弹出一个窗口,给我提示:Could not find target ... 
