在上一个blog学习了&运算符,使用&取了变量、数组等地址,有什么用那?如果能够将取得的变量的地址传递给函数,能否通过这个地址在函数内访问到外部这个变量?答案是肯定的,scanf("%d",&i)就是一个C语言的函数,另外我们试了,将地址交给一个整数,这个事情不靠谱,因为整数和地址不见得总是类型一致,那么什么样的类型可以接收取地址得到的地址那?指针!

指针的定义

  指针类型的变量就是保存地址的变量,就是保存&运算符取得的其他变量的地址的一个变量:有int i,就可以做出一个int *p,这个*星号表示p是一个指针,(通常使用p表示指针,因为p是pointer指针的第一个字母),int *是说p是一个指针,它指向的是一个int,现在把i的地址交给p。

int i;
int *p = &i;

  也就是说我们原本有个变量i,现在我们又有一个变量p,变量p是一个指针,它里面的值是i的地址,比如说i的地址是0x2000,那么p的值就是2000,在这种情况下我们可以说,p指向了i。所以我们在说p指向了i,也就是再说变量p里的值是i的地址。

  指针变量中*的位置。在定义指针的时候,有下列两种写法:*靠近int或者*靠近p。第一种,虽然*靠近int,但是并不是把*加给了int,而是把*加给了p,*p是一个int,于是p是一个指针了,而并不是说p是int*类型的,在C语言中没有int*这个类型,只是说*p是一个类型,不管*是靠近int还是靠近p,意思都是一样的。

int* p;
int *p;

  在看一个例子:通过上一个例子,我们知道int *和int*意思是一样的,都是说p是一个指针,那么q是什么?q只是一个普通类型的int类型的变量,换句话说并不是说把*加给了int,是把*加给了p。如果想要表达p和q都是指针,应该在q前也加一个*。

int* p,q;
int *p,q;

  所以我们有了指针变量,变量里值是内存的地址,这个地址会指向另外一个变量,普通变量里面放的内容是一个实际的值,在指针里面不会放实际的值,只会放别的变量的地址,int *p,p里只会有别的整数的地址。

作为参数的指针

  当将一个指针作为一个函数参数的时候,就可以这么写:void f(int *p),当调用时则需要交给它一个地址:int i=0,f(&i),而不能传递给它这个变量本身或者这个变量的值,我们需要使用&运算符将这个变量的地址传给这个指针,让我们编程试一试这件事情:

 #include <stdio.h>
void f(int *p); int main(void) {
int i=6;
printf("&i=%p\n",&i);//在main里有一个i的变量,在main里打印出i的地址,看下地址是多少
f(&i); //然后把i的地址取出来交给f函数
return ;
} void f(int *p){
printf(" p=%p\n",p); //在f函数里,打印出p的值看下是多少
}

  看下结果:i的地址是4c,取出来传给f函数,在f函数里这个函数也是4c。这就意味着在main里有个变量i的值是6,地址是4c,然后我们把这个地址取出来交给了f函数里变量p,p的值是4c,于是我们可以说p是一个指针,指向i这个变量。

  这就意味着在f函数里面,有外面的main里面的变量i的地址,我们不知道它叫做i,但是我们有它的地址,如果不是这样子传一个地址进去,可能只能得到它的一个值,比如再加入一个g函数,输入是一个int数,将i的值传进去:

 #include <stdio.h>
void f(int *p);
void g(int k); int main(void) {
int i=;
printf("&i=%p\n",&i);
f(&i);
g(i); //再main里调用g函数,将i传递过去给g函数
return ;
} void f(int *p){
printf(" p=%p\n",p);
} void g(int k){ //g函数,输入是一个整型数k
printf(" k=%d\n",k);
}

  在这个过程当中,在函数g里得到只是i的值,和外面main里的变量i之间没用直接的关系。这是我们之前学函数的时候学到的。现在我们通过这个指针变量p得到了变量i的地址,这使得f函数里拥有可以访问外面这个这个变量的能力了,怎么访问?访问意味着读或者写,读是访问,我们可以读到这个值,写那?写是修改i变量的值,怎么写?如果有一个地址,你想访问这个地址上的变量,那么需要用到一个运算符,*。

*运算符

  *不是一个新的运算符,在做乘法的时候我们使用*进行乘法。现在需要把这个*当作一个新的运算符,一个单目的运算符,只有一个操作数,我们用它来访问指针的值所表示的地址上的变量。那么我们使用*号加上指针得到的变量的值之后那,这个变量可以拿来做左值,也可以用来做右值,也就是我们既可以拿来放在等号左边去写这个值,也可以放在等号的右边去读这个值。下面来试一试这个值该怎么做:

 void f(int *p){
printf(" p=%p\n",p);
printf("*p=%d\n",*p);//在f里打印*p的值,这里一定要有个概念就是说,*号和p放在一起,那么*p这个整体可以看成一个整数
}

  将*p当成一个整数,和之前讲数组时,可以将一个数组a[10]当成一个整体也就是一个整数来看一样。现在输出这个整数的值,运行的结果是*p=6,所以这就意味着,通过p这个指针我们得到了f函数外面变量i的值,也就是读除了这个变量的值。那么我们试下写入:

 void f(int *p){
printf(" p=%p\n",p);
printf("*p=%d\n",*p);
*p = ;
}

  在f函数里往*p=26,那么这样能否修改main里i的值那,如果i的值改变了,那么g函数中的k的值也应该改变了,运行程序发现确实如此。这就意味着,经过f函数调用之后,i的值改变了。我们在讲函数的时候一再说,c语言的函数在调用时发生的参数的转移是一种值的传递,我们把值传进了函数,所以函数和调用它的地方没用任何的联系,现在情况有点不一样了,但是我们仍然坚持说,现在这个传递依然是值的传递,地址值被传进了函数,但是因为传进来的是地址,所以通过这个地址在函数内部可以以*p的方式访问到外面的变量的值:p是i的地址,*p就是i的值。我们在对*p赋值的时候,实际上是在做左值。

  左值之所以叫左值,是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果,*p=3,*p是取得地址表示得变量得值,这是表达式运算得结果,可以放在赋值号左边作为一个左值进行赋值。数组a[0]=10,中括号也是一个运算符,取下标的运算符,赋值号左边也是一种运算,运算的结果放在赋值号的左边,可以接受值。所以这是为什么我们叫左值,而不是在赋值号的左边就叫做变量,因为a[0]不是变量,*p也不是变量,他们是表达式运算的结果,所以说再赋值号的左边是左值,在赋值号的右边是右值,这是值,这不是变量。

  目前为止我们看了指针的两个运算符,一个是取地址运算符,一个是取地址所代表变量的值,这两个运算符是相互反作用的。 

听翁恺老师mooc笔记(3)--指针的定义的更多相关文章

  1. 听翁恺老师mooc笔记(7)--字符串1

    C语言中字符串的定义 如果定义一个字符数组word,并使用大括号对其初始化,如下图所示: 但是这个不是C语言的字符串,只是字符数组,不是字符串,因为不能使用字符串的方式进行计算.那么C语言的字符串长什 ...

  2. 听翁恺老师mooc笔记(6)--指针运算

    指针值加1就是将指针值加上sizeof(指针所指变量的类型) 1+1=2,那么指针加1是加上了1这个数字吗?试一下,在代码中定义了char数组,char也是整数,数组名是ac,ac中有10个元素,0- ...

  3. 听翁恺老师mooc笔记(5)--指针与数组

    如果我们通过函数的参数将一个数组传递到参数中去,那么在函数里接收到的是什么东西呢?我们知道如果传递一个普通变量,那么参数接收到的是值,如果传递一个指针变量,参数接收到的也是值,只不过这时的值是地址.那 ...

  4. 听翁恺老师mooc笔记(4)--指针的应用场景

    指针应用场景一:交换两个变量的值 在学习函数时,交换两个数的值,做一个swap函数,传递值进去,也可以将两个值交换过来,没问题,可是离开swap就没有用了,为什么?因为传进去的是两个值. #inclu ...

  5. 听翁恺老师mooc笔记(16)--程序设计与C语言

    问题1:计算机遍布生活的各个方面,若你需要一个功能可以下载APP,我们需要的大部分功能都可以找到对应的APP,如果没有可以自己写一个软件,但是很少人需要这么做,那么我们为什么学习计算机编程语言? 学习 ...

  6. 听翁恺老师mooc笔记(12)--结构中的结构

    结构数组: 和C语言中的int,double一样,一旦我们做出一个结构类型,就可以定义这个结构类型的变量,也可以定义这个结构类型的数组.比如下面这个例子: struct date dates[100] ...

  7. 听翁恺老师mooc笔记(11)--结构和函数

    结构作为函数参数: 声明了一个结构就有了一种自定义的数据类型,这个数据类型和int.float.double一样,int等基本类型可以作为函数的参数,那么这种个自定义的结构类型也应该可以作为函数参数, ...

  8. 听翁恺老师mooc笔记(10)--结构

    定义结构: 在程序里,如果想要表达一个数据就需要一个变量,而每个变量又都需要一个类型,之前学过C语言中有int.double.float.char等这些基础类型,还有指针.数组等.如果你要表达的数据比 ...

  9. 听翁恺老师mooc笔记(8)--字符串2

    字符串的赋值 字符串的输入与输出 对C语言的基础类型,比如int.double等类型,scanf.printf有专门的格式转换,而对字符串,scanf.printf使用%s格式字符进行输入与输出.当使 ...

随机推荐

  1. js call的方法

    call 方法 请参阅 应用于:Function 对象 要求 版本 5.5 调用一个对象的一个方法,以另一个对象替换当前对象. call([thisObj[,arg1[, arg2[, [,.argN ...

  2. 一个2D平面游戏,的碰撞引擎实现

    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); i ...

  3. 微信小程序天坑--图片汉字命名

    图片用汉字命名的,在开发者工具中是显示的,但是,在真机的微信中,是不会显示的. 大写的尴尬,微信小程序开发者工具对于做微信的UI来说,就是一个天坑,在电脑上漂漂亮亮的,到手机上各种意想不到的情况.

  4. 回滚 - 每天5分钟玩转 Docker 容器技术(141)

    kubectl apply 每次更新应用时 Kubernetes 都会记录下当前的配置,保存为一个 revision(版次),这样就可以回滚到某个特定 revision. 默认配置下,Kubernet ...

  5. 软件License认证方案的设计思路

    销售license是商业软件的贯用商业模式.用户向商家购买软件安装盘搭载license许可,才可以使用该软件.我们作为软件开发者,为了保护自身的权益,在软件开发过程中也不可避免的会设计license管 ...

  6. Servlet和Filter生命周期

    1. 生命周期 1.1. Servlet生命周期 servlet是一个基于java技术的WEB组件,运行在服务器端,我们利用 sevlet可以很轻松的扩展WEB服务器的功能,使它满足特定的应用需要.s ...

  7. java 关于性别的处理

    运用数结构的思想(在数据库中把性别的值设置为 男 1 女 0) //数据结构思想应用 public static final Integer EMP_GENDER_OF_MAN = 1; public ...

  8. button 和input 的区别及在表单form中的用法

    先说一下button 和input的定义: <button> 标签定义的是一个按钮 1.在 <button> 元素内部,您可以放置任何内容,比如文本或图像.这是该元素与使用 & ...

  9. Google Maps API的使用

    之前在学习了简单的API调用后,查看了几个知名网站的API调用方法,发现Google的API调用还是相对比较简单的.下面就从API key的获取.googlemaps的安装,再到实际使用做一下说明. ...

  10. windows下远程访问Redis,windows Redis绑定ip无效,Redis设置密码无效,Windows Redis 配置不生效,Windows Redis requirepass不生效,windows下远程访问redis的配置

    转载:http://fanshuyao.iteye.com/blog/2384074 一.Redis下载地址: https://github.com/MicrosoftArchive/redis/re ...