深入理解指针(1)
目录
1.内存和地址
1.1 内存
1.2 究竟该如何理解编址
2 指针变量和地址
2.1 取地址操作符(&)
2.2指针变量和解引⽤操作符(*)
2.2.1 指针变量
2.2.2 如何拆解指针类型
2.2.3 解引用操作符
2.3 指针变量的⼤⼩
3 指针变量类型的意义
3.1指针的解引⽤
3.2指针+-整数
3.3 void* 指针
4 指针运算
4.1 指针+- 整数
4.2 指针 - 指针
4.3 指针的关系运算
1.内存和地址
1.1 内存
我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存是 8GB/16GB/32GB 等,
其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节。
计算机中常⻅的单位(补充):
bit - ⽐特位Byte - 字节KBMBGBTBPB
1B yte = 8b it1 KB = 1024B yte1 MB = 1024 KB1 GB = 1024 MB1 TB = 1024 GB1 PB = 1024 TB
在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起了新的名字叫:指针。
1.2 究竟该如何理解编址
CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,⽽因为内存中字节很多,所以需要给内存进⾏编址(就
计算机中的编址,并不是把每个字节的地址记录下来,⽽是通过硬件设计完成的。
⾸先,必须理解,计算机内是有很多的硬件单元,⽽硬件单元是要互相协同⼯作的。所谓的协同,⾄少相互之间要能够进⾏数据传递。
但是硬件与硬件之间是互相独⽴的,那么如何通信呢?答案很简单,⽤"线"连起来。
⽽CPU和内存之间也是有⼤量的数据交互的,所以,两者必须也⽤线连起来。
不过,我们今天关⼼⼀组线,叫做 地址总线。
硬件编址也是如此
我们可以简单理解,32位机器有32根地址总线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含义,每⼀种含义都代表⼀个地址。
地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传⼊CPU内寄存器。
2 指针变量和地址
2.1 取地址操作符(&)
int a = 10;
每个字节都有它自己的地址
0x00000068FB53F9E4&a取出的是a所占4个字节中地址较⼩的字节的地址。
2.2指针变量和解引⽤操作符(*)
2.2.1 指针变量
2.2.2 如何拆解指针类型
指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。
取出a的地址并存储到指针变量p中
例子:
2.2.3 解引用操作符
& - 取地址操作符
* - 解引用操作符(间接访问操作符)
2.3 指针变量的⼤⼩
int a = 10;
int * pa = &a; //0x0012ff40
pa是指针变量,指针变量也是变量,也是需要向内存申请空间的
pa这个指针变量的大小是多少?向内存申请了多大空间?
地址的存储需要多大空间,指针变量的大小就是多大
32位的机器上,地址是通过32根地址线传递的
那么一个地址就是32个0/1的二进制,需要32个bit位存储 = 4个字节
指针变量的大小就是4个字节
64位的机器上,地址是通过64根地址线传递的
那么一个地址就是64个0/1的二进制,需要64个bit位存储 = 8个字节
指针变量的大小就是8个字节
int main()
{
char* pc;
printf("%zu\n", sizeof(pc));
printf("%zu\n", sizeof(char*));
printf("%zu\n", sizeof(int*));
printf("%zu\n", sizeof(short*));
printf("%zu\n", sizeof(long*));
printf("%zu\n", sizeof(long long*));
printf("%zu\n", sizeof(float*));
printf("%zu\n", sizeof(double*));return 0;
}
32位平台下地址是32个bit位,指针变量⼤⼩是4个字节64位平台下地址是64个bit位,指针变量⼤⼩是8个字节注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。
3 指针变量类型的意义
3.1指针的解引⽤
访问了4个字节
只访问了一个字节
访问了8个字节,非法访问内存
3.2指针+-整数
int main()
{
int a = 10;
int* pa = &a;
char* pc = &a;//int*
printf("pa = %p\n", pa);
printf("pa+1= %p\n", pa+1);//加了4个字节printf("pc = %p\n", pc);
printf("pc+1= %p\n", pc+1);//加了1个字节
return 0;
}
我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。指针可以+1,那也可以-1。
3.3 void* 指针
void*指针,无具体类型的指针
但不能直接进行指针的+-整数和解引用的运算
void* 类型的指针可以接收不同类型的地址,但是⽆法直接进⾏指针运算。
4 指针运算
指针+- 整数指针-指针指针的关系运算
4.1 指针+- 整数
整型数组
之前学过的
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
指针+- 整数
p+i 就是数组中下标为i元素的地址
*(p+i)就是下标为i的这个元素
反着打印
代码1:
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//拿到第一个元素的地址
int* p = &arr[0];
for (i = sz-1; i >= 0; i--)
{
printf("%d ", *(p + i));
}return 0;
}
反着打印
代码2:
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//拿到第一个元素的地址
int* p = &arr[sz-1];
for (i = 0; i < sz; i++)
{
printf("%d ", *p);
p--;
}return 0;
}
字符数组
之前学的
int main()
{
char arr[] = "hello world";
printf("%s", arr);
return 0;
}
int main()
{
char arr[] = "hello world";
//拿到第一个元素的地址
char* pc = &arr[0];
while (*pc != '\0')
{
printf("%c", *pc);
pc++;
}
return 0;
}
代码2:
int main()
{
char arr[] = "hello world";
char* pc = &arr[0];
while (*pc)
{
printf("%c", *pc);
pc++;
}
return 0;
}
4.2 指针 - 指针
|指针-指针|:得到的是两个指针之间的元素个数
前提:两个指针指向了同一块空间,否则不能相减
int main()
{
int arr[10] = { 0 };
printf("%lld\n", &arr[9] - &arr[0]);
printf("%lld\n", &arr[0] - &arr[9]);
return 0;
}
数组随着下标的增长,地址是由低到高变化的
相似
想一个函数,求字符串的长度
int main()
{
char arr[] = "abcdef";
printf("%zu\n", strlen(arr));
return 0;
}
size_t my_strlen(char* p)
{
size_t count = 0;//计数器
while (*p)
{
count++;
p++;
}
return count;
}int main()
{
char arr[] = "abcdef";
//数组名其实也是数组首元素的地址
//arr == &arr[0]
printf("%zu\n", my_strlen(arr));
return 0;
}
指针 - 指针
size_t my_strlen(char* p)
{
char* start = p;
while (*p) //使用p,来找到字符串中的\0
{
p++;
}
return p - start;
}int main()
{
char arr[] = "abcdef";
//数组名其实也是数组首元素的地址
//arr == &arr[0]printf("%zu\n", my_strlen(arr));
return 0;
}
4.3 指针的关系运算
利用指针的大小比较
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//打印数组的内容
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
while (p < arr + sz)//指针的大小比较
{
printf("%d ", *p);
p++;
}
return 0;
}