• 指针


    计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样,例如 int 占用 4 个字节,char 占 用 1 个字节。为了正确地访问这些数据,必须为每个字节都编上号码,就像门牌号、身份证号一样,每个字节的编 号是唯一的,根据编号可以准确地找到某个字节。

    我们将内存中字节的编号称为地址(Address)或指针(Pointer)。

    #include<stdio.h>
    int main(){
        int i = 100;
        char ch[20] = "xz";
        printf("%#x , %#x
    ",&i,ch);
        return 0;
    }

    运行结果:0x10ffd98 , 0x10ffd7c

    %#X 表示以十六进制形式输出,并附带前缀 0X。a 是一个变量,用来存放整数,需要在前面加&来获得它的地址; str 本身就表示字符串的首地址,不需要加&。
    C 语言中有一个控制符%p,专门用来以十六进制形式输出地址,不过 %p 的输出格式并不统一,有的编译器带 0x 前缀,有的不带,所以此处我们并没有采用。

    指针定义:

    数据在内存中的地址也称为指针。

    指针变量

    如果一个变量存储了一份数据的指针,我们就称它为指针变量。

    地址 = 指针    指针变量 = 变量 + 地址 (将地址赋给变量就是指针变量)

     定义指针变量: 

    定义指针变量与定义普通变量非常类似,不过要在变量名前面加星号*,格式为:

    datatype *name; 

    datatype是数据类型,比如int char .....

    或者:

    datatype *name = value;

    *表示这是一个指针变量,datatype 表示该指针所指向的数据的类型 。例如:

    int *p1; 

    p1 是一个指向 int 类型数据的指针变量,至于 p1 究竟指向哪一份数据,应该由赋予它的值决定。再如:

    int a = 100; 
    int *p_a = &a;

    在定义指针变量 p_a 的同时对它进行初始化,并将变量 a 的地址赋予它,此时 p_a 就指向了 a。值得注意的是, p_a 需要的一个地址,a 前面必须要加取地址符&,否则是不对的。

    和普通变量一样,指针变量也可以被多次写入,只要你想,随时都能够改变指针变量的值,请看下面的代码:

    //定义普通变量 
    float a = 99.5, b = 10.6; 
    char c = '@', d = '#';
    //定义指针变量 
    float *p1 = &a; 
    char *p2 = &c; 
    //修改指针变量的值 
    p1 = &b;
    p2 = &d; 

    *是一个特殊符号,表明一个变量是指针变量,定义 p1、p2 时必须带*。而给 p1、p2 赋值时,因为已经知道了它 是一个指针变量,就没必要多此一举再带上*,后边可以像使用普通变量一样来使用指针变量。也就是说,定义指针 变量时必须带*,给指针变量赋值时不能带*。

    假设变量 a、b、c、d 的地址分别为 0X1000、0X1004、0X2000、0X2004,下面的示意图很好地反映了 p1、p2 指 向的变化:

     需要强调的是,p1、p2 的类型分别是 float*和 char*,而不是 float 和 char,它们是完全不同的数据类型,读者要引 起注意。

    指针变量也可以连续定义,例如:

    int *a, *b, *c;  //a、b、c 的类型都是 int* 

    注意每个变量前面都要带*。如果写成下面的形式,那么只有 a 是指针变量,b、c 都是类型为 int 的普通变量:

     int *a, b, c; 

    通过指针变量取得数据
    指针变量存储了数据的地址,通过指针变量能够获得该地址上的数据,格式为:

    *pointer; 

    这里的*称为指针运算符,用来取得某个地址上的数据,请看下面的例子:

    #include<stdio.h>
    int main(){
        int i = 100;
        int *p = &i;
        printf("%d,%d
    ",*p,i);
        return 0;
    }

    运行结果:100,100

    假设 i 的地址是 0X1000,p 指向 i 后,p 本身的值也会变为 0X1000,*p 表示获取地址 0X1000 上的数据,也 即变量 i 的值。从运行结果看,*p 和  i 是等价的。

    上节我们说过,CPU 读写数据必须要知道数据在内存中的地址,普通变量和指针变量都是地址的助记符,虽然通过 *p 和 a 获取到的数据一样,但它们的运行过程稍有不同:a 只需要一次运算就能够取得数据,而 *p 要经过两次 运算,多了一层“间接”。 
    假设变量 a、p 的地址分别为 0X1000、0XF0A0,它们的指向关系如下图所示:

     

      程序被编译和链接后,a、p 被替换成相应的地址。使用 *p 的话,要先通过地址 0XF0A0 取得变量 p 本身的值, 这个值是变量 a 的地址,然后再通过这个值取得变量 a 的数据,前后共有两次运算;而使用 a 的话,可以通过地 址 0X1000 直接取得它的数据,只需要一步运算。 
    也就是说,使用指针是间接获取数据,使用变量名是直接获取数据,前者比后者的代价要高。 
    指针除了可以获取内存上的数据,也可以修改内存上的数据,例如:

    #include<stdio.h>
    int main(){
        int a = 15,b = 99,c = 222;
        int *p = &a;  //定义指针变量,并指向具体地址
        *p = b;  //通过指针变量修改内存上的数据
        c  = *p; //通过指针变量获取内存上的数据并赋给c
        printf("%d, %d, %d, %d
    ",a,b,c,*p);
        return 0;
    }

    输出结果:99, 99, 99, 99

    *p 代表的是 a 中的数据,它等价于 a,可以将另外的一份数据赋值给它,也可以将它赋值给另外的一个变量。
     *在不同的场景下有不同的作用:*可以用在指针变量的定义中,表明这是一个指针变量,以和普通变量区分开;使 用指针变量时在前面加*表示获取指针指向的数据,或者说表示的是指针指向的数据本身。
     也就是说,定义指针变量时的*和使用指针变量时的*意义完全不同。

    指针变量也可以出现在普通变量能出现的任何表达式中,例如:

    int x, y, *px = &x, *py = &y; 
    y = *px + 5;  //表示把x的内容加5并赋给y,*px+5相当于(*px)+5 
    y = ++*px;  //px的内容加上1之后赋给y,++*px相当于++(*px) 
    y = *px++;  //相当于y=(*px)++ 
    py = px;  //把一个指针的值赋给另一个指针 

    关于 * 和 & 的谜题
    假设有一个 int 类型的变量 a,pa 是指向它的指针,那么*&a 和&*pa 分别是什么意思呢?
     
    *&a 可以理解为*(&a),&a 表示取变量 a 的地址(等价于 pa),*(&a)表示取这个地址上的数据(等价于 *pa), 绕来绕去,又回到了原点,*&a 仍然等价于 a。
     
    &*pa 可以理解为&(*pa),*pa 表示取得 pa 指向的数据(等价于 a),&(*pa)表示数据的地址(等价于 &a),所 以&*pa 等价于 pa。 对星号*的总结
    在我们目前所学到的语法中,星号*主要有三种用途:  表示乘法,例如 int a = 3, b = 5, c;  c = a * b;,这是最容易理解的。  表示定义一个指针变量,以和普通变量区分开,例如 int a = 100;  int *p = &a;。  表示获取指针指向的数据,是一种间接操作,例如 int a, b, *p = &a;  *p = 100;  b = *p;。

    两种字符串表示方法:

    #include<stdio.h>
    int main(){
        char *str = "I love you!";
        char string[20] = "I love you!";
        printf("%s
    ",str);
        printf("%s
    ",string);
        return 0;
    }

     除了字符数组,C 语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如:

    char *str = "http://c.biancheng.net"; 
    或者: 
    char *str; str = "http://c.biancheng.net"; 

    字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第 0 个字符;我们通常将第 0  个字符的地址 称为字符串的首地址。字符串中每个字符的类型都是 char,所以 str 的类型也必须是 char *。

    #include<stdio.h>
    #include<string.h>
    int main(){
        char *str = "I love you!";
        int len = strlen(str);  //获取字符串长度
        char string[20] = "I love you!";
        printf("%s
    ",str);
        printf("%s
    ",string);
        for(int i=0;i<len;i++){
            printf("%c",*(str+i));  //采用迭代的方法将字符串打印出来
        }
        putchar('
    ');
        return 0;
    }

    这一切看起来和字符数组是多么地相似,它们都可以使用%s 输出整个字符串,都可以使用*或[ ]获取单个字符,这 两种表示字符串的方式是不是就没有区别了呢?
     
    有!它们最根本的区别是在内存中的存储区域不一样,字符数组存储在全局数据区或栈区,第二种形式的字符串存 储在常量区。全局数据区和栈区的字符串(也包括其他数据)有读取和写入的权限,而常量区的字符串(也包括其 他数据)只有读取权限,没有写入权限。

     到底使用字符数组还是字符串常量
    在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操 作,那么只能使用字符数组,不能使用字符串常量。

    最后我们来总结一下,C 语言有两种表示字符串的方法,一种是字符数组,另一种是字符串常量,它们在内存中的 存储位置不同,使得字符数组可以读取和修改,而字符串常量只能读取不能修改。


    int *p  :一级指针,表示p所指向的地址里面存放的是一个int类型的值
    int **p :二级指针,表示p所指向的地址里面存放的是一个指向int类型的指针(即p指向的地址里面存放的是一个指向int的一级指针)
    例如:
    int i=10;         //定义了一个整型变量
    int *p=&i;      //定义了一个指针指向这个变量
    int **p1=&p; //定义了一个二级指针指向p指针
    那么取出10的值方式为:
    printf(“i=[%d] ”,*p);
    printf(“i=[%d] ”,**p1);

  • 相关阅读:
    ios 数据类型转换 UIImage转换为NSData NSData转换为NSString
    iOS UI 12 block传值
    iOS UI 11 单例
    iOS UI 08 uitableview 自定义cell
    iOS UI 07 uitableviewi3
    iOS UI 07 uitableviewi2
    iOS UI 07 uitableview
    iOS UI 05 传值
    iOS UI 04 轨道和动画
    iOS UI 03 事件和手势
  • 原文地址:https://www.cnblogs.com/iBoundary/p/11562363.html
Copyright © 2020-2023  润新知