一、内存(也称内存储器、主存储器)基本单位
?? 计算机存储信息的最小单位是位(又称比特,写作bit,简写b,一位为一个0或1),计算机内存中的基本存储单位是字节(写作Byte,简写B,1B=8b)
?? 1GB=1024MB,1MB=1024KB,1KB=1024B, 210=1024(2的10次方为1024) ?? 所以1GB= 1024MB= 1024 * 1024KB= 1024 * 1024 * 1024B(即10243、230) ?? ??
二、地址和指针的基本概念
?? 计算机的内存是由一个个字节(Byte)组成的,每个字节可保存8b(8个0或1)。计算机内存的字节数可以有很多,2GB的内存就有2147483648(2 * 10243、231)个字节,把这些内存用十六进制编号: ?? 第0个字节编为为0号, ?? 第15个字节编为F号, ?? 第16个字节编为10号(即0x10), ?? 第6421984个字节编为61FDE0(即0x61FDE0) ?? 第2147483648个字节编为80000000(即0x80000000,将十六进制换算成二进制的话等价于8 * 167,等价于23 * 24*7,等价于231即2147483648) ?? ??以上计算机内存中字节的十六进制编号就称为地址,地址也称为指针 ??什么是指针呢?指针就是地址(指针存放的数据就是地址),地址就是编号,也就是内存中字节的编号 ?? ??
三、指针变量、char属于整型的?
1.关于字符和整数
??在c/c++中char是整数类型的,因为字符常量存储在计算机存储单元中时,并不是存储字符(如a,z,#等)本身,而是以其代码(一般采用ASCII代码)存储的,例如字符’a’的ASCII代码时97,因此,在存储单元中存放的是97(以二进制形式存放),而不是字符’a’。 ??另一方面char可表示-128 ~ 127 的数,int可表示-2147483648 ~ 2147483647,char本身就是值类型的,所以说char是整数型的,不要因为char可以用来表示字符就把char不当整型,整数和符号之间是可以转换的:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a='a';
int b=97;
char c='#';
printf("字符a=%c, 整数b转换成字符表示=%c, 字符a转换成整数表示=%d\n", a, b, a);
printf("用整数97表示字符=%c, 90+7=%c\n", 97, 90+7);
return 0;
}
??输出结果为
字符a=a, 整数b转换成字符表示=a, 字符a转换成整数表示=97
用整数97表示字符=a, 90+7=a
?? ??
2.指针变量
??言归正传,平时保存的数据类型一般都是基本类型的数据,如整型 int/long/shore/char、实型 float/double,如果要保存地址怎么办呢? ??地址不能被保存到普通变量中,这时候就到指针变量出场了,指针变量这种变量是专门用来保存地址的,指针变量也可简称为指针 ??如何定义指针变量呢,如果有定义整型变量 a int a=1; ,为了保存a的地址,我们可以定义这样一个指针 p: ??定义指针变量和定义普通变量的形式类似,只需要在变量名前加 * 号
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=1;
int *p;
p=&a;
printf("整数a=%d的地址是%#x,\n指针p的地址是:%#x\n", a, p, &a);
return 0;
}
??输出结果:
整数a=1的地址是0x61fe14,
指针p的地址是:0x61fe14
??注意这里的 *号 不是 *指针运算符 ,* 号是一个标志,有了 * 号才表示所定义的是指针变量,才能保存地址 ??1.变量名是 p,不是*p ??2.变量 p的类型是 int *,不是 int ??指针变量 p保存了普通变量 a的地址,就是“对准了”普通变量 a,因为可以通过 p中所保存的这个地址来访问变量 a(就是存取变量 a)。我们称:
指针变量 p指向了变量 a
或称:
p是指向变量 a的指针变量、
p是指向变量 a的指针
?? ??在一条定义语句中可以同时定义多个指针变量、普通变量
double *m, n;
int *x, *y, z;
int* a, b, c;
?? ??
四、指针变量的基类型
??上面说到指针变量时,提到过: ??2.变量 p的类型是 int * ??“ int * 类型 ”是什么含义呢?它表示指针变量 p所指向的数据类型是 int型,也就是说将来 p要保存一个地址,但这个地址有讲究,必须是一个 int型数据的地址才能被保存 ?? ?? int * 中 int表示该指针变量将保存何种类型数据的地址,换句话说是指针变量所能指向的数据类型,该类型称为指针变量的基类型 ?? ??指针变量要保存的地址必须是基类型这种类型数据的地址,指针变量只能指向同基类型的数据 ?? void基类型的指针:
void *p;
int a=1;
float b=3.14;
double c=3.333;
char d='d';
p=&a;
p=&b;
p=&c;
p=&d;
?? ??
五、指针变量赋值
?? 变量定义了不赋初值,其值就会是随机数 ?? 指针也一样,定义了不赋初值,其保存的地址就会是随机地址,即指向内存中随机的数据,这很危险,说不定指向的是系统运行所必须的一个很重要的数据,如果通过指针修改指向的数据,可能系统就崩溃了,所以为了避免悲剧,指针必须赋初值
int a=100;
int *p=&a;
int *p;
p=&a;
int *p=0;
int *p=NULL;
?? 针对第三种赋值,系统规定,如果一个指针变量里保存的地址为0,则说明这个指针变量不指向任何内容,叫做空指针。 ?? ??
六、指针的两个运算符
?? 1.&取址运算符,获取变量地址 ?? 2.*指针运算符(或称间接访问运算符),获取或改写以指针 p(p指向的地址)为地址的内存单元的内容(注意别和定义时的 * 号 搞混了),如:
int a=101;
int *p=&a;
printf("*p的值为%d, a的值为%d", *p, a);
?? 因为 p保存的是a 的地址,* p 就是取 a地址保存的数据(101),* p 等价于 a ?? & 和 * 运算符的运用:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=1, x=2, *p;
p=&a;
x=*p;
*p=5;
printf("%d %d %d", a, x, *p);
return 0;
}
?? int a=5, *p=&a; ?? p表示指针 p保存的值(a的地址)
?? &p表示指针 p本身的地址
?? * p表示指针 p指向的数据,是变量 a
??&、* 互为逆运算,即一个&和 * 可以相互抵消如:*&p 等价于 p 等价于 &a 、*&*&*&*p 等价于 *p 等价于 *a
?? 利用指针比较两数大小:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=0, b=1, *p, *q, *t;
p=&a;
q=&b;
scanf("%d %d", p, q);
if(a<b)
t=p, p=q, q=t;
printf("最大值:%d, 最小值%d", *p, *q);
return 0;
}
??a、b本身的值没变,变的只是指针 p、q指向的数据,p指针指向最大的数,q指向最小的数
?? ??
七、指针和一维数组
??如有定义一维数组int a[5]={1, 3, 0, 5, 9}; ??那么指针int *p=&a[0] 则指向 a[0],指针指向的数组类型也必须同基类型的数组 ??此时,如果p+1; printf("%d", *p); ,会输出什么?答案是输出3 ??如果有定义指针int *q=&a[3]; ,那么printf("%d", q-p); 会输出什么?答案是2
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[5]={1, 3, 0, 5, 9};
int *p=&a[0], *q=&a[3];
p++;
printf("p+1:%d\n", *p);
printf("q-p:%d", q-p);
return 0;
}
??输出结果:
p+1:3
q-p:2
??1.指针p ± 整数n,不是简单的将指针p保存的地址加减整数n,而是加减n个“单位”,即n个“数组元素”的字节数,也可以理解成数组下标的加减,运算法则:
p ± n = p中保存地址值 ±(每元素字节数*n)
?? ??2.两指针相减,结果为两个地址间相差的单位个数(数组元素个数),运算法则:
p2 - p1=(p2保存地址 - p1保存地址) / 每元素字节数
?? ??通过两指针相减,可以求出指针p指向的数组元素的下标:
int a[10]={1, 3, 2, 4, 5, 7, 9, 6, 8, 0};
int *p=&a[4];
printf("指针p指向的数组元素的下标是:%d", p-&a[0]);
??输出结果:
指针p指向的数组元素的下标是:4
?? ??
八、一维数组的指针:一维数组名是指针变量?
??int a[5]; 对于一维数组名 a有:我们可以用 a 来表示数组 a的首地址(即&a[0]),a是一个假想的“指针变量”,它本质上是数组 a的名字且它表示的地址不能被改变(也有人称数组名 a是指针常量),所以在对指针变量赋值时可以这样:
int a[5]={1, 3, 5, 7, 9};
int *p;
p=&a[0];
p=a;
printf("%d %d", a[3], p[3]);
??输出结果:
7 7
1.指针的语法糖、两个重要公式
??数组名 a是“指针变量”,具有指针变量的所有特性(只要不改变 a的值 ) ??语法糖也叫糖衣语法,在不改变原语法意思的基础上用另一种更简单、可读性更好的语法表示,这种可读性更好的语法就叫语法糖,例如: ??int a[5], *p=a; ,前面学习了 间接访问运算符、指针加减法,*(a+i) 和*(p+i) 实际上都是表示数组元素 a[i] ,a[i] 就是*(a+i) 和*(p+i) 的语法糖 ??公式1:
*(a+i) 等价于 a[i]
?? ??对公式1两边同时做 & 运算,得到公式2:
a+i 等价于 &a[i]
??公式1、2不仅适用于数组名,也适用于所有类型指针变量(一级指针、二级指针乃至更高级别的指针变量,也就是说公式1、2也可以写成*(p+i) 等价于 p[i] )、p+i 等价于 &p[i] ??
2.间接访问运算符与++、-- 的优先级
int a[]={1, 3, 5, 7}, *p=a+1;
printf("%d ", *p++);
printf("%d ", ++*p);
printf("%d ", (*p)++);
printf("%d ", *++p );
??输出结果为
3 6 6 7
?? ??
使用指针逆置数组,然后遍历输出
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[10]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p, *q, t;
p=a;
q=&a[9];
while(p<q)
{
t=*p, *p=*q, *q=t;
p++, q--;
}
for(p=a; p<=&a[9]; p++)
printf("%d ", *p);
return 0;
}
??输出结果为
9 8 7 6 5 4 3 2 1 0
|