入门编程后,接触到的方方面面都透露着指针的王者气息——好像是块硬骨头。
没事儿,慢慢啃,细细啃,总能啃下来
所以,先来个初阶指针!
指针和内存是息息相关的,所以先看看内存
指针:内存中最小单元的编号(地址)
就是图中绿色的
指针变量:存放指针(地址)的变量——可以通过地址找到地址所指向的内存区域
口语中我们说创建一个指针,存放谁谁的地址,指的就是指针变量
研究一个东西就得不断提出疑问:
1个字节。
为什么不是bit?为什么不是kb?
bit: 一个 char 都要给8个地址来存放,太浪费了
kb: 只是存放一个 char 都要占用1kb,空间又浪费了
通过 通电的地址线(硬件)
对于32位机器,有32根地址线。通电后,每根地址先寻址时,都会产生 高电平(1) 和 低电平(0)
则可以产生
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
…
11111111 11111111 11111111 11111111即 2^32 个地址
一个地址标识一个字节:2^32 byte == 2^32/1024 kb ==2^32/1024/1024mb == 2^32gb == 4gb
即能编址 4gb 的空间
总结:
- 指针就是内存地址 , 指针变量用来存指针(口语中常把指针变量叫成指针)
- 地址的产生是物理性的(通电后的高低电平),能编制多大空间取决于机器(地址线)
- 指针的大小: 32位平台上是 4个字节 ; 64位平台上是8个字节
形式: type + * + 变量名
int a = 10;
int* p = &a;
“*” 说明,p是一个指针变量; “int” 说明,这个指针变量指向的变量类型为int
咱们说:“指针变量可以通过它里面的地址找到地址所指向的内存空间”,怎么实现呢?
解引用(*),我愿称它为“穿梭机”,给它一个地址,就穿梭到对应的位置
int main()
{
int a = 10;
int* pa = &a;//(1)
*pa = 20;//(2)
//这里换成 *(&a) = 20;也是可以的哦
printf("%d\n", a);
return 0;
结果:20
}
解析:
- 创建了一个指向int的指针变量,里边放a的地址
- 把pa这个存有地址的指针变量放到“ 穿梭机 ”里面,就可以穿梭到a的位置了!
总结:穿梭机* 只需要一个地址,就能完成穿梭!
来看两个例子
//例1
#include
int main()
{
int a = 0;
char b = 0;
int* pa = &a;
char* pb = &b;
printf("pa=%p ---->", pa);
printf("pa+1=%p\n", pa + 1);
printf("pb=%p ---->", pb);
printf("pb+1=%p\n", pb + 1);
return 0;
}
输出结果:pa=02B6F6F0 ---->pa+1=02B6F6F4
pb=02B6F6E7 ---->pb+1=02B6F6E8
同样是+1,int*类型的 pa 就移动了 4个字节
而 char*类型的 pb 只移动了 1个字节
//例2:主要看下面调试的过程
#include
int main()
{
int a = 0x11223344;
char* pc = (char*)&a;//强行把指向int的地址放到 char*类型的指针变量
int* pi = &a;
*pc = 0;
*pi = 0;
return 0;
}
- 通过例1可以知道:指针变量的类型,决定了指针前后移动的距离(int* 就移动4bytes ; char*就移动1bytes)
- 通过例2可以知道:指针变量的类型,决定了解引用时能访问多大空间
#include
int main()
{
int a = 0;
int* pa = &a;
printf("%p\n", pa);
printf("%p\n", pa + 1);
printf("%p\n", pa - 1);
return 0;
}
结果:
00D5F700
00D5F704
00D5F6FC
通过例子可以知道:指针的 +/- 运算,实质上是 向前/后移动 , 移动的距离,由指针变量类型决定
| 指针-指针 | = 两个地址之间元素的个数
通常在同一块区域(如同一个数组)中,大指针-小指针
int my_strlen(char* start)
{
char* end = start;
while (*end != '\0')
{
end++;
}
return end - start;
}
int main()
{
char str[] = "bacon";
int len = my_strlen(str);
printf("%d\n", len);
return 0;
}
此处就使用了指针-指针,返回了字符串头尾地址之间的元素个数
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* p;
for (p = arr; p < &arr[5];p++ )//指针的关系运算
{
*p = 0;
}
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
}
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,
但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
对于 arr[10]
可以用指针和 " arr[11] " 比 ; 不能和 " arr[0-1] "
但是大多数编译器也能跑过去
野指针:指向的位置不可知的指针
#include
int main()
{
int* p;//局部变量指针未初始化,默认为随机值
*p = 20;//不知道哪位幸运观众被赋了个20
return 0;
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* parr = arr;
int i = 0;
for (i = 0; i <= 5; i++)//这里循环了6次哦
{
//第六次循环,parr = &arr[6]
//越界访问,指向的地方已经不明了
*(parr) = 0;
parr++;
}
return 0;
}
3.指针指向的空间释放了
int* Fun()
{
int a = 0;
return &a;//返回了a的地址后,a就销毁了
}
int main()
{
int* p = Fun();//p到底指向谁?
return 0;
}
int main()
{
int* p = NULL;
int a = 10;
p = &a;
if (p != NULL)
{
*p = 20;
}
return 0;
}
前面说过,数组名就是数组首元素地址(两种例外),那引用数组的第n个元素到底是怎么实现的呢?
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9 };
int* parr = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("arr[%d]=%p -------> *(parr+%d)=%p\n", i, &(arr[i]), i,parr+i);
}
return 0;
}
结果:
arr[0]=0051F85C -------> *(parr+0)=0051F85C
arr[1]=0051F860 -------> *(parr+1)=0051F860
arr[2]=0051F864 -------> *(parr+2)=0051F864
arr[3]=0051F868 -------> *(parr+3)=0051F868
arr[4]=0051F86C -------> *(parr+4)=0051F86C
arr[5]=0051F870 -------> *(parr+5)=0051F870
arr[6]=0051F874 -------> *(parr+6)=0051F874
arr[7]=0051F878 -------> *(parr+7)=0051F878
arr[8]=0051F87C -------> *(parr+8)=0051F87C
arr[9]=0051F880 -------> *(parr+9)=0051F880
这里可以看到:arr [ i ] == *( parr + i )
我是这么理解的:一级指针,穿梭一次就能找到地儿;二级指针要穿梭两次
二级指针:指向一级指针的指针变量,存放着一级指针的地址
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;
printf("&pa = %p <----> ppa = %p\n", &pa, ppa);//应该是pa的地址
printf("&a = %p\npa = %p\nppa = %p\n", &a , &*(pa) , &**(ppa) );
return 0;
}
结果:
&pa = 02D6F9F4 <----> ppa = 02D6F9F4
&a = 02D6FA00
pa = 02D6FA00
ppa = 02D6FA00
指针是 定语 , 数组是 主语 ,所以
指针数组:存放指针变量的数组
int main()
{
int num[5] = { 1,2,3,4,5 };
int* p_num1 = &num[0];
int* p_num2 = &num[1];
int* p_num3 = &num[2];
int* p_num4 = &num[3];
int* p_num5 = &num[4];
//指针数组 p_num 存放了
//p_num1, p_num2, p_num3, p_num4, p_num5 5个指针
int* p_num[5] = { p_num1, p_num2, p_num3, p_num4, p_num5 };
int i = 0;
int j = 0;
for (i = 5; i > 0; i--)
{
*(p_num[j]) = i;
j++;
}
for (i = 0; i < 5; i++)
{
printf("%d ", num[i]);
}
return 0;
}
结果:
5 4 3 2 1
这就是初阶指针的内容啦,我觉得 “穿梭机” 这个概念很不错 ^ ^
培根的blog , 与你共同进步!