C语言指针深入解析(上)

C语言指针深入解析(上),第1张

目录

前言

 一、指针是什么?

1.1 内存、指针变量、指针大小

1.1.1 内存

1.1.2 指针变量 

1.1.3 指针大小

二、指针类型和指针类

2.1 指针类型

2.2 指针类型的意义

2. 指针的解引用 

三、指针运算 

3.1 指针+-整数

3.2 指针+-指针

3.3 指针的关系运算

四、指针和数组

 五、二级指针

六、指针数组 

七、野指针


前言  一、指针是什么?

计算机中,内存也是分为了许多的内存小块,每一块都有自己的内存编号, 而指针是内存中一个最小单元的编号,也就是地址,我们可以想象内存编号就是指针;平时我们口语中说的指针,通常指的是指针变量,用来存放内存地址的变量。

1.1 内存、指针变量、指针大小 1.1.1 内存

先看一段代码:

#include
int main()
{
	int a = 10;
	int b = 20;
}

这段代码非常简单,就是两个变量的声明,分别赋值了 10、20。 我们把内存看成学校,那"int a = 10;" int b = 20;"所表达的意思如下:

1.学校的寝室门号为dor_x,dor_y。                                                                                         

2.10,20两学生入住了学校寝室。                                                                                     

3.dor_x,dor_y就是所谓变量a,b的地址。                                                                                 

4.10,20两学生通过寝室号(变量a,b的内存地址dor_x,dor_y)找到了寝室(a,b)。

1.1.2 指针变量 

先看一段代码: 

#include
int main()
{
	int a = 10;
	int* p = &a;
	return 0;
}

这里我们可以的得出:

🚀就是通过&(取地址符),取出a变量的地址,将这个地址存放到另一个变量p中,这个变量p就是指针变量。

🚀int是整形,内存中有些数据是很大的,为了避免出现栈(内存)溢出出现报错,这只会将变量a第一个字节的地址存放在p变量中,p就是一个指针变量( 用来存放地址的变量,存放在指针中的值都被当成地址处理)

1.1.3 指针大小

最小的内存单元空间是1个字节。而对于指针来说,一个指针占多少个字节是由计算机 *** 作系统的位数决定的。这里我们用32位 *** 作系统举例说明:32位的电脑,有着32跟的地址线,在寻址的时候会长生0(低电压)和1(高电压),那么32跟地址线会产生多少的地址呢,我们来看看:

00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000002
00000000 00000000 00000000 00000003
00000000 00000000 00000000 00000004
         ...........
01111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111

由数学概率的角度来说,这里会产生2^32(2的32次方)种概率数,而这里的概率数就是我们的地址数;32个0和1的二进制序列组合成了一个地址,换算成字节是4个字节,那地址就得用4个字节来存储,而指针变量就是地址,所以,每一个指针变量都是4个字节(在32位才是4个字节,64位下是8个字节)               

🚀指针是用来存放地址的,地址是唯一标示一块地址空间的                                                    🚀指针的大小在32位平台是4个字节,在64位平台是8个字节

二、指针类型和指针类 2.1 指针类型

先看一段代码:

#include 
int main()
{
	int num = 10;
    int* p = #    //int* 类型的指针是为了存放 int 类型变量的地址。
    
    //下面的指针变量出现不兼容的情况,因为int与其他类型的存储方式是不同的,
    //这就要根据定义变量的类型而定,下面会讲。

	char* p = #   //char* 类型的指针是为了存放 char 类型变量的地址。
	short* p = #  //short* 类型的指针是为了存放 short 类型变量的地址。
	long* p = #   //以此类推
	float* p = #
	double* p = #
	return 0;
}

将&num(取num的地址)存放在p中,这里我们知道p是指针变量,指针类型如上面代码所示;但是呢,不同的类型之间在编译的时候会出现不兼容的情况,那怎样来处理这样的情况呢?

2.2 指针类型的意义

先看一段代码:

#include 
#include 
int main()
{
	int n = 10;           //变量n的类型为int整形
	char* pc = (char*)&n; //取n的地址,传个char类型的指针变量pc,(char*)&n是对n进行类型转换
	int* pi = &n;

	printf("%p\n", &n);   //&n和pc的地址内存是一样的,因为存放的都是第一个字节的内存地址
	printf("%p\n", pc);

	printf("%p\n", pc + 1);//pc代表首元素的地址,pc+1相当于跳过一个char的地址
	printf("%p\n", pi);    //pi存了n的地址,相当于打印n的地址
	printf("%p\n", pi + 1);//pi代表n首元素的地址,pi+1相当于跳过一个int的地址
	return 0;
}

运行结果如下: 

           

 总结:

指针的类型决定了指针变量在内存中进行寻址的距离大小。
2. 指针的解引用 

先看一段代码:

#include
//第一段代码
int main()
{

	int n = 0x11aabbcc; //0x11aabbcc中,cc是低地址位
	char* pc = (char*)&n;
	*pc = 0;   //这里的"*"为解引用 *** 作符,n中的地址为0x11aabbcc,&n将n的地址传给了pc,
			   //*pc = 0;这句语句的意思通过pc中所存n的地址找到n,将0赋了n;
	printf("%p\n", n); //这里打印11aabb00是因为指针类型所限制,只更改了一个字节的内容
	return 0;
}


#include
第二段代码
int main()
{
	int n = 0x11aabbcc;
	printf("%p\n", n);
	char* pc = (char*)&n;
	int* pi = &n;
	*pc = 0;
	printf("%p\n", n);
	*pi = 0;
	printf("%p\n", n);
	return 0;
}

运行结果为: 

总结:指针解引用 *** 作符会有访问地址大小的权限,而这是由指针的类型所决定;例如char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

三、指针运算  3.1 指针+-整数

指针的加减运算表示在内存中向前或向后移动多少内存单元,内存单元的多少是由指针类型的大小所决定。

3.2 指针+-指针

先看一段代码:

#include 
int main()
{
	int arr[10] = { 0 };
	printf("%d",&arr[6]-&arr[3]);//打印3
	return 0;
}

指针-指针得到的值就是指针与指针之间的元素个数,但是呢必须是同一块的内存空间。

3.3 指针的关系运算

假设有指针数组arr[10]:

#include 
int main()
{
	int arr[10] = { 0 };
	if(&arr[6] > &arr[3])  
		printf("&arr[6]的内存地址大于&arr[3]");//条件成立,会打印
	else
		printf("&arr[6]的内存地址小于&arr[3]");
	return 0;
}

指针的关系运算也是有条件的,可能有越界的情况,便出现了这样一条规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

四、指针和数组

 先看一段代码:

#include 
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
   
    int sz = sizeof(arr) / sizeof(arr[0]); //sizeof(数组名):数组表示整个数组,计算的是整个数 
                                           //组的大小,单位是字节
    printf("%d %p\n", sz, &arr);         //&数组名:数组名表示整个数组,取出的是整个数组的地址
    
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}

 运行结果:

 由上我们可知到可见数组名和数组首元素的地址是一样的,但有两种例外:

🚀sizeof(数组名):数组表示整个数组,计算的是整个数组的大小,单位是字节

🚀&数组名:数组名表示整个数组,取出的是整个数组的地址

我们也可以通过指针来访问数组: 

#include
void Print(int* arr,int sz)
{
	int* p = arr;
	for (int i=0;i

 

 五、二级指针

二级指针:指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?这里就衍生了二级指针。

先看一段代码:

#include 
int main()
{
    int a = 10;
    int* b = &a;
    int** c = &b;
    **c = 30;  //通过解引用 *** 作符,找到a的地址,然后对a存储的数据进行修改
    printf("%d",a);//打印30
    return 0;
}

如图所示: 

🚀a的地址存放在b中 , b的地址存放在c中。b是一级指针,而c是二级指针。                        🚀c可用通过存储b的内存地址指向b,而b也可以通过存储a的内存地址指向a;

 对于二级指针运算有:

🌈*b通过对b中的地址进行解引用,就找到a中多存储的数据(20)

int a = 20; 
int*b = &a;//等价于 b = &a;

 🌈**c通过 *c 找到 b ,然后对 b 进行解引用 *** 作*b(*c) ,再找到 a,然后对a的值进行修改。

int** c = &b;
**c = 30;
六、指针数组 

指针数组是数组。是存放指针的数组。 数组我们已经知道整形数组,字符数组。

char arr[5];
int arr[5];
int* arr[5];// arr是一个数组,有五个元素,每个元素是一个整形指针int*。

上面所表示的为

七、野指针 

野指针:我们已经理解指针是会指向一个内存单元的,而野指针就是说,这个指针指向的地址是我们不可知的,也就是随机的,不正确的、没有明确限制的。下面几种情况都会出现野指针的情况:

🚀指针未初始化                        🚀指针越界访问                      🚀指向的空间没有释放

//1.定义一个指针变量时没有初始化,指针变量的随机值的意思就是说他指向的内存是随机的
int *p;


//2、动态开辟的内存空间在使用完后调用free函数释放掉这段内存空间,
int func()
{
    int *p = malloc(sizeof(int));
    free(p);//没有将p置为NULL的 *** 作,虽然开辟的空间被释放掉但指针依旧存在。
}

//3、对指针的 *** 作已经超出了指针变量的作用域
int arr[1] = {0};//只有一个
int *p = arr;
*(P++);          //当指针指向的范围超出数组arr的范围时,p就是野指针

本章完,下章续上!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/706969.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)