美文网首页
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语言05- 指针

    C语言05- 指针 13:指针 指针是C语言中的精华,也是C语言程序的重点和难点。 13.1:指针定义与使用 指针...

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

    1.基本概念 1.1 存储单元 存储单元一般应具有存储数据和读写数据的功能,以8位(bit)二进制作为一个存储单元...

  • 02-C语言的指针

    02-C语言的指针 目标 C语言指针释义 指针用法 指针与数组 指针与函数的参数 二级指针 函数指针 指针在C中很...

  • C语言指针总结大学霸IT达人

    C语言指针总结大学霸IT达人 C语言的指针是C语言区别其它语言的最主要的特定之一。有了指针,C语言就可以抛开所有束...

  • 带小白学C语言指针

    C语言里指针才是C语言的开始和指针;C语言里基本所有东西都是由指针演变而成; 指针是指向地址的变量,类型就是指针...

  • 在Swift中使用C语言的指针

    在Swift中使用C语言的指针 在Swift中使用C语言的指针

  • 关于C语言函数指针

  • [Swift]结构体指针操作

    C语言的指针操作 在c语言中申明一个变量并通过指针修改该变量的值 a value is 2 c语言操作结构体指针操...

  • Go语言-指针

    Go语言中的指针不同于C语言,Go语言的指针使用方法要简单很多。当然和C语言从指针定义到指针的使用都有很大的不同。...

  • 指针数组和数组指针

    指针数组|存储指针的数组 在C语言和C++语言中,数组元素全为指针的数组称为指针数组。 例如:int *p[10]...

网友评论

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

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