C语言关于指针的 a、*a、&a 您所在的位置:网站首页 十六进制中a表示的值是 C语言关于指针的 a、*a、&a

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

2023-09-17 16:15| 来源: 网络整理| 查看: 265

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 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 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 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



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有