C指针的值和地址
目录(?)[-]
1. 指针和指向的变量
2. 改变指针的内容
1. 指针内容和指针指向的内容变量
2. 通过指针赋值的形式改变指针内容
3. 将指针作为函数参数改变指针内容
在C语言中,函数传参分为两种:传址和传值。
传值是一个复制的过程。
传址对于变量和对象来说是传址,而针对于同一级的指针来说也是传值。传址针对的对象是指针指向的对象,此时对指针的一切行为都是为了间接的操作指针所指向的对象。但如果想要操作指针本身的内容值,那么就需要在指针层面再往下一层,让指针本身上升为变量的地位,让深一层的指针(二级指针)指向指针变量,二级指针在函数参数作为输出参数时用得较广。
1 指针和指向的变量
指针是C语言数据类型中一种独立的数据类型:指针类型。作为C语言的精华,当定义一个指针变量时,此变量的作用就是用来存相应数据结构(变量、函数)的地址。然后通过变量的地址(指针变量的值)取地址内容的方式(*指针变量)访问(读、写)相应的变量。如定义一个整形变量和一个整形指针,用指针指向整形变量。
[plain]view plainc opyprint?
1.int i;
2.i = 1;
3.int *p = NULL;
4.p = &i;
5.*p = 2;
此小段程序的功能就是通过指针p访问所指向的i,并通过p改变了所指向的变量i的内容。p地址通过取地址内容符*就能访问地址中存的变量了(变量作为地址的内容)。
它们的关系如下i, p的关系如下:
图1 指针和变量
指针本身作为一个变量,也有自己的地址。指针作为一种特殊的变量作用是用来存地址,指针获取数据结构的地址之后可以通过指针变量访问(读写)到对应的数据结构。通过*p的形式访问的是变量,改变的是指针存的内容中的内容。如上图中,*p = 2;时是通过指针p改变的0x0001(指针值)中的i(0x0001地址中存的内容)的值。
2 改变指针的内容
试想一下,在外定义一个指针变量p1,并将它指向某个变量( var )。当向函数传递一个一级指针时,它传递给函数指针参数p2的是var变量的地址,如果在函数内对p2操作的代码是*p2( 访p2地址中的内容),那么所涉及的内容都是var变量;如果在函数内对p2的操作代码是p2,如p2 = var1( 另一个变量),那么只能代表p2指向的目标变量发生了变化,p2的值也发生了变化。综上,对p2的任何操作都跟外部定义的指针p1无关,p1不会受到任何影响。
2.1指针内容和指针指向的内容(变量)
指针内容是指指针的值,是指针指向内容的地址(如i的地址0x0001)。指针指向的内容是指指针指向的那个变量(如p指向的是变量i)。
2.2 通过指针赋值的形式改变指针内容
同类型的指针可以相互赋值。被重新赋值的指针内容就会被改变,然后就可以访问到新指针内容的变量。
如
[plain]view plainc opyprint?
1.int i = 1;
2.int j = 2;
3.int *p1 = NULL, *p2 = NULL;
4.p1 = &i;
5.p2 = &j;
6.p1 = p2;
7.//Do something
原本p1指向i,存i的地址;p2指向j,存j的地址;p1=p2时,两个指针指向同一块内存,都是存的变量j的地址,两个指针都可以访问到变量j。
这是两个指针在同一个作用域内指针内容改变的形式:指针赋值即可改变。
那么如何让一个函数来改变指针的值呢?
2.3 将指针作为函数参数改变指针内容
传址函数 void fun(int *p);
根据图1知,调用函数fun(p);//(p = &i)
形参为地址(指针)时,函数传参为传址方式即将指针的内容(一个变量的地址)传了进去。然后通过*p(函数参数)操作的都是传进来那个地址所存的变量,而对于这个函数来说,此地址对应的变量的作用域和生存期都还在,这样就间接的操作了传进地址中存的变量,改变的当然是变量(p所指向的变量i了),但未能改变实参指针p的内容(指针内容)。
所以,如果要改变指针的内容且通过将自己作为函数的参数来实现,那就要使作用域比传入指针变量作用域小的指针访问传入指针的指针内容(说起来有点绕)。图示记录
图2 二级指针和指针关系
所以,经过fun(&p1)之后,p1指针内容自然就被改变指向变量j了。这样就实现了指针作为函数参数传入后指针内容被改变。此种方式在程序设计的时候可以采用的^-^。需要做到的一点就是让外部指针的地址作为函数指针参数的值,即函数指针参数在一级指针(*p)形式下所访问的是外部指针变量。函数指针参数的二级指针(**p)形式才是访问外部指针所指向的变量。
C Note Over。
一、区别
int z=0;
int &x = z;//引用,x与z指向同一内存单元
int *y;
y = &x;//0x0012ff14 p本身所占内存单元的地址
cout < 下面是摘抄别人的一段测试程序。 void test(){ int a=8; int *p=&a; p=(int *)malloc(N*sizeof(int)); // printf( "%d/n ",*p); //a. cout < <&p; //0x0012ff14 p本身所占内存单元的地址 //b. cout < <*p; //8p所指向的内存单元的值 c. cout < } 从测试的结果很容易看出来,*P是一个指针变量,它存储的数据是地址,并且也只能存储地址,所以在给*P赋值时,请您确认等号右边的值是不是一个地址。如果不是,那么您的程序将会出现BUG,(或许您是一位优秀的汇编写手,那么请您处理好该指针变量)。 另外还要说明的是int *p=&a;等价于int *p ; p = &a; 应该注意的2点是: 1.给指针只能传地址,不能传值.否则要做强制类型转换. 2.在做类型转换和赋值时候,应该注意赋值的类型匹配. 指针与数组的区别: 很多初学者弄不清指针和数组到底有什么样的关系。我现在就告诉你:他们之间没有 任何关系!只是他们经常穿着相似的衣服来逗你玩罢了。 指针就是指针,指针变量在32 位系统下,永远占4 个byte,其值为某一个内存的地址。指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到。 数组就是数组,其大小与元素的类型和个数有关。定义数组时必须指定其元素的类型 和个数。数组可以存任何类型的数据,但不能存函数。 既然它们之间没有任何关系,那为何很多人把数组和指针混淆呢?甚至很多人认为指 针和数组是一样的。这就与市面上的C 语言的书有关,几乎没有一本书把这个问题讲透彻,讲明白了。 #include intmain() { int a[5]={1,2,3,4,5}; int *ptr1=(int *)(&a+1); //a数组是一个整体,sizeof(a)=20;&a表示数组的首地址,&a+1会相当于加20个字节长度。 int *ptr2=(int *)((int)a+1); //a强制转换为int型后,相当于整数加一,所以这只是相当于地址加了一个字节。再转换为地址存入ptr2 int *ptr3=(int *)(a+1); //这个是大家非常熟悉的了,数组a作为左值时相当于&a[0],它加一相当于加了一个数组类型的长度,即4字节(整形)。a不能作为右值。 printf("%x,%x,%x",ptr1[-1],*ptr2,ptr3); printf("%x,%x,%x",ptr1,ptr2,ptr3); return 0; } #include using namespace std; int g =300; void print(int p,int q) { cout<< p<< endl<< q<< endl; } void set(int*&p,int* q) { p=&g;// p是引用类型,可以改变传进参数的指向 q=&g;// q不会 } int main() { int a =10, b=20; int*pa=&a,*pb=&b; print(a, b); cout<< pa<< endl<< pb<< endl; set(pa, pb); // pa现在指向g,但pb还是指向b,这就是引用和非引用的差别 print(a, b); print(*pa,*pb); return0; } int *&p是指针的引用,不会为p开辟空间,只是一个名字,此时相当直接对pa操作,后者开辟了个临时指针,得到pa的地址,根据指针指向地址修改pa所指向的值!两者效果是一样,但具体的实际过程有区别! 二、*&p表示什么意思 void main() { int *a; int *&p=a; int b=10; p=&b; } int a=10; int p=a; 这种情况,p与a是不同的变量,这里是将a的值赋给p int &p=a; 即p是a 的别名,p和a其实是同一个整形变量,两个占用同一块内存空间,如果有p=15;那么a也是15,修改p与修改a是完全等价的 那么 int *a; int * &p=a; 很容易理解,把int * 看成一个类型,a就是一个整型指针,p 是a的别名 关于"*p","&p","*&p","**&p" 指针方面的理解 根据上面的结果我先前对*&p and **&p 难以理解 由于p = &i,那么i 的地址编号传替给了p.也就是说指针p 存放了i的地址。而输出p 也就是i的地址了。 &p 即代表p指针变量的地址编号,而不是值。 *p 代表指向i的值。 *&p 根据结果断定是本身指针变量的值,那么可以推定**&p 即指向了i的值了。 &p = p指针的地址而不是里边的内容 *(&p)就相当于读出p指针里的内容 就是i的地址 **(&p)就是i的值