c语言指针
所有在内存中的数据结构均可用指针来访问。
认识内存:线性存储
int a[3];//逻辑和存储均为线性
int b[3][4];//二维数组的逻辑是二维的,存储结构是线性的
for(int i = 0;i<3;i++) {
for(int j = 0;j < 4;j++) {
printf("%p
",&arr[i][j]);
}
putchar(10);//输出的地址是线性的
}
存储结构是现行的原因是由内存的物理特性决定的,数组在内存中是一段连续的存储空间
变量的地址
&:取地址
除了char占一个地址,其他的类型包含多个字节也就是多个地址,当我们对一个变量取地址时拿到的是低位字节的地址。
char a;short b;int c;double d;
printf("%p
",&a);
printf("%p
",&b);
printf("%p
",&c);
printf("%p
",&d);
//大小问题---32位下4字节,64位机下8字节,地址的大小与类型没有关系,与平台有关系
printf("sizeof(&a) = %d
",sizeof(&a));
printf("sizeof(&b) = %d
",sizeof(&b));
printf("sizeof(&c) = %d
",sizeof(&c));
printf("sizeof(&d) = %d
",sizeof(&d));
对变量取地址,取出的地址就是一个指针,且是常量指针。
&:reference,*:derefer运算互逆。狭义上是取地址和取内容。
常量指针不是一个单纯的地址+而是有类型的。
指针的本质:是一个有类型的地址,类型决定了从这个地址开始的寻址能力。
char a = 1;short b = 2;int c = 3;double d = 121.2345;
printf("&a = %p
",&a);
printf("&b = %p
",&b);
printf("&c = %p
",&c);
printf("&d = %p
",&d);
/*
&a = 0061FEAF
&b = 0061FEAC
&c = 0061FEA8
&d = 0061FEA0
*/
printf("%d
",*(&a));
printf("%d
",*(&b));
printf("%d
",*(&c));
printf("%f
",*(&d));
//互为逆运算
printf("%d
",*((char *)0x0061FEAF));
printf("%d
",*((short *)0x0061FEAC));
printf("%d
",*((int *)0x0061FEA8));
printf("%f
",*((double *)0x0061FEA0));//前面不加类型会报错。
int data = 0x12345678;
printf("%p
,&data");
printf("%x
",*(&data));
printf("%p
",*((int*)));
指针变量
申明一个指针类型变量:要保存两种东西,一个地址数据,而是类型。
type * pointerName
:表明了本变量是指针,4个字节大小,此处只是表示申明(其他地方是的*一般是取内容作用)。type,类型决定了该指针变量中存储的地址的寻址能力。
int data = 0x12345678;
int *pd = &data;
printf("%x
",*(&data));//12345678通过*原始取数据
printf("%x
",*pd);//12345678通过指针的形式直接取内容,无需转换
printf("%x
",*(int*)pd);//12345678
printf("%x
",*(short*)pd);//5678
printf("%x
",*(char*)pd);//78类型表示取地址能力
//对地址不可以赋值
指向/被指向/更改指向
指向:指向谁就是保存了谁的地址。
被指向:
更改指向:指针可以改变。
野指针
指向一段无效的空间,即无效的指针。
有两种形式:
1 未初始化
2 指向已经被释放的空间
int *pa;
*pa = 100;//野指针,未知的空间
//即使不使用申明成NULL指针
int *p = NULL;//(void *) 0,专门初始化未初始化的指针
void
void一个字节,c语言中最小的单位。
printf("sizeof(void) = %d
",sizeof(void));//1
printf("sizeof(char) = %d
",sizeof(char));//1以后还可以扩展
//运算符的重载,由语境决定运算符的属性
int data;
int *p = &data;//*表示申明
*p = 200;//*表示解引用,取内容。
指针的运算
+,-,++,--
指针:类型(步长)+地址(物理数据)
数值+1:简单的加1
指针+1:加的是步长,即指针类型的大小
int *p = (int*)0x0001;
int pData = 0x0001;
printf("p = %d p+1 = %d
",p,p+1);//1,5
printf("pData+1 = %d pData+1 = %d
",pData,pData+1);//1 2
printf("(doouble*)p = %d (double*)p+1 = %d
",(double*)p,(double*)p+1);//1,9
printf("(int*)pData+1 = %d (int*)pData+1 = %d
",(int*)pData,(int*)pData+1);//1 2
int arr[10];
int * pHead = &arr[0];
int * pTail = &arr[9];
printf("%d
",pTail - pHead);//9 pHead + 9*sizeof(int) = pTail;
int arr1[10];
int pHead1 = (int)&arr[0];
int pTail1 = (int)&arr[9];
printf("%d
",pTail1 - pHead1);//36
运算关系
判断回文
char name[5] = {'m','a','d','a','m'};
char *pH = &name[0];char *pT = &name[4];
int flag = 1;
while(pH < pT) {//指针关系判断
if(*pH == *pT) {
pH++;
pT--;
}else {
flag = 0;
break;
}
}
if(flag ==1) {
printf("回文!");
}else {
printf("不是回文!");
}
数组与指针
一维数组的访问:
本质法---数组偏移法
下标法---通用访问方法
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(int i = 0;i < 10;i++)
{
printf("%d
",*(arr+i));//偏移法
}
for(int i = 0;i < 10;i++)
{
printf("%d
",arr[i]);//通用下标法
}
//数组名是指针常量方可唯一的表示一个数组。数组名是数组的唯一标识符。
//一维的数组名可以赋值给以及指针,二维不可以。
printf("arr =%p
",arr);//数组名的形式访问数组首地址
printf("arr + 1 =%p
",arr + 1);//首地址+1个int类型的大小4
printf("&arr[0] =%p
",&arr[0]);//取数组首元素的地址
printf("&arr[0] + 1 =%p
",&arr[0]+1);//数组首元素的地址+1个int的大小
数组名能干的指针能干,数组名不能干的指针也能干
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(int i = 0;i < 10;i++)
{
printf("%d
",*(arr+i));//偏移法
}
for(int i = 0;i < 10;i++)
{
printf("%d
",arr[i]);//通用下标法
}
int *p = arr;
//可以直接替换
for(int i = 0;i < 10;i++)
{
printf("%d
",*(p+i));//偏移法
}
for(int i = 0;i < 10;i++)
{
printf("%d
",p[i]);//通用下标法
}
for(int i = 0;i < 10;i++) {
printf("%d
",*p++);
printf("%d
",*(p++));
printf("%d
",arr[0]++);//完全等价,++的优先级高于*
}
二维数组与指针
二维数组的访问方式
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
for(int i = 2;i >= 0;i--) {
for(int j = 3;j >= 0;j--) {
printf("a[%d][%d] = %#x
",i,j,&arr[i][j]);
}
printf("==================
");
}
printf("arr = %#p arr+1 = %#x arr+2 = %#x
",arr,arr+1,arr+2);
printf("arr[0] = %#p arr[0]+1 = %#x arr[0]+2 = %#x
",arr[0],arr[0]+1,arr[0]+2)
/*
a[2][3] = 0x61fea4
a[2][2] = 0x61fea0
a[2][1] = 0x61fe9c
a[2][0] = 0x61fe98
==================
a[1][3] = 0x61fe94
a[1][2] = 0x61fe90
a[1][1] = 0x61fe8c
a[1][0] = 0x61fe88
==================
a[0][3] = 0x61fe84
a[0][2] = 0x61fe80
a[0][1] = 0x61fe7c
a[0][0] = 0x61fe78
==================
arr = 0X0061FE78 arr+1 = 0x61fe88 arr+2 = 0x61fe98
arr[0] = 0X0061FE78 arr[0]+1 = 0x61fe7c arr[0]+2 = 0x61fe80
*/
printf("*arr = %#p *arr+1 = %#x *arr+2 = %#x
",*arr,*arr+1,*arr+2);
printf("&arr[0] = %#p &arr[0]+1 = %#x &arr[0]+2 = %#x
",a&rr[0],&arr[0]+1,&arr[0]+2);
//两者互为逆运算
//a-a[0]:对a解引用(*)会产生a[0];对a[0]引用(&)会产生a;
printf("arr[3][2] = %d
",*(*(arr+2)+1));
//arr[i] <=> *(arr+i)
//arr[i][j] <=> *(*(arr+i)+j)
往指定的地址写如数据
int *p = (int*)0x12345678;//指定的地址,c++要求严格的数据类型
*p = 300;
//很容导致内存奔溃