创作不易,希望大家点点小赞和关注。接下来让我带领大家开始进行C语言难题(指针)的学习,让我们努力学习,向着美好的生活,跑步前进吧~
指针的简介
首先我们需要了解指针是什么?
我为大家总结一下两个要点来理解指针:
1. 指针是内存中一个最小单元的编号,也就是地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
通俗的说:指针就是地址,但是我们经常说的指针通常指的是指针变量。
一、内存和地址
我们把内存划分为一个个的内存单元,每个内存单元的大小取1个字节(bit)。计算机能够识别、存储、运算的时候都是二进制的。
计算机中的常见单位如下:
1byte | 8bit |
1kb | 1024byte |
1MB | 1024KB |
1GB | 1024MB |
1TB | 1024GB |
1PB | 1024TB |
C语言中把地址起了新的称号:指针。
我们可以理解为,每个内存单元,相当于一个教学楼,一个byte空间里面能放8个bit,就如同一个教室里有八个同学,每个同学是一个bit。
每个内存单元也都有一个编号(这个编号就相当于我们平时教学楼的门牌号),有了这个内存单元的编号,CPU就可以快速找到一个内存空间。
我们可以理解为:
内存单元的编号==地址==指针
所以我们也可以这样理解:
一个字节 | 0xFFFFFFFF |
一个字节 | 0xFFFFFFFF |
一个字节 | 0xFFFFFFFF |
......... | ......... |
......... | ......... |
......... | ......... |
......... | ......... |
一个字节 | 0x00000002 |
一个字节 | 0x00000001 |
一个字节 | 0x00000000 |
而CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址,计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的
计算机内有很多的硬件单元,硬件单元之间相互协调。它们是通过线来建立联系的。
32位 -------------- 32根地址线 每根只有两态表示
64位 -------------- 64根地址线 (0,1)【电脉冲有无】
其中2根线4种含义 32根则有2的32次方中含义(而每一种含义就是一个地址)。地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。
二、指针变量
1、取地址操作符 &
我们可以通过&(取地址操作符)取出变量的内存起始地址,把地址可以存放到一个变量中,这个 变量就是指针变量。
int a = 10;
int* p = &a; //这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量中,p就是一个之指针变量
&a取4个字节中的第一个字节的地址,地址较小的字节。
补充:我们打印地址使用%p进行打印,是十六进制打印的。并且前面有一个0,我们可以使用%x进行地址打印,前面则没有0.
2、指针变量和解引用操作符 *
那么整形指针: int * pint; 能够指向整形数据的指针。浮点型指针则是: float * pf; 能够指向浮点型数据的指针。那么这里的*是什么意思呢?
int main()
{
int a= 0;
int* p = &a;
return 0;
}
这个*是用来说明变量p是一个指针变量。当p是一个指针变量时,在其前面加上一个*,这个*表示解引用操作符。
我们先来看这行代码:int* p = &a;就是创建一个变量p,使他的值等于p指向的空间里的内容,也就是a的值,这时,a的值就是0。这里的*p,用的是p指向的内容中的值。
这里我们通常称之为对p这个指针变量所指向的地址进行解引用,就是*p就等于它所指向的地址所表达的这个内容(这里的*p这是表示a的值也就是0),小编通常称*p为:解引用p(地址所指向地址的内容)。
举例如下:
int main()
{
int a = 20;
int* p = &a;
printf("%d\n", *p); //20
printf("%p\n", &a); //0000002010B2F9E4
printf("%p\n", p); //0000002010B2F9E4
return 0;
}
3、指针变量的大小
我们知道P(指针)也是变量,也是需要向内存申请一块空间的,这样才能存放地址。
那么P空间大小是多少?
答:在32平台上是4个字节,在64平台上是8个字节,这里的空间大小与P的类型无关。
指针变量大小是多少?
答:指针变量需要多大的空间,取决与存放的地址,地址的存放需要多大的空间,指针变量的大小就是多大。所以指针类型决定了指针进行解引用操作的时候,访问多大的空间,例如:int* 的指针解引用访问4个字节;char*的指针解引用访问的就是1个字节。
总结就是:指针的内省决定了,对指针解引用时有多大的权限(一次能操作几个字符)。
char* 一个字节;int* 4个字节;short* 2个字节。
三、指针+ - 整数
在指针的+-整数中,指针类型决定了指针的步长,也就是向前或者向后走一步具体有多大的距离。我们来观察下面的代码。
#include<stdio.h>
int main()
{
int n = 0;
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;
}
运行结果如下:
int main()
{
int arr[5] = { 1,2,4,5,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[0];
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
我们知道数组在内存中是连续存放的,我们可以通过地址找到了数组的首地址。也就是指针指向了这个数组的首地址。通过循换i=0的时候,指向数组的首地址通过解引用来打印数组的第一个元素并且以此类推。
四、void*指针
在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为⽆具体类型的指针(或者叫泛型指 针),这种类型的指针可以⽤来接受任意类型地址。
但是void*指针也有局限性, void* 类型的指针不能直接进⾏指针的+-整数和解引⽤的运算。举例说明:
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
char* pc = &a;
return 0;
}
![c15705b5d1b84983af703d7eec37c9b0.png](https://img-blog.csdnimg.cn/direct/c15705b5d1b84983af703d7eec37c9b0.png)
#include <stdio.h>
int main()
{
int a = 10;
void* pa = &a;
void* pc = &a;
*pa = 10;
*pc = 0;
return 0;
}
![9a492bd99aaf49308f015c5ca2be5c42.png](https://img-blog.csdnimg.cn/direct/9a492bd99aaf49308f015c5ca2be5c42.png)
五、const修饰指针
变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作用。 我们需要知道,const修饰变量时,const是给予变量一个常属性(可以理解为常量属性,也就是不可修改)。
代码说明如下:
#include <stdio.h>
int main()
{
int m = 0;
m = 20;//m是可以修改的
const int n = 0;
n = 20;//n是不能被修改的
return 0;
}
![961f2a447ccc4c2895e9821f40e81ced.png](https://img-blog.csdnimg.cn/direct/961f2a447ccc4c2895e9821f40e81ced.png)
#include <stdio.h>
//代码1
void test1()
{
int n = 10;
int m = 20;
int* p = &n;
*p = 20;//ok?
printf("%d ", n); //20
p = &m; //ok?
printf("%d ", *p); //20
}
void test2()
{
//代码2
int n = 10;
int m = 20;
const int* p = &n;
*p = 20;//ok?
printf("%d ", *p); //报错
p = &m; //ok?
printf("%d ", *p); //20
}
void test3()
{
int n = 10;
int m = 20;
int* const p = &n;
*p = 20; //ok?
printf("%d ", *p); //20
p = &m; //ok?
printf("%d ", *p); //报错
}
void test4()
{
int n = 10;
int m = 20;
int const* const p = &n;
*p = 20; //ok?
printf("%d ", *p); //报错
p = &m; //ok?
printf("%d ", *p); //报错
}
int main()
{
//测试⽆const修饰的情况
test1();
//测试const放在*的左边情况
test2();
//测试const放在*的右边情况
test3();
//测试*的左右两边都有const
test4();
return 0;
}
![76fd7afe58f54f5bbc230072508c9398.png](https://img-blog.csdnimg.cn/direct/76fd7afe58f54f5bbc230072508c9398.png)
希望对大家有所帮助,有什么不了解的可以私信或者评论我哟~~~
蟹蟹大家的来访,你们的点赞关注都会是我接下来创作更优质文章的动力~✌🏻️