您现在的位置是:首页 >技术杂谈 >初识C语言的指针网站首页技术杂谈

初识C语言的指针

bhlu 2024-05-31 00:00:02
简介初识C语言的指针

前言

指针在C语言中是非常重要的概念。

指针是什么

  • 指针是内存中的最小单元的编号,就是内存地址
  • 指针通常指的是指针变量,是用于存放内存地址的变量。

内存的最小单元是1字节。

32位系统中的指针变量大小是始终4个字节,64位系统中的指针变量大小是始终8字节

在C语言中,可以通过以下方式使用指针变量

#include <stdio.h>

int main() 
{
	int a = 10;
	int* p = &a;  // 这里p就是一个指针变量,存放的是a在内存中的地址
	return 0;
}

指针类型

指针类型分为以下几种

  • char *pc;声明一个指向字符类型的指针变量
  • int *pi;声明一个指向整数类型的指针变量
  • short *ps;声明一个指向短整数类型的指针变量
  • long *pl;声明一个长整数类型的指针变量
  • float *pf;声明一个单精度浮点数类型的指针变量
  • double *pd;声明一个双精度浮点数类型的指针变量

以下用char*int*作为例子简述指针类型之间的区别

// 区别1: char*存放char类型变量的地址,int*存放int类型变量的地址
#include <stdio.h>

int main()
{
	char str = 'a';
	char* pc = &str;  // 这里pc是str在内存中的地址
	*pc = 'b';
	printf("%c
", str);  // b

	int* pi = &str; // 这里会有一个warnning,提示str是char类型,用int*不兼容
	*pi = 10;  // err
	printf("%d
", str); 
	return 0;
}


/* 
区别2: 
	char*类型指针加1是往后移一个字节,因为对应的char是占一个字节,
	int*类型指针加1是往后移4个字节,因为对应的int是占4个字节
*/
#include <stdio.h>

int main()
{
	int n = 10;
	int* pi = &n;
	printf("%p
", pi); // 00B9F954
	printf("%p
", pi + 1); // 00B9F958 跟上面相差4个字节

	char str = 'a';
	char* pc = &str;
	printf("%p
", pc); // 012FFA53
	printf("%p
", pc + 1); // 012FFA54  跟上面相差1个字节
	return 0;
}

总结:存放不同类型变量的地址,应该使用与之对应类型的指针变量。

操作指针

指针±整数

指针+n = 原指针地址 + (指针对应的数据类型的字节长度 * n)

以下用char*int*作为例子

#include <stdio.h>

int main()
{
	int n = 10;
	int* pi = &n;
	printf("%p
", &n); // 0057FEB0
	printf("%p
", pi); // 0057FEB0
	printf("%p
", pi + 2); // 0057FEB8 = 0057FEB0 + (4 * 1)

	char str = 'a';
	char* pc = &str;
	printf("%p
", &str); // 0057FE9B
	printf("%p
", pc); // 0057FE9B
	printf("%p
", pc+3); // 0057FE9E = 0057FE9B + (1 * 3)
	return 0;
}

总结:指针对应的数据类型是指针移动的距离的关键因素。

指针的解引用

每个类型的指针操作的空间不同

以下用char*int*作为例子

#include <stdio.h>

int main()
{
	int n = 0x11223344;  // 这里使用的是16进制,也可以使用10进制 287454020
	int* pi = &n;
	char* pc = (char*)&n;
	// 这里&n在内存监视中是这样的 0x001DF970  44 33 22 11
	*pi = 0;  // 这里操作完之后,是这样的 0x001DF970  00 00 00 00

	n = 0x11223344;  // 0x001DF970  44 33 22 11
	*pc = 0; // 这里操作完之后,是这样的 0x001DF970  00 33 22 11
	return 0;
}

总结:指针操作的空间是这个指针对应的数据类型的大小,例如char*就是4个,int*就是1个。

野指针

野指针是指指向未知存储位置的指针。

野指针出现的原因

  1. 指针未初始化

    int main()
    {
    	int* p;  // 这里创建了指针却未初始化,所以它默认是随机值
    	*p = 10;  // error
    	return 0;
    }
    
  2. 指针越界访问

    /*
    这个数组长度为10,但是在for循环的时候指针最后一次超出了10,造成了指针越界访问
    */
    
    int main()
    {
    	int arr[10] = { 0 };
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	int* p = arr;
    	for (int i = 0; i < sz + 1; i++)
    	{
    		*p = i; // 在最后一次循环的时候,越界了
    		p++;
    	}
    	return 0;
    }
    
  3. 指针指向的空间被释放

    #include <stdio.h>
    
    int* test()
    {
    	int a = 10;
    	return &a;
    }
    
    int main()
    {
    	int* p = test(); // 此时p指向的是a的地址,但是a在test函数结束之后就被回收了,所以此时p就是野指针了。
    	return 0;
    }
    

如何避免出现野指针

  • 指针创建的时候先初始化:int* p = NULL;

  • 避免指针越界访问

  • 在使用指针的时候,先置空,使用时再判断一下是否是NULL

    #include <stdio.h>
    
    int main()
    {
    	int* p = NULL;
    	if (p != NULL)  // 这里判断一样
    	{
    		*p = 1;
    	}
    	return 0;
    }
    
  • 避免函数返回变量的地址

指针运算

  • 指针±整数改变指针的地址

  • 指针-指针算出字符串的长度

    #include <stdio.h>
    
    int main()
    {
    	int arr[10] = { 0 };
    	int* p0 = &arr[0];
    	int* p6 = &arr[6];
    	printf("%d
    ", p6 - p0);  // 6
    	return 0;
    }
    
    // 这里使用指针-指针写一个函数算出字符串的长度
    int my_strlen(char* arr)
    {
    	char* p = arr;  // 记录下开始的位置
    	while (*arr != '')
    	{
    		arr++;
    	}
    	// 这个结束之后说明arr到了字符串的末尾的位置
    	return arr - p;
    }
    
  • 指针的关系运算符

    #include <stdio.h>
    
    int main()
    {
    	int arr[2] = { 0 };
    	int* p = arr;
    	*p++ = 1;  // 这里其实就是*p=1;p++;
    	return 0;
    }
    

指针和数组

除以下两种情况下不是,其他情况下数组名表示的是数组首元素的地址

  1. sizeof(arr)这里的数组是代表的整个数组
  2. &arr这里也是整个数组

以下代码验证数组名代表的是不是数组首元素的地址

#include <stdio.h>

int main()
{
	int arr[5] = { 0 };
	printf("数组名地址=%p
", arr); // 数组名地址 = 012FFB30
	printf("数组首元素地址=%p
", &arr[0]); // 数组首元素地址 = 012FFB30
	return 0;
}

验证*(arr + i) = &arr[i]

#include <stdio.h>

int main()
{
	int arr[5] = { 0 };
	int* p = arr;
	for (int i = 0; i < 5; i++)
	{
		printf("&arr[%d]=%p-------*(arr+%d)=%p
", i, &arr[i], i, p + i);
	}
	return 0;
}

/*
&arr[0]=010FFAC4-------*(arr+0)=010FFAC4
&arr[1]=010FFAC8-------*(arr+1)=010FFAC8
&arr[2]=010FFACC-------*(arr+2)=010FFACC
&arr[3]=010FFAD0-------*(arr+3)=010FFAD0
&arr[4]=010FFAD4-------*(arr+4)=010FFAD4
*/

练习:使用指针对数组进行赋值

#include <stdio.h>

int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr; // 这里将p指向数组首元素地址
	for (int i = 0; i < sz; i++)
	{
		*(p + i) = i;
	}
	// 最后数组为{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	return 0;
}

二级指针

指向指针变量地址的指针就是二级指针

int main()
{
    int a = 10;
    int* pa = &a;  // 一级指针
    int** ppa = &pa;  // 二级指针
    *(*paa) = 30;  // *(*ppa) = *pa = a --> a = 30
    return 0
}

/*
int * p: 这里的 *代表指针,左边代表指向的是int类型,右边是指针变量名p
int* * pp: 这里的*代表指针,左边代表指向的是int*类型,右边是指针变量名pp
*/

指针数组

当出现很多变量都需要创建指针的时候,可以使用指针数组来存储对应的变量。

#include <stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;

	int* pa = &a;
	int* pb = &b;
	int* pc = &c;

	// 上面三行这样写的话感觉有点繁琐,所以采用下面的指针数组来书写
	int* arr[3] = { &a, &b, &c };
	*(arr[0]) = 1;
	*(arr[1]) = 2;
	*(arr[2]) = 3;
	printf("%d %d %d
", a, b, c);  // 1 2 3
	return 0;
}

用指针数组模拟二维数组

#include <stdio.h>

int main()
{
	int arr1[3] = { 1, 2, 3 };
	int arr2[3] = { 11, 22, 33 };
	int arr3[3] = { 111, 222, 333 };

	int* arr[3] = { arr1, arr2, arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++) 
		{
			printf("%d	", arr[i][j]);
		}
		printf("
");
	}
	return 0;
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。