时间:2022-06-07 09:20:50 | 栏目:C代码 | 点击:次
引例:
计算机的内存看成大街上的一排房屋,每个房屋都要有门牌号,这个就相当于计算机的内存地址,而房屋里面住的人、家具等等就相当于需要存放的各种各样的数据,所以要想访问这些数据就得知道它的内存地址。
**bit:计算机的内存便是由数以亿万计的位(bit)**组成,每个位的容纳值为0或1。
在16位的系统中(比如8086微机) 1字 (word)= 2字节(byte)= 16(bit)
在32位的系统中(比如win32) 1字(word)= 4字节(byte)=32(bit)
在64位的系统中(比如win64)1字(word)= 8字节(byte)=64(bit)
地址与字节的关系:
拿我本人的机器来举例吧,我的操作系统是64位的,1word=8byte=64bit,int类型在内存中占4个字节(64位+VS),
所以地址是4个连在一起的数字,用一般取地址符&输出的是变量的首地址。
地址就是以字节做单位的。指针类型占4个字节。
补充:数据类型所占的字节数或者位数实际上与操作系统的位数和编译器(不同的编译器支持的位数可能有所不同),反正具体某种数据类型所占的字节数是编译器根据操作系统的位数决定的,用例如==sizeof()==测一下就好。
C类型 | 32位(所占字节数) | 64位(所占字节数) |
---|---|---|
char | 1个字节 | 1 |
short int | 2 | 2 |
int | 4 | 4 |
wchar_t | 2(宽字符) | 2 |
char16_t | 2(unicode字符) | 2 |
char32_t | 4(unicode字符) | 4 |
float | 4(6位有效数字) | 4 |
double | 8(10位有效数字) | 8 |
int * | 4 | 4 |
int i;//int类型占两个字节,16位(64位操作系统占4个字节),i在内存起始地址为6申请了2个字节的内存空间,并命名为i char a;//char类型占一个字节,8位,a在地址为8上申请了1字节的内存空间,并命名为a
//将30存入变量i的内存空间 i=30; //将字符't'存入变量a的内存空间 a='t'; //通过取地址符&,输出变量的首地址6 cout<<"i的地址为:"<<&i<<endl; //输出变量i所储存的值 cout<<"i的值为:"<<i<<endl; //通过取地址符&,输出变量的首地址8 cout<<"a的地址为:"<<&a<<endl; //输出变量i所储存的值 cout<<"a的值为:"<<a<<endl;
//假设pi占2个字节宽度,实际上32位系统中,指针占4个字节 //指针变量pi在起始地址为8上申请了2个字节的内存空间 int *pi; //将变量i的起始地址赋值给pi,所以pi的值为i的起始地址 pi=&i; //输出指针变量p的首地址11 cout<<"pi的地址为:"<<&pi<<endl; //输出变量i的首地址,因为pi的值被赋予为i的首地址 cout<<"pi的值为:"<<pi<<endl; //通过解引用符*访问到变量i所储存的值 cout<<"pi所指向的值:"<<*pi<<endl; *pi=20;//将通过解引用运算符访问到变量i并赋值为20,pi的值和地址都没有变化 cout<<"i的值为:"<<i<<endl;
变量pi的值就是分配给变量pi的内存空间所储存的数据,在上面这个例子中就是被赋予i的首地址6!
从语法角度看,把指针声明语句里面的指针变量的名字去掉,剩下的就是指针的类型。
从语法角度看,就是把指针声明语句中的指针变量的名字和名字左边的指针声明符*去掉,剩下的就是指针所指的类型了。
**指针的值本是指针变量自身所储存的数值,但是例如int p1=&i;p1的值为i的地址,可分解为两句int p1;p1=&i;这样就不难理解了,p1所储存的值就是i的首地址,因为&i表示变量i的首地址。
在32位程序中(4个字节的程序),所有类型的指针的值都是一个32位的整数。因为32位程序里的内存地址都是32位长(4个byte)。
简而言之,指针的值占4个字节,所以说指针指向了i的内存空间,输出的值为i这块内存区域的首地址。
int i=0; int *p=&i; cout<<p<<endl;//输出的是变量p保留的i的首地址 ++p;//p的值加了4(sizeof(int)=1) cout<<p<<endl;//输出的值为i的首地址+4
一个指针 ptrold 加(减)一个整数 n 后,结果是一个新的指针 ptrnew,ptrnew 的类型和 ptrold 的类型相同,ptrnew 所指向的类型和 ptrold所指向的类型也相同。ptrnew 的值将比 ptrold 的值增加(减少)了 n 乘sizeof(ptrold 所指向的类型)个字节。就是说,ptrnew 所指向的内存区将比 ptrold 所指向的内存区向高(低)地址方向移动了 n 乘sizeof(ptrold 所指向的类型)个字节。
指针和指针进行加减:
两个指针不能进行加法运算
,这是非法操作,因为进行加法后,得到的结果指向一个不知所向的地方,而且毫无意义。两个指针可以进行减法操作,但必须类型相同
,一般用在数组方面,不多说了。
这里&是取地址运算符
,*是间接运算符
,也叫解引用操作符。
重点理解(&a)=20:*
&a表示变量a的地址,(&a)表示通过a的地址找到a,然后通过解引用符给其赋值为20。(int *p=&a; *p=20;这里的p就相当于&a,这样就比较好理解了。)
p 的运算结果就五花八门了。总之p 的结果是 p 所指向的东西,这个东西有这些特点:它的类型是 p 指向的类型,它所占用的地址是 p所指向的地址。
int a[]={1,2,3,4,5}; int i; i=a[0];//i=*a; i=a[3];//i=*(a+3)
数组名a代表数组本身,类型为int [],但是如果把a看作指针的话,它指向数组的第0个单元,类型为int *,所指向的类型为int。
下面总结一下数组的数组名(数组中储存的也是数组)的问题:
声明了一个数组 TYPE array[n],则数组名称 array 就有了两重含义:
在不同的表达式中数组名 array 可以扮演不同的角色:
在表达式 sizeof(array)中,数组名 array 代表数组本身,故这时sizeof 函数测出的是整个数组的大小。
在表达式array 中,array 扮演的是指针,因此这个表达式的结果就是数组第 0 号单元的值。sizeof(*array)测出的是数组单元的大小。
表达式 array+n(其中 n=0,1,2,…)中,array 扮演的是指针,故 array+n 的结果是一个指针,它的类型是 TYPE *,它指向的类型是 TYPE,它指向数组第 n 号单元。故sizeof(array+n)测出的是指针类型的大小。在 32 位程序中结果是 4。
sizeof(int(*)[10])=4
sizeof(int[10])=40
sizeof(ptr)=4
实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。
const int i与int const i没有区别,const int *pi也与int const *pi没有区别,int与const那个放前放后都是一样的。
重点是*在const的左边还是右边:
补充:
情况一:int *pi 指针指向 const int i 常量的情况
const int i1 = 40;
int * pi;
pi = &i1;
//const int 类型的 i1 的地址是不能赋值给指向 int 类型地址的指针 pi 的。否则 pi 岂不是能修改 i1 的值了吗!
pi = (int * ) &i1;//强制类型转换可是 C 所支持的。
*p1=80;//编译能过但是i1的值还是40
情况二:const int *pi 指针指向 const int i1 的情况
const int i1=40;
const int * pi;
pi=&i1;//两个类型相同,可以这样赋值。很显然,i1 的值无论是通过pi 还是 i1 都不能修改的。
情况三:用 const int *const pi 声明的指针
int i;
const int * const pi=&i; //你能想象 pi 能够作什么操作吗?
pi值不能改,也不能通过 pi 修改 i 的值。因为不管是*pi 还是 pi 都是 const的。