美文网首页
C语言关于指针的 a、*a、&a

C语言关于指针的 a、*a、&a

作者: Robinone | 来源:发表于2019-07-29 17:55 被阅读0次

    1.基本概念

    1.1 存储单元

    存储单元一般应具有存储数据和读写数据的功能,以8位(bit)二进制作为一个存储单元,也就是一个字节。

    每个单元有一个地址,是一个整数编码,可以表示为二进制整数。程序中的变量和主存储器的存储单元相对应。

    变量的地址对应着存储单元的地址,变量内容对应着单元所存储的数据。

    存储地址一般用十六进制数表示,而每一个存储器地址中又存放着一组二进制(或十六进制)表示的数,通常称为该地址的内容。

    变量有三个要素是:变量名、变量类型、变量值。

    变量代表了一个存储单元,其中的值是可变的,故称为变量。

    1.2 C语言的变量

    变量a 本质上代表一个存储单元。CPU通过该存储单元的地址访问该存储单元中的数据。所以a本来代表两个值:存储单元的地址和储单元中的数据。于是就有了二异性。

    为了消除这种二义性,C语言规定a表示存储单元中的数据,&a表示存储单元的地址。

    a存储单元中的数据可以是一个普通数值,也可以是另一个存储单元的地址,比如:

    a = &b;
    

    语句就是将b的存储单元的地址存入a存储单元中。

    C语言规定*a代表a中存储的地址对应的存储单元中的数据,也就是访问*a就等于访问b,于是*a提供了通过a访问b中的数据的手段。

    1.3 操作符 *、&

    *:取某地址的值(数据内容),运算符后通常跟一个地址

    &:取某数据(变量)的地址,运算符后通常跟一个变量

    • a表示a对应的存储单元中的数据。
    • &a表示a对应的存储单元的地址。
    • *a表示:首先,要求a对应的存储单元中的数据一定是另一个存储单元的地址。
    • 于是,*a表示另一个存储单元中的数据。

    当a声明的类型是int时,a中存储的是一个整数数值,通过a可以访问(读取或修改)这个数值。
    当a声明的类型是int *时,a中存储的是一个存储单元的地址,而该存储单元中存储的数据是一个整数数值;

    通过*a可以访问(读取或修改)这个数值。a == &a 都是该存储单元的地址。
    当a声明的类型是int *
    时,a中存储的是一个存储单元的地址,而该存储单元中存储的数据是另外一个存储单元的地址,另外这个存储单元中存储的是一个整数数值;通过**a可以访问(读取或修改)这个数值。

    1.4 指针

    C语言中,地址也称指针。

    计算机中所有数据都必须放在内存中,不同类型的数据所占的字节数不一样,如int型占用4字节,char占用一个字节。
    我们将内存中字节的编号称为地址(address)或指针(pointer),地址从0开始依次增加。

    1.4.1 指针的大小

    指针是一个形无符号整型,一个整数,它的大小取决于系统是16 32 还是64位的 16/8=2byte 32/8=4byte 64/8=8byte.

    1.4.2 指针指向的内容的大小

    指针所指向的内存块所占内存大小.32位编译环境下,char占一个字节,int占2个字节,double占4个字节,long double占8个字节(这是默认的32位编译环境下,在64位下翻倍就是了),所以一个char指针所占内存为4个字节(32位下),所指向的内存区域占1个字节.同理其它类型也是一样的。

    2.具体解析

    2.1 普通变量(不带*)

    int i = 100;
    
    

    此时,普通变量变量名i即地址中的数据(变量值),也就是100。存在一个地址:&i(表示唯一)。

    简单理解:普通变量名=数据。

    #include <stdio.h>
    
    void test () {
        int i = 100;
        printf("i-----%i\n",i);
        printf("&i----%p\n",&i);
    }
    
    
    int main(int argc, const char * argv[]) {
      
        test();
        
        return 0;
    }
    

    控制台输出

    i-----100
    &i----0x7ffeefbff51c
    

    2.2 一级指针变量

    int *a = &i;
    

    一级指针变量:变量名a实际所指的内容为变量i的地址,即a=&i=&(*a)=*(&a);其中*a表示通过a中的内容(i的地址)取值即i。存在两个地址:&a(唯一表示方式)和a(表示方式不唯一,如上)。

    简单理解:指针变量名=地址-------->数据。

    
    #include <stdio.h>
    
    void test () {
        int i = 100;
        printf("i-----%i\n",i);
        printf("&i----%p\n",&i);
        int *a;
        printf("a-----%p\n",a);
        printf("&a----%p\n",&a);
        
        a = &i;
        printf("a-----%p\n",a);
    }
    
    
    int main(int argc, const char * argv[]) {
      
        test();
        
        return 0;
    }
    
    

    控制台输出

    i-----100
    &i----0x7ffeefbff51c
    a-----0x0
    &a----0x7ffeefbff510
    a-----0x7ffeefbff51c
    Program ended with exit code: 0
    

    变量a声明后没有赋值,其值为0,将&i(也就是i的地址)赋值给a后,a保存了i的地址,这是变量a对应的存储单元存储的数据,而a本身有自己的地址即自身存储单元的地址。(这里说存储单元并不是特别准确,应该是以a地址开头的存储块)

    2.3 二级指针变量

    int **p = &a;
    

    二级指针变量:变量名p所指内容为一级指针的地址=&a=&(*p)=*(&p)=p,该地址(&(*p))中的内容是*p,其中*p中的内容为i的地址=&i=&(**p)=**(&p)=*p
    (其中p=&a,a=&i)。

    • 存在三个地址:&p(表示唯一)、p(表示不唯一)、*p(表示不唯一)。

    • 简单理解:指针变量名=地址---------->地址------------>数据。

    • 其中&(*)和*(&)相互抵消功能。

    
    #include <stdio.h>
    
    void test () {
        int i = 100;
        printf("i-----%i\n",i);
        printf("&i----%p\n",&i);
        int *a;
        printf("a-----%p\n",a);
        printf("&a----%p\n",&a);
        
        a = &i;
        printf("a-----%p\n",a);
        
        int **p;
        p = &a;
        printf("p-----%p\n",p);
        printf("*p----%p\n",*p);
        printf("&p-----%p\n",&p);
    }
    
    
    int main(int argc, const char * argv[]) {
      
        test();
        
        return 0;
    }
    

    控制台输出

    i-----100
    &i----0x7ffeefbff51c
    a-----0x0
    &a----0x7ffeefbff510
    a-----0x7ffeefbff51c
    p-----0x7ffeefbff510
    *p----0x7ffeefbff51c
    &p-----0x7ffeefbff508
    Program ended with exit code: 0
    

    你可以尝试自己分析一下。

    二级指针保存的是一级指针的地址,它的类型是指针变量,而一级指针保存的是指向数据所在的内存单元的地址,虽然都是地址,但是类型是不一样的。

    3.指针与数组

    C语言中由于指针的灵活性,导致指针能代替数组使用,或者混合使用。容易混淆的是字符数组和字符指针这两个,下面就这两个进行解析。

    3.1字符数组

     char str[10] = {"hello world"};
    

    当编译这句代码时,编译器会将str数组中的元素从第一个元素开始逐个填入(hello world\0 )。

    由于C语言中没有真正的字符串类型,可以通过字符数组表示字符串,因为它的元素地址是连续的。

    C语言中规定数组代表数组所在内存位置的首地址,也是 str[0]的地址,即str = &str[0];

    让我们来看一个问题

    printf("%s",str); 
    

    为什么用首地址就可以输出字符串。

    在C语言中字符串常量的本质表示其实是一个地址。

    3.2 字符指针

    char *s ;
    
    s = "Hello";
    

    为什么可以把一个字符串赋给一个指针变量。。

    这不是类型不一致吗???

    C语言中编译器会给字符串常量分配地址,如果 "Hello", 存储在内存中的 0x3000 0x3001 0x3002 0x3003 0x3004 0x3005 .

    s = "Hello" ,这是什么操作,对了,地址。

    其实真正的意义是 s ="Hello" = 0x3000;

    看清楚了吧 ,Hello 看作是字符串,但是编译器把它看作是地址 0x3000,即字符串常量的本质表现是代表它的第一个字符的地址。。。。。。。。。。

    s = 0x3000

    那么 %s ,它的原理其实也是通过字符串首地址输出字符串,printf("%s ", s); 传给它的其实是s所保存的字符串的地址。。。

    字符数组:

    char  str[10] = "hello";
    

    前面已经说了,str = &str[0] , 也等于 "hello"的首地址。。

    所以printf("%s",str); 本质也是 printf("%s", 地址");

    3.3 char * 与 char a[];

    char *s;
    
    char a[] ;
    
    • 前面说到 a代表字符串的首地址,而s这个指针也保存字符串的地址(其实首地址),即第一个字符的地址,这个地址单元中的数据是一个字符,

    • 这也与 s 所指向的 char 一致。

    • 因此可以 s = a;

    • 但是不能 a = s;

    • C语言中数组名可以复制给指针表示地址, 但是却不能赋给给数组名,它是一个常量类型,所以不能修改。。

    3.4 char ** 与char * a[]

    char *a [] ;
    
    • 由于[] 的优先级高于* 所以a先和 []结合,他还是一个数组,数组中的元素才是char * ,前面讲到char * 是一个变量,保存的地址。。

    • 所以 char *a[ ] = {"China","French","America","German"};

    • 同过这句可以看到, 数组中的元素是字符串,那么sizeof(a) 是多少呢,有人会想到是五个单词的占内存中的全部字节数 6+7+8+7 = 28;

    • 但是其实sizeof(a) = 16;

    • 为什么,前面已经说到, 字符串常量的本质是地址,a 数组中的元素为char * 指针,指针变量占四个字节(64bit编译器为8个字节),那么四个元素就是16个字节了

    3.4.1 易错的char **

     char **s = "hello world";
    
    • s的类型是 char ** 而 "hello world "的类型是 char *

    • 虽然都是地址,但是指向的类型不一样,因此,不能这样用。从其本质来分析,"hello world",代表一个地址,比如0x003001,这个地址中的内容是 'h',为 char 型,而 s 也保存一个地址 ,这个地址中的内容(*s) 是char * ,是一个指针类型,所以两者类型是不一样的。

    3.4.2 char **崩溃

    char  **s;
    *s = "hello world";
    

    上面的代码貌似是合理的,编译也没有问题,但是 printf("%s",*s),就会崩溃

    why??

    咱来慢慢推敲一下。。

    printf("%s",*s); 时,首先得有s 保存的地址,再在这个地址中找到 char *的地址,即*s;

    举例:

    s = 0x1000;

    在0x1000所在的内存单元中保存了"hello world"的地址 0x003001 , *s = 0x003001;

    这样printf("%s",*s);

    这样会先找到 0x1000,然后找到0x003001;

    如果直接 char **s;

    *s = "hello world";

    s 变量中保存的是一个无效随机不可用的地址, 谁也不知道它指向哪里。*s 操作会崩溃。。

    所以用 char **s 时,要给它分配一个内存地址。

    char  **s ;
    s = (char **) malloc(sizeof(char**));
    *s =  "hello world";
    

    这样 s 给分配了了一个可用的地址,比如 s = 0x412f;

    然后在 0x412f所在的内存中的位置,保存 "hello world"的值。



    END

    相关文章

      网友评论

          本文标题:C语言关于指针的 a、*a、&a

          本文链接:https://www.haomeiwen.com/subject/padbrctx.html