- 本篇会加入个人的所谓‘鱼式疯言’
- ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言,而是理解过并总结出来通俗易懂的大白话,我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的,可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念。
前言
在本篇文章中,小编将带大家进入指针的世界,人人都说指针难,那不妨看看小编的指针解读之后,看看会不会有什么不一样呢?
我们将学习到:
- 指针变量和地址
- 指针运算和泛型指针(void*)
- const修饰指针的内容.
❤️❤️❤️下面,下面,下面 ! ! !
💖💖💖我们开始吧吧吧 ! ! !
一.指针变量和地址
在讲指针变量之前,下编认为有必要让大家知道一旦,C语言的指针和指针变量是不一样,在理解这个之前我们就要知道内存这个玩意
1.内存
举个生活的例子:
假设有⼀栋宿舍楼,把你放在楼⾥,楼上有100个房间,但是房间没有编号,你的⼀个朋友来找你玩,如果想找到你,就得挨个房⼦去找,这样效率很低,但是我们如果根据楼层和楼层的房间的情况,给每个房间编上号,有了房间号,如果你的朋友得到房间号,就可以快速的找房间,找到你。
所以生活中我们有了房间号就可以很轻松的找到房间.
那么我们如果需要读取数据呢?我们就需要找到对应的地址,当我们需要存储数据时,就会放入对应的地址,而我们所放的这些区域就叫做内存.
<1>.内存大小
通常我们把每一个内存单元记住一个字节(byte)
而内存最小的单位是比特(bit).
字节继续往上走就是我们的千字节(kb),兆字节(MB)…
换算单位如下(💕宝子们签收下哈💕):
1byte = 8bit
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB= 1024TB
2.指针变量,指针和地址的异同
上面我们理解了内存和指针的关系,我们在回到C语言,在C语言创建变量其实就是向内存申请空间
#include <stdio.h>
int main()
{
int a = 10;
return 0;
}
上述代码中我们在内存中申请了4个字节,用来存放整数10,上图中4个字节的地址分别是:
0x006FFD70
0x006FFD71
0x006FFD72
0x006FFD73
<1>.取地址符(&)和解应用操作符(*)
鱼式疯言
在我们C语言中,指针就是地址,地址就是指针,只是我们生活中总是把指针变量说成指针.
那我们如何能得到a的地址呢? 这⾥就得学习⼀个操作符(&)-取地址操作符
#include <stdio.h>
int main()
{
int a = 10;
int *pa = &a;//取出a的地址
printf("%p\n", &a);
printf("%p\n", pa);
return 0;
}
按照我画图的例⼦,会打印处理:006FFD70
&a 取出的是 a 所占 4 个字节中地址较⼩的字节的地址。
指针变量的基本形式:
int *pa=&a;
&a 含义就是取出 a 的地址放入的指针类型的变量 p 中
怎么理解呢?
请看鱼式疯言:
鱼式疯言
int 代表的是原变量a的数据类型为 int,而 *pa 的含义就是告诉我们 pa是指针变量,是用来存放a的地址的
而我们访问的多少字节呢,是取决于我们是 int * ,那我们就访问 (读取)就能···· 还是short * 等等这样不同类型的大小。这点小编在下面也会重点突出说明指针类型不同的意义
下面我们看看解引用 (*)
#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;
printf("%d\n", *pa);
*pa = 20;
printf("%d\n", a);
}
看到效果了吧,
*pa 的本质就是通过 pa 所指向 a 的地址中来访问变量 a
并对其数据进行修改(😁下面小编也会讲解不能修改的情况😁).
<2>.指针变量的大小
鱼式疯言
指针变量也有自己的类型的哦,比如 int char double …
而这些没有具体变量名但和我们平常用的 ( int * ) ( char * ) ( double * ) 相似的,我们就把他们称为
指针变量类型.
#include<stdio.h>
int main()
{
printf("%zd\n", sizeof(char*));
printf("%zd\n", sizeof(short*));
printf("%zd\n", sizeof(int*));
printf("%zd\n", sizeof(double*));
return 0;
}
这时就有小伙伴问了:
🤔🤔🤔这里为什么会有两种不同的运行结果 ,而且不同的指针数据类型还内存大小相同 ???
其实啊 !!!
在我们C语言规定:
我们指针变量的大小与类型种类无关,与当时所处的环境有关
当编译器为 X86 环境时,我们指针大小为 4 个字节
当编译器为 X64 的环境时,我们指针大小为 8 个字节.
那么爱提问的小爱同学就有疑惑了 🤔 🤔 🤔
竟然我们的不同的指针类型的大小在相同环境都是相同的。
为什么我们要分那么多指针变量的类型呢 ? ? ?
这个问题问的好啊,小编在下面会 一 一 为友友们解答,
友友们先别着急哦 💖 💖 💖
二.指针运算
指针的基本运算有三种,分别是:
• 指针± 整数
• 指针-指针
• 指针的关系运算
1.指针±整数
因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素。
#include<stdio.h>
//指针+-整数
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));//p+i 这⾥就是指针+整数
}
return 0;
}
指针加减整数的本质:
指针以指针变量类型为单位进行跳跃
什么意思呢?
et:
p 为int *类型指针,p+1就会跳过int大小的四个字节的地址,可能有0x1122->0x1126
鱼式疯言
虽然指针大小与指针类型无关
但是指针进行加减整数时
出现多少个字节是与指针类型密切相关的
- char * +1 就跳过一个字节, short * +1 就跳过2个字节…
- 指针的解引用访问也是如此,当我们访问时 char * 大小的指针就会只访问 一 个字节,int *就会访问 4 个字节
这就是为什么我们指针类型不同的原因了 💖 💖 💖
友友们请看下面这段代码
下面这段底代码就有力的证明了小编的总结是否正确 💕 💕 💕
#include <stdio.h>
int main()
{
int a = 20;
//取出 a 的地址 放在一个短整型指针中
short* pa = (short*) & a;
int b = 0x44332211;
//取出 b 的地址分别放在一个 整型指针 和 字符指针 中
int* pib = &b;
char* pcb = (char*) & b;
//观察各自的变化
printf("pa = %p\n", pa );
printf("pa+1 = %p\n\n", pa + 1);
printf("*pib = %x\n", *pib);
printf("*pcb = %x\n", *pcb);
return 0;
}
2.指针-指针
//指针-指针
#include <stdio.h>
size_t my_strlen(char* s)
// size_t <=> 无符号整型 unsigned
{
//先记住首元素的地址
char* p = s;
while (*p != '\0')
p++;
//当一个块空间的指针相减时
//最终返回的是元素个数
return p - s;
}
int main()
{
printf("%zd\n", my_strlen("abc"));
return 0;
}
指针减去指针得到的两个指针变量的之间元素个数的绝对值.
🤔🤔🤔啊 啊 啊!!!
😲😲😲为什么是绝对值呢???
因为啊,咱们内存地址的是有高低之分的
高地址-低地址得正,
低地址减高地址得负
.
元素个数肯定为正的
所以我们要取绝对值😊
鱼式疯言
两个指针变量相减时,一定要注意是指向同一块内存才可以,什么意思呢?
比如我创建了 arr1 数组和 arr2 数组,我只能单独在 arr1 或者 arr2 数组中取用指针相减.,
不可以同时取 arr1 的地址又取 arr2 的地址然后相减,得到的是 只能是随机值.
小编另外还想说一点的是,在C语言中是存储指针详相减的,但不存在指针相加的情况,不然很有可能会出现越界访问的情况
3.指针的关系运算
//指针的关系运算
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
while (p < arr + sz) //指针的⼤⼩⽐较
{
printf("%d ", *p);
p++;
}
return 0;
}
前面我们说到了指针是有高低地址之分的,所以我们就可以根据高低地址的大小来判断不等号是否成立。
4.泛型指针(void*)
在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为⽆具体类型的指针。(或者叫泛型指针)
这种类型的指针可以⽤来接受任意类型地址。但是也有局限性。
void* 类型的指针不能直接进 ⾏ 指针的± 整数和解引⽤的运算。
#include <stdio.h>
int main()
{
int a = 10;
void* pa = &a;
void* pc = &a;
*pa = 10;
*pc = 0;
return 0;
}
友友们都看到了吧,咱们的泛型指针 (void*) 是可以用来接受但是是不可以用来解引用的。
#include <stdio.h>
int main()
{
int a = 10;
void* pa = &a;
printf("%p", pa + 1);
return 0;
}
😲友友们都看到了吧😲,咱们的泛型指针(void*)也是不可以用来指针加减的。
鱼式疯言
当我们需要用泛型指针时,那我们该怎么办呢?
这时我们就需要用到我们前面所讲的一个内容了,我们可以对泛型指针进行强制类型转化成我们想要的指针类型
et:下面示例:
#include <stdio.h>
int main()
{
int a = 10;
void* pa = &a;
printf("%p\n", pa);
printf("%p", (char*)pa + 1);
return 0;
}
三 const修饰的指针
1.const 的普通用法
变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。
但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?
❤️💕接下来就要请我我们的专属嘉宾 const 来帮忙了!!!
const修饰普通常量:
#include <stdio.h>
int main()
{
int m = 0;
m = 20;//m是可以修改的
const int n = 0;
n = 20;//n是不能被修改的
return 0;
}
这里我们看到cosnt修饰普通变量是不可更改的。
2.const 修饰指针变量
<1>. 情况一
#include <stdio.h>
int main()
{
const int n = 0;
printf("n = %d\n", n);
const int* p = &n;
*p = 20;
printf("n = %d\n", n);
}
如果我们直接修饰 指针变量p,我们无法通过p拥有的a地址来篡改n的数据。编译器就会报错。
那如果是下面这种呢?
<2>. 情况二
#include <stdio.h>
int main()
{
const int n = 0;
printf("n = %d\n", n);**
int* const p = &n;
*p = 20;
printf("n = %d\n", n);
}
上面我们看到了,这里的const我们修饰的是指针 p 本身和和变量 n,
😵 😵 😵 这时小伙伴们是不是又连发呢 ??????
那么让小编来解释下吧!
前面 cosnt 修饰n我们是没问题的
那么这边我们可以 const 修饰 p 怎么解释呢?
const 修饰 p 指的是修饰 p 自身这个变量的地址,const 修饰的不再是 *p 而是 p
而我们需要用到 *p来访问 n 数据 来修改 n 数据,就再也不能限制指向我们的 n 的指针了,那么也不能通过锁定我们的 n 的地址来修改 n 的数值
但值得注意的是,我们的 p 自身是无法再再修改了
鱼式疯言
我们修改数据的时候可以通过两个方面修改:
1.通过本身变量名赋值修改
2.通过指向该变量在的地址进行修改。
总结
在本篇博文中,我们主要就讲解了指针的三个点
1.指针变量和地址中,内存是什么以及其大小转化关系,指针和地址的区别 ,& 和 * 的运用。
2.我们还说明了,指针可以加上或减去一个整数,指针之间可以想减,指针可以关系判断,
还有一个比较特殊的指针类型 void* 的解释
3最后我们提了一嘴我们的关键字 const 的在普通变量和指针变量的用法。
小伙伴们有没有收获到呢?💕💕💕💕
💖💖💖本次博文就到这里了,感觉各位小伙伴的赏脸品读小编写的拙作哦,如果觉得小编写的还不错的咱可支持三关下,不妥当的咱评论区指正,希望我的文章能给各位家人们带来哪怕一点点的收获就是小编创作的最大动力💖💖💖