zoukankan      html  css  js  c++  java
  • 十一、指针

    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)所以只能返回

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

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

  • 相关阅读:
    自动化单元测试工具 EvoSuite 的简单使用
    Dialog lookup method
    微软Axapta中PDF支持中文
    Connected to Ax AOS by code
    Dialog control field event
    Find the tables extending a specific extended data type
    sqlplus不使用服务名,直接使用IP地址连接Oracle
    ATL编写的带窗口的控件,无论VC6(ATL3.0) VC7.1(ATL7.0) 在Windows 7 或 Server 2008 下,WinForm调用时发生的问题
    Ice的slice文件自定义编译命令
    VC9 SP1中新增加的标准 C++ 库中的新功能
  • 原文地址:https://www.cnblogs.com/Strugglinggirl/p/9046298.html
Copyright © 2011-2022 走看看