十一、指针

1、取地址运算:&运算符取得变量的地址

scanf("%d",&i);  //里的&——获得变量的地址,它的操作数必须是变量。
int i;
printf("%x",&i);         //%x 是以十六进制形式输出指针的值(即内存地址)
printf("%p",&i);         //%p 是输出指针的值(即内存地址)

//64位下结果不一样。一般用%p。

地址的大小是否与int相同取决于编译器。

&不能对没有地址的东西取地址
&(a+b) ?
&(a++)?
&(++a)?

 

2、指针:指针变量就是记录地址(或保存地址)的变量

int i;
int* p=&i;
int* p,q;  //p是指针变量,q不是
int *p,q;  //p是指针变量,q不是。
变量的值是内存的地址

(1)普通变量的值是实际的值。

(2)指针变量的值是具有实际值的变量的地址

 

 作为参数的指针

(1)在被调用的时候得到了某个变量的地址;

(2)在函数里面可以通过这个指针访问外面的这个i;

void f(int *p);
int i=0;
f(&i);

 

访问那个地址上的变量*

(1)* 是一个单目运算符,用来访问指针的值所表示的地址上的变量。

(2)可以做右值也可以做左值

int k=*p;
*p=k+1;
*左值之所以叫左值

(1)是因为出现在赋值号左边 的不是变量,而是值,是表达式计算的结果。

a[0]=2;
*p=3;

(2)是特殊的值,所以叫做左值。

指针的运算符&*

——互相反作用

(1)*&yptr   ——>   *(&yptr)    ——>    *(yptr的地址) ——> 得到那个地址上的变量  ——>  yptr

(2)&*yptr   ——>  &(*yptr)     ——>   &(y)   ——>得到y的地址,也就是yptr   ——>  yptr

传入地址

(1)为什么

int i;
scanf("%d",i);

(2)编译没有报错?

3、指针的使用:指针有什么用呢?

——交换两个变量的值
void swap(int *pa, int *pb)
{
    int    t=*pa;
       *pa=*pb;
       *pb=t;
}
——函数返回多个值,某些值就只能通过指针返回

传入的参数实际上是需要保存带回的结果的变量。

——函数返回运算的状态,结果通过指针返回

(1)常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:

-1或0(在文件操作会看到大量的例子)。

(2)但是当任何数值都是有效的可能结果时,就得分开返回了

后续的语言(C++,Java)采用了异常机制来解决这个问题。

4、指针与数组:为什么数组传进函数后的sizeof不对了?

指针最常见的错误

定义了指针变量,还没有指向任何变量,就开始使用指针

传入函数的数组成了什么?

(1)函数参数表中的数组实际上是指针。

sizeof(a)=sizeof(int*)

但是可以用数组的运算符[]进行运算。

int isPrime(int x , int konwPrimes[] , int numberOfKnowPrimes)
{
    int ret=1;
    int i;
    for(i  =0 ;  i< numberOfKnowPrimes;  i++){
         if(x % konwPrimes[i] == 0 ){
                  ret =0;
                  break;
         } 
   }
   return ret;
}

 

数组参数

以下四种函数原型是等价的:

int  sum(int *ar, int n);
int sum(int *,int);
int sum (int ar[] ,int n);
int sum (int [], int);
数组变量是特殊的指针

(1)数组变量本身表达地址,所以

int a[10];
int *p=a;  //无需用&取地址
a== &a[0];  //但是数组的单元表达的是变量,需要用&取地址。

(2)[]运算符可以对数组做,也可以对指针做:

p[0] <==> a[0]

(3)* 运算符可以对指针做,也可以对数组做:

*a=25;

(4)数组变量是const的指针,所以不能被赋值

int  a[]  <==>  int *const a=....

 

5、指针与const:指针本身和所指的变量都可能是const

指针是const

表示一旦得到了某个变量的地址,不能再指向其他变量。

int  *const q  =  &i;      //q是const

*q = 26 ;   //OK

q++;   //ERROR
所指是const

表示不能通过这个指针去修改那个变量的值(并不能使得那个变量成为const)

const int *p = &i;

*p=26;  //ERROR!  (*p)是const

i = 26 ; //OK
 
p = &j; //OK

判断哪个被const了的标志是const在 * 的前面还是后面。

转换

(1)总是可以把一个非const的值转换成const的

void  f(const  int* x);
int  a=15;
f(&a);  //OK
const int b = a;

f(&b);   //OK
b = a+1;  //ERROR!

(2)当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改。

const数组
const  int  a[]={1,2,3,4,5,6};

(1)数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int。

(2)所以必须通过初始化进行赋值。

保护数组值

(1)因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值。

(2)为了保护数组不被函数破坏,可以设置参数为const

int sum( const int a[], int length);

 

6、指针运算

(1)给指针加1 表示要让指针指向下一个变量

int  a[10];
int  *p=a;
*(p+1) ——> a[1]

(2)如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义。

指针计算

——这些算术运算可以对指针做:

(1)给指针加、减一个整数(+,+=,-,-=)

(2)递增递减(++/--)

(3)两个指针相减

*p++

(1)取出p所指的那个数据来,完事之后顺便把p移到下一个位置去。

(2)* 优先级虽然高,但是没有++高

(3)常用于数组类的连续空间操作

(4)在某些CPU上,这可以直接被翻译成一条汇编指令

指针比较

(1)<,<=,==,>,>=,!=都可以对指针做

(2)比较它们在内存中的地址

(3)数组中的单元的地址肯定是线性递增的

0地址

(1)当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址

(2)所以你的指针不应该具有0值

(3)因此可以用0地址来表示特殊的事情:

——返回的指针是无效的;

——指针没有被真正初始化(先初始化为0)。

(4)NULL是一个预定定义的符号,表示0地址

——有的编译器不愿意你用0来表示0地址。

指针的类型

(1)无论指向什么类型,所有的指针的大小都是一样的,因为都是地址

(2)但是指向不同类型的指针是不能互相赋值的

(3)只是为了避免用错指针

指针的类型转换

(1)void *表示不知道指向什么东西的指针

——计算时与char* 相同(但不相通)

(2)指针也可以转换类型

int *p = &i;
void *q=(void*)p;

(3)这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量

——我不再当你是int啦,我认为你就是个void

用指针来做什么

(1)需要传入较大的数据时用作参数

(2)传入数组后对数组做操作

(3)函数返回不止一个结果

——需要用函数来修改不止一个变量。

(4)动态申请的内存...

7、动态内存分配

输入数据

(1)如果输入数据时,先告诉你个数,然后再输入,要记录每个数据

(2)C99可以用变量做数组定义的大小,C99之前呢

int  *a = (int*) malloc (n*sizeof(int));
malloc
#include<stdlib.h>

void* malloc(size_t  size);

(1)向malloc申请的空间的大小是以字节为单位的

(2)返回的结果是void*,需要类型转换为自己需要的类型

int*)malloc(n*sizeof(int))
没空间了?

(1)如果申请失败则返回0,或者叫做NULL

(2)你的系统能给你多大的空间?

free()

(1)把申请得来空间还给“系统”

(2)申请过的空间,最终都应该要还

(3)只能还申请来的空间的首地址

(4)free(0)?

常见问题

(1)申请了没free——>长时间运行内存逐渐下降

(2)free过来再free

(3)地址变过了,直接去free

8、函数间传递指针

好的模式

(1)如果程序中要用到动态分配的内存,并且会在函数之间传递,不要让函数申请内存后返回给调用者

(2)因为十有八九调用者会忘了free,或找不到合适的时机来free

(3)好的模式是让调用者自己申请,传地址进函数,函数再返回这个地址出来

除非函数的作用就是分配空间,否则不要在函数中malloc然后传出去用

函数返回指针

——返回指针没问题,关键是谁的地址?

(1)本地变量(包括参数)?函数离开后这些变量就不存在了,指针所指的是不能用的内存

(2)传入的指针?没问题

(3)动态申请的内存?没问题

(4)全局变量——>以后说

函数返回数组

和返回指针是一样的

(1)如果一个函数的返回类型是数组,那么它实际返回的也是数组的地址

(2)如果这个数组时这个函数的变量,那么回到调用函数那里,这个数组就不存在了

(3)所以只能返回

——传入的参数:实际就是在调用者那里

——全局变量或动态分配的内存

 

posted @ 2018-05-16 15:38  Strugglinggirl  阅读(186)  评论(0编辑  收藏  举报