听翁恺老师mooc笔记(3)--指针的定义
在上一个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),而不能传递给它这个变量本身或者这个变量的值,我们需要使用&运算符将这个变量的地址传给这个指针,让我们编程试一试这件事情:
1 #include <stdio.h> 2 void f(int *p); 3 4 int main(void) { 5 int i=6; 6 printf("&i=%p\n",&i);//在main里有一个i的变量,在main里打印出i的地址,看下地址是多少 7 f(&i); //然后把i的地址取出来交给f函数 8 return 0; 9 } 10 11 void f(int *p){ 12 printf(" p=%p\n",p); //在f函数里,打印出p的值看下是多少 13 }
看下结果:i的地址是4c,取出来传给f函数,在f函数里这个函数也是4c。这就意味着在main里有个变量i的值是6,地址是4c,然后我们把这个地址取出来交给了f函数里变量p,p的值是4c,于是我们可以说p是一个指针,指向i这个变量。
这就意味着在f函数里面,有外面的main里面的变量i的地址,我们不知道它叫做i,但是我们有它的地址,如果不是这样子传一个地址进去,可能只能得到它的一个值,比如再加入一个g函数,输入是一个int数,将i的值传进去:
1 #include <stdio.h> 2 void f(int *p); 3 void g(int k); 4 5 int main(void) { 6 int i=6; 7 printf("&i=%p\n",&i); 8 f(&i); 9 g(i); //再main里调用g函数,将i传递过去给g函数 10 return 0; 11 } 12 13 void f(int *p){ 14 printf(" p=%p\n",p); 15 } 16 17 void g(int k){ //g函数,输入是一个整型数k 18 printf(" k=%d\n",k); 19 }
在这个过程当中,在函数g里得到只是i的值,和外面main里的变量i之间没用直接的关系。这是我们之前学函数的时候学到的。现在我们通过这个指针变量p得到了变量i的地址,这使得f函数里拥有可以访问外面这个这个变量的能力了,怎么访问?访问意味着读或者写,读是访问,我们可以读到这个值,写那?写是修改i变量的值,怎么写?如果有一个地址,你想访问这个地址上的变量,那么需要用到一个运算符,*。
*运算符
*不是一个新的运算符,在做乘法的时候我们使用*进行乘法。现在需要把这个*当作一个新的运算符,一个单目的运算符,只有一个操作数,我们用它来访问指针的值所表示的地址上的变量。那么我们使用*号加上指针得到的变量的值之后那,这个变量可以拿来做左值,也可以用来做右值,也就是我们既可以拿来放在等号左边去写这个值,也可以放在等号的右边去读这个值。下面来试一试这个值该怎么做:
1 void f(int *p){ 2 printf(" p=%p\n",p); 3 printf("*p=%d\n",*p);//在f里打印*p的值,这里一定要有个概念就是说,*号和p放在一起,那么*p这个整体可以看成一个整数 4 }
将*p当成一个整数,和之前讲数组时,可以将一个数组a[10]当成一个整体也就是一个整数来看一样。现在输出这个整数的值,运行的结果是*p=6,所以这就意味着,通过p这个指针我们得到了f函数外面变量i的值,也就是读除了这个变量的值。那么我们试下写入:
1 void f(int *p){ 2 printf(" p=%p\n",p); 3 printf("*p=%d\n",*p); 4 *p = 26; 5 }
在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也不是变量,他们是表达式运算的结果,所以说再赋值号的左边是左值,在赋值号的右边是右值,这是值,这不是变量。
目前为止我们看了指针的两个运算符,一个是取地址运算符,一个是取地址所代表变量的值,这两个运算符是相互反作用的。
posted on 2017-02-21 16:48 niuxiaoxia 阅读(823) 评论(1) 编辑 收藏 举报