网站备案身份核验,推广型网站建设机构,win系统的wordpress,黄金路网站建设公司【C语言】—— 指针一 #xff1a; 初识指针#xff08;上#xff09; 一、内存和地址1.1、如何理解内存和地址1.2、 如何理解编址 二、指针变量和地址2.1、取地址操作符 2.2、 指针变量2.3、 解引用操作符 ∗ * ∗2.4、指针变量的大小 三、指针变量类型的意义3.1、 指… 【C语言】—— 指针一 初识指针上 一、内存和地址1.1、如何理解内存和地址1.2、 如何理解编址 二、指针变量和地址2.1、取地址操作符 2.2、 指针变量2.3、 解引用操作符 ∗ * ∗2.4、指针变量的大小 三、指针变量类型的意义3.1、 指针的解引用3.2、 指针 - 整数3.3、 v o i d void void * 指针 四、指针运算5.1、指针 - 整数5.2、指针 -- 指针5.3、指针关系的运算 一、内存和地址
1.1、如何理解内存和地址 在讲内存和地址之前我想给大家讲一个生活中的案例。 假设我们要找好朋友他只告诉你在哪栋楼但那栋楼没有门牌号。这时你想找到他只能挨家挨户地去寻找这样效率无疑会很低而且其他住户也会有意见。 因此为了方便查找与管理楼栋都会给每个房间上一个门牌号有了门牌号就能快速定位朋友所在的房间。 在生活中我们给每个房间一个房间号提高效率那如果对照计算器中又会是怎样的呢 CPU中央处理器在处理数据的时候需要的数据是从内存中读取的处理后的数据也会放在内存中。我们知道买电脑时电脑内存有 8GB / 16GB / 32GB那么电脑是如何管理这些内存的呢 其实计算机也是把内存分为一个个内存单元每个内存单元大小取一个字节。每个内存单元有一个编号 (这个编号相当于房间的门牌号有了这个内存单元的编号CPU就可以快速访问一个内存空间。 生活中我们把门牌号叫做地址在计算机中我们把内存单元的编号也称为地址。 C语言中给地址起了个新名字指针 我们可以这样理解 内存单元的编号 地址 指针 1.2、 如何理解编址 CPU访问内存中的某个字节空间必须知道这个字节空间在内存的什么位置⽽因为内存中字节很多所以需要给内存进⾏编址。 计算机中的编址并不是把计算机每个字节的地址记录下来而是通过硬件设计来完成。 首先我们要知道计算机内有许多硬件单元硬件单元的相互协作需要不断进行数据传递。 那么问题来了硬件与硬件之间是相互独立的那么他们是如何交流信息的呢答案其实并不神秘那就是用线相连。 CPU与内存之间也有大量的数据交互他两也必须用线连起来。 今天我们关心的一组线叫地址总线。 我们可以简单理解32位机器有32根地址总线每根线只有两种状态0 或 1 【电脉冲的有无】。所以一根线只有两种状态只能表示两种含义而 32 根线就能表示 2 32 2^{32} 232 中含义每一种含义都代表着一个地址。 地址信息被下达给内存在内存上就可以找到该地址所对应的数据而数据通过数据总线传入CPU内寄存器。 二、指针变量和地址
2.1、取地址操作符 在C语言中创建一个变量本质上就是向内存申请空间 例如 上述代码就是为 a a a 创建了一个 4 个字节大小的内存空间里面存放的是1016进制表示 四个地址分别是 0x00AFF8300x00AFF8310x00AFF8320x00AFF833 而我们想要得到 a a a 的地址就需要用到取地址操作符 学习前面的知识我们知道每个字节都有一个地址而整型变量 a a a 一共申请了 4 个字节那操作符取出的地址是哪一个呢
#includestdio.hint main()
{int a 10;//为a申请4个字节的空间a;//取出a的地址printf(%p\n, a);return 0;
}由于编译器每一次运行给变量分配的地址都不同这里我就不打印了 a a a 取出的是 a a a 所占 4 个字节中最小的地址 按上面的例子即0x00AFF830
2.2、 指针变量 我们用取地址操作符取出变量的地址把他放在哪呢不存储起来以后想要使用怎么办呢地址其实也只是一个数值而已如地址0x0012FF40总不能让他无家可归吧。 就像上述例子中的 10创建了整型变量 a a a 来存放C语言中也提供了专门的变量类型来存放指针那就是指针变量
#includestdio.hint main()
{int a 10;//为a申请4个字节的空间int* pa a;//取出a的地址放在指针变量p中printf(%p\n, pa);return 0;
}上述代码中 p a pa pa 就是一个指针变量他的类型是 int* 我们该如何理解int*呢 ∗ * ∗ 表示他是指针变量 i n t int int 表示他所存放的地址所指向的类型为 i n t int int 类型 而同理指针变量还有其他类型 l o n g ∗ long* long∗ 指向 l o n g long long 类型 c h a r ∗ char* char∗ 指向 c h a r char char 类型 s h o r t ∗ short* short∗ 指向 s h o r t short short 类型 … … … … … … 2.3、 解引用操作符 ∗ * ∗ 我们用取地址操作符把变量的地址取出来有什么用呢那自然是为了方便未来要找到他。 我们只要拿到了地址指针就能通过地址指针找到他所指向的对象而这个过程就需要用到解引用操作符*。
#includestdio.hint mian()
{int a 0;int* pa a;*pa 100;return 0;
}上述代码中*pa 100;就运用了解引用操作符* pa 的意思是通过pa存放的地址找到其指向的空间那么*pa其实就是变量 a 了使用*pa 100;就是把 a a a 变成了 100. 2.4、指针变量的大小 前面的学习中我们了解到32位的机器设有32根地址总线每根线只有两种状态即有电流通过和无电流通过逻辑上表示 0 和 1 这与二进制序列的表示相符。我们把32位的地址总线产生的二进制序列看成一个地址那么这个地址一共有32个 b i t bit bit 位即 4 个字节才能存储。 同理64位的机器存储地址需要 8 个字节的空间。 32位平台下地址是32个 b i t bit bit 位指针变量大小是 4 个字节64位平台下地址是64个 b i t bit bit 位指针变量大小是 8 个字节 注意指针变量的大小与类型无关只要他是指针变量他的大小就是 4 字节或 8 字节。
三、指针变量类型的意义
3.1、 指针的解引用 我们得知指针变量的大小和类型无关那么为什么还要有各种各样的指针类型呢 C语言当然不可能无缘无故创造这么多类型他们都是有意义的让我们一起往下看。 下面我们来看看两段代码
#includestdio.hint main()
{int n 0x11223344;int* pi n;*pi 0;return 0;
}#includestdio.hint main()
{int n 0x11223344;char* pc (char*)n;*pc 0;return 0;
}从调试我们可以看到代码 1 会将 n n n 的 4 个字节全部改为 0 而代码 2 只是将 n n n 的第一个字节改为 0。 指针的类型决定了对指针解引用时指针有多大的访问权限一次能操作几个字节 例如 c h a r char char* 只能访问 1 个字节 i n t int int* 能访问 4 个字节
3.2、 指针 ± 整数 让我们先来看一段代码观察他们的地址变化
#includestdio.hint main()
{int n 10;char* pc (char*)n;int* pi n;printf(n %p\n, n);printf(pc %p\n, pc);printf(pc1 %p\n, pc 1);printf(pi %p\n, pi);printf(pi1 %p\n, pi 1);return 0;
}运行结果 我们可以看到 c h a r char char* 类型 1 跳过了 1 个字节 i n t int int* 类型 1 跳过了 4 个字节。 这是因为 c h a r char char * 指针类型所指向的 c h a r char char 类型大小是 1 个字节 同理 i n t int int * 所指向的 i n t int int 类型大小是 4 个字节 指针的类型决定了指针向前或向后走一步有多大 3.3、 v o i d void void * 指针 在所有指针类型中还有一种特殊的指针 v o i d void void * 指针。我们知道 v o i d void void 用在函数的返回类型和参数中时表示无返回值、无参数那么 v o i d void void * 指针又表示什么呢 void * 可以理解为无具体类型的指针或者叫泛型指针它可以接收任意类型的地址 但是它也有局限性void *类型指针不能完成上面的解引用和指针 - 整数的运算因为 void *类型指针不知道访问权限一次应操作几个字节想完成上述运算只能先强制类型转换为其他指针类型。 举例
#includestdio,hint main()
{int a 10;int* pi a;char* pc a;return 0;
}我们可以看到将一个 i n t int int 类型的变量地址赋值给一个 c h a r char char * 类型的指针变量。编译器给出了警告因为类型不兼容要想消除这个警告除了强制类型转换成 c h a r char char * 类型还可以用 v o i d void void *类型接收。 如下
#includestdio.hint main()
{int a 10;void* pi a;void* pc a;*pi 10;*pc 0;return 0;
}VS编译器运行结果 我们可以看到虽然用 v o i d void void * 类型接收编译器并没有报警告但是却无法解引用编译器直接报错那么 v o i d void void * 类型有什么用呢 一般 v o i d void void * 类型用在函数参数部分用来接收未知类型数据的地址的这样设计可以达到泛型编程的效果。使得一个函数可以处理多种类型的数据。 四、指针运算 指针的基本运算有三种分别是 指针 ± 整数指针 - 指针指针的关系运算 5.1、指针 ± 整数 在 C语言——详解数组 一文中我曾经提到过数组在内存中是连续存放的。因此对于数组来说我们只要找到了他的首元素地址那他剩下元素的地址也能顺藤摸瓜找到。
#includestdio.h
//指针 - 整数
int main()
{int arr[9] { 1,2,3,4,5,6,7,8,9 };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;
}注用指针访问数组的前提条件数组在内存中是连续存放的 指针 ± 整数公式 p n p n pn − - − 跳过 n s i z e o f ( t y p e ) n sizeof(type) nsizeof(type) 个字节 例如 int a 10;
int* p a;则 p 1 p 1 p1 跳过4 个字节 1 ∗ s i z e o f t y p e 1 * sizeoftype 1∗sizeoftype 5.2、指针 – 指针 指针 – 指针是什么意思呢我们可以类比生活中的例子 上面的例子指针 ± 正数可以类比日期 ± 天数 同理指针 – 指针也可类比成日期 – 日期 看下面这段代码
#includestdio.hint my_strlen(char* s)
{char* p s;while (*p ! \0)p;return p - s;
}int main()
{printf(%d\n, my_strlen(abc));return 0;
}这里我们模拟实现了一个简陋的 s t r l e n strlen strlen 函数 函数来求字符串长度代码逻辑是传入字符串首元素的地址指针然后找到字符串最后一个元素的地址指针两个指针相减得到的是中间的元素格式即整个字符串的长度啦。 但需要注意的是
指针能减指针但指针不能指针你听说法日期加日期的吗指针也是如此。可以低地址 – 高地址只不过得到的是负数指针 – 指针的前提条件一定是两个指针指向的是同一块空间 例如
#includestdio.hint main()
{int arr[10] { 0 };char ch[5] { 0 };printf(%d\n, ch[4] - arr[6]);return 0;
}上述代码是错误的代码因为两个指针并没有指向同一块空间。
5.3、指针关系的运算 指针关系运算即地址指针与地址指针比较大小
请看代码
#includestdio.hint 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;}
}该代码中循环条件判断中就运用了指针关系的运算以确保对数组的访问不越界。 好啦本期关于指针就介绍到这里啦希望本期博客能对你有所帮助同时如果有错误的地方请多多指正让我们在C语言的学习路上一起进步