说说对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 ...
随机推荐
- Objective-C中NSArray和NSMutableArray是如何使用的?
Objective-C的数组比C++,Java的数组强大在于,NSArray保存的对象可以是不同的对象.但只能保存对象,int ,char,double等基本数据类型不能直接保存,需要通过转换成对象才 ...
- Swift超详细的基础语法-结构体,结构体构造器,定义成员方法, 值类型, 扩充函数
知识点 基本概念 结构体的基本使用 结构体构造器(构造函数/构造方法) 结构体扩充函数(方法), 又称成员方法 结构体是值类型 1. 基本概念 1.1 概念介绍 结构体(struct)是由一系列具有相 ...
- Apache配置虚拟主机后,不能访问localhost的问题
今天想试用一下php7,但是发现php7只支持Apache2.4版本,而我电脑上的Apache是2.2版本,为了想尝鲜,就必须去下载新的Apache2.4 php7和apache2.4安装整合以后,l ...
- 到目前为止,Linux下最完整的Samba服务器配置攻略
关 于Samba,大家有没有这种感觉,弄了N久丫的死活不喘气儿.找来各种“哥”和“姐”,发现全是“详解…配置专题”,看了半天不知道别个在说什么,好不 容易找了个简单的教程,从头到尾跟着做,尼玛,浪费时 ...
- oc语言学习之基础知识点介绍(四):方法的重写、多态以及self、super的介绍
一.方法重写 /* 重写:当子类继承了父类的方法时,如果觉得父类的方法不适合,那么可以对这个方法进行重新实现,那么这个就重写. 注意:也就是说,一定只能发生在父类和子类关系中. 然后是子类重新实现父类 ...
- 《JAVA核心技术卷 卷1 基础知识》
第一卷 关键字:体系结构中立,可移植性,高性能,多线程 体系机构中立:通过解释字节码实现,优点是,让JAVA能在很多机器上运行.缺点是运行速度很慢. 可移植性:因为JAVA的基本数据类型有固定的大小. ...
- 启用Win8(中文版/核心版)中被阉割的远程桌面服务端
Windows 8/8.1 标准版(中文版/核心版)中取消了远程桌面服务端,想通过远程连接到自己的电脑就很麻烦了,第三方远程桌面速度又不理想(如TeamViewer).通过以下方法可让系统恢复远程桌面 ...
- ### Paper about Event Detection
Paper about Event Detection. #@author: gr #@date: 2014-03-15 #@email: forgerui@gmail.com 看一些相关的论文. 1 ...
- 04_SSM框架整合(Spring+SpringMVC+MyBatis)
[SSM的系统架构] [整合概述] 第一步: MyBatis和Spring整合,通过Spring管理mapper接口. 使用mapper的扫描器自动扫描mapper接口在Spring中进行注册. 第二 ...
- 代码版本管理/SVN/Git
代码版本管理 一.SVN 1.SVN diff(create patch) 遇到了一个问题: Index: 通信协议.doc ===================================== ...