
🎉个人主页:缘三水的博客
❄专栏传送门:C语言专栏(新手向)
🎀人生格言:行动是迷茫的最好解药
🚀个人介绍:

文章目录
- 前言
- (一)内存和地址
-
- [1.1 内存](#1.1 内存)
- [1.2 编址](#1.2 编址)
- (二)指针变量和地址
-
- [2.1 取地址操作符&](#2.1 取地址操作符&)
- [2.2 指针变量](#2.2 指针变量)
- [2.3 解引用操作符](#2.3 解引用操作符)
- [2.4 指针变量的大小](#2.4 指针变量的大小)
- (三)指针变量类型的意义
-
- [3.1 解引用](#3.1 解引用)
- [3.2 指针+-整数](#3.2 指针+-整数)
- [3.3 void*指针](#3.3 void*指针)
- (四)指针运算
-
- [4.1 指针+-整数](#4.1 指针+-整数)
- [4.2 指针--指针](#4.2 指针--指针)
- [4.3 指针的关系运算](#4.3 指针的关系运算)
- 总结
前言
以下是本篇文章正文内容
(一)内存和地址
1.1 内存
计算机中内存被划分为一个一个的内存单元
每一个内存单元的大小是一个字节 ,也是八个比特
计算机中的内存单位
1Byte(字节) = 8bit(比特)
1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
每个内存单元都有编号
这种编号能帮助我们快速的在庞大的内存中找到对应的空间
这种类似生活中的房间门牌号,所以也称做地址
在C语言中,也叫做指针
总结:内存单元的编号 == 地址 == 指针
1.2 编址
有地址,那地址是如何编写的?
计算机中的编址并非一个一个的记录下来
而是通过硬件设计 ,在出厂的时候就固定下来了
类似乐器中的吉他,每一根弦发出的声音在乐器生产时就固定下来了
硬件编址的原理
依靠机器上的地址总线
32位的机器就有32根地址总线 每根总线有两种状态:0和1 ,0和1表示电脉冲的有无
因此1根地址总线可以表示2种含义,n根地址总线就能表示2的n次方 种含义
32根就是2的32次方种
每种含义代表一个地址
CPU与内存的交互CPU访问内存的空间需要地址,
地址就通过32根地址总线产生,
产生的地址信息传达给内存定位到对应的空间即可完成内存空间的访问
之后要用到对应空间内的数据,就通过数据总线 ,传回CPU
(二)指针变量和地址
2.1 取地址操作符&
c
int a = 0;
创建一个变量a就是在内存中申请一块空间
c
&a
&a是取得变量a最小 的地址
上面的a是整形,占据4个字节,那取地址&a取的就是a的空间内最小的地址

2.2 指针变量
指针变量 用来存储地址
示例
c
int a = 0;
int* pa = &a;//pa就是指针变量,存储a的地址
- 那 int * 怎么理解呢?
*是在说明pa是指针变量
int是在说明pa指向的是一个整形int的对象,也就是变量a
示例
c
char c = "yuansanshui";
char* pc = &c;
*在说明pc是指针变量
char在说明pc指向的是字符型char的对象,也就是变量c
2.3 解引用操作符
地址存储在指针变量中是为了我们后续使用的
那如何使用呢?
示例1
c
int a 10;
int* pa = &a;
*pa = 20;
printf("%d", a);//打印出的结果是20
这里用到了解引用操作 *
*pa就是通过pa存放的地址找到指向的对象,也就是变量a
所以,*pa = 20;就是将20赋给变量a
示例2
c
char c = "y";
char* pc = &c;
*pc = "s"
示例2是通过解引用指针变量,将变量c的内容改为"s"
2.4 指针变量的大小
一个int整型变量的大小是4个字节
那么一个指针变量的大小是多大呢?
在32位的机器中
由于指针变量是用来存放地址的,一个地址是32位二进制位,也就是32比特,也就是4字节,因此指针变量的大小在32位机器中就是4个字节
在64位机器中同理
指针变量的大小是8个字节
不同类型的指针变量的大小相同
这点可以利用sizeof证明
c
int main()
{
printf("%zd\n", sizeof(char *));
printf("%zd\n", sizeof(short *));
printf("%zd\n", sizeof(int *));
printf("%zd\n", sizeof(double *));
return 0;
}

(三)指针变量类型的意义
既然指针变量的大小与类型无关,那指针变量的类型有什么意义呢?
3.1 解引用
示例1
c
int main()
{
int n = 0x11223344;
int* pi = &n;
*pi = 0;
return 0;
}
调试-窗口-监视-内存
我们可以看到n的内容全部4个字节都改为了0

示例2
c
int main()
{
int n = 0x11223344;
char* pc = (char*)&n;
*pc = 0;
return 0;
}
可以看到只有第一个字节改为了0

从上面两个示例可以看出int类型的指针变量在解引用时就改变了4个字节
char 类型的指针变量在解引用是只改变了1个字节
于是我们可以得出
结论:指针的类型,决定了解引用时的权限(一次可以改变几个字节)
3.2 指针±整数
示例
c
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc + 1);
printf("%p\n", pi);
printf("%p\n", pi + 1);
return 0;
}

pc + 1 只是加了一个字节
pi + 1却加了四个字节
说明指针变量的类型会影响±整数的跨度
结论:指针变量类型决定了±整数的跨度有多大
3.3 void*指针
指针有一种特殊类型void*类型
是指无具体类型 的指针(泛型指针)
用处:可以接收任意类型的地址
一般用在函数参数部分,用来接收不同类型的地址
局限性:无法 进行解引用 和指针变量±整数的操作
示例1
c
int main()
{
int a = 10;
int* pa = &a;
char* pc = &a;
return 0;
}
将a的地址取出放在char类型的指针变量会报警告,两者类型不兼容
于是我们可以用void 类型来接收
示例2
c
int main()
{
int a = 10;
void* pa = &a;
void* pc = &a;
*pa = 10;
*pc = 0;
return 0;
}

虽然可以接收,但由于void*类型的指针变量无法进行指针运算
于是,程序报错
结论:void*可以接收任何类型,但无法进行指针运算
(四)指针运算
4.1 指针±整数
示例1 利用指针变量打印数组的全部内容
c
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%d ", *(pa + i));
}
return 0;
}

根据数组的内容在内存中是连续存放 的,因此只需要得到第一个内容arr[0]的地址,就能利用指针变量±整数,一个一个的得到数组后的内容
而示例1的pa是不变的,i在变
还有一种写法也能达到同样效果
示例2
c
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%d ", *pa);
pa++;
}
return 0;
}
这个写法的指针变量pa在变,结果相同

示例3
同样的,也可以打印字符数组

原理是
使用指针变量取第一个字符的地址,
利用while循环判断指针变量解引用后是否为\0,
是就停止打印
不是就继续打印直到遇到\0停止
4.2 指针--指针
指针-指针的前提:两个指针指向了同一块空间
示例
c
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%lld", &arr[9] - &arr[0]);
printf("%lld", &arr[0] - &arr[9]);
return 0;
}


结论:|指针-指针|绝对值,得出的结果是两个指针之间的元素个数
问题 自己撰写一个函数,要求利用指针求出字符串的长度
示例1
原本C语言直接提供了strlen库函数 来求出字符串\0前的字符个数
c
#include <string.h>//strlen对应的头文件
int main()
{
printf("%zu",strlen("abcdef"));//strlen返回值用%zu打印
return 0;
}
示例2
那我们如何用指针来写这个函数呢?
方法一,指针±整数
c
size_t my_strlen(char* p)//2.用指针变量接收arr首元素的地址
{
size_t count = 0;
while (*p)
{
count++;
p++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
printf("%zu", my_strlen(arr));//1.arr其实就是首元素的地址
return 0;
}
arr等价于arr数组内首元素的地址
所以用一个类型为char 的指针变量p接收首元素的地址
由于长度大于等于0 ,是 无符号整型 ,因此创建一个size_t类型的 计数器 count
用while循环判断指针变量 p是否为\0
不是,计数器加1
是,循环停止,返回计数器的值
方法二,指针-指针
c
size_t my_strlen(char* p)
{
char* start = p;//指针变量start存放p也就是首元素的地址
while (*p)
{
p++;
}//循环结束,p此时的地址为\0的地址
return p - start;//放回的就是\0与首元素之间的元素个数
}
int main()
{
char arr[] = "abcdef";
printf("%zu", my_strlen(arr));
return 0;
}
4.3 指针的关系运算
指针是有大小的,有大小就能进行比较,指针的大小比较即为指针的关系运算
问题 利用指针关系运算打印数组内容
c
int main()
{
int arr[] = { 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;
}

地址比数组后面一个的地址还小,则循环继续,打印继续;
直到等于数组后面一个地址,循环停止,不再打印
总结
这篇文章我们详细介绍了指针部分内容,希望能对你有帮助
有错误的地方希望可以得到大佬的指正
最后不要吝啬你的点赞,收藏和评论!!!
感谢你的观看
