C语言——指针详解(必收藏)

目录

1.什么是指针?

1.1概念

1.2指针的大小

​ 1.3指针类型的作用

2.野指针 

2.1野指针产生的原因

2.2 如何规避野指针

3.指针运算 

3.1指针+-整数

3.2指针-指针

3.3 指针的关系运算 

4. 二级指针

5. 数组名

*6.指针数组和数组指针 

6.1指针数组

*6.2数组指针 

 6.3举例区别含义

7.数组参数和指针参数

7.1数组参数

7.2指针传参

8.函数指针 

9.函数指针数组

9.1指向函数指针数组的指针

*10.回调函数

1.什么是指针?

1.1概念

简单的来说,指针就是地址。我们口头上说的指针其实指的是指针变量。指针变量就是一个存放地址的变量。

1.2指针的大小

 指针在32位机器下是4个字节,在64位机器下是8个字节。注:(指针的大小与类型无关)

 

 1.3指针类型的作用

上面涉及到指针的大小与指针类型无关,那么指针类型的作用是什么呢?

指针解引用访问,使用的访问权限是多少    话不多说,上图!

当一个int* 的指针+1,跳过4个字节;当一个char* 的指针+1,跳过1个字节。

同理,对于(指针+-整数)类的题也是如上的原理。

2.野指针 

野指针顾名思义,指针指向的位置不可知,就像没有主人的流浪狗。

2.1野指针产生的原因

  1. 指针未被初始化

  2. 指针越界访问

  3. 指针指向的空间释放

对于前两点比较好理解,下面对第三点进行解释:

int* test( )
{
	int a = 5;
	return &a;
}
int main()
{
	int* p = test();
	*p = 10;
	return 0;
}

变量a的地址只在test()函数内有效,当把a的地址传给指针p时,因为出了test函数,变量a的空间地址释放,导致p变成了野指针。

2.2 如何规避野指针

  1. 小心越界
  2. 及时把指针赋成空指针
  3. 避免返回局部变量的地址
  4. 使用指针前检查有效性

3.指针运算 

3.1指针+-整数

此部分内容跟指针类型那部分一致

3.2指针-指针

指针-指针的绝对值指的是两个指针之间元素的个数。

前提:两个指针必须指向同一空间

例如:&arr[9]-&arr[0] 

下面举个例子实现my_strlen函数

int my_strlen(char str[])
{
	char* p = str;
	int count = 0;
	while (*p != '')
	{
		p++;
		count++;
	}
	return count;
}


int main()
{
	char arr[] = "abcdef";
	printf("%dn",my_strlen(arr));
	return 0;
}

3.3 指针的关系运算 

此部分内容很简单,指针与指针之间比较大小就是指针的关系运算。

例如:定义int* p1,数组int arr[5],p1>&arr[5]。

但是要遵循一个标准:允许指针指向数组元素和指针指向数组最后一个元素后面的位置进行比较,不允许指针指向数组元素与指针指向数组第一个位置的前面进行比较。

4. 二级指针

二级指针就是存放指针地址的指针变量。就像有三个抽屉第一个抽屉的钥匙放在第二个抽屉,第二个抽屉的钥匙放在第三个抽屉。

例如:int a=10;int* p1=&a;int** p2=&p1;

对int* * 做解释:第一个*号代表指针指向的类型是int*的,第二个*代表这个是指针。

5. 数组名

数组名大家都很熟悉,但要区分以下几种情况:

  1. sizeof(arr):表示的是整个数组的大小。
  2. &arr: 表示整个数组,取出的是整个数组的大小。

其他情况数组名都表示数组首元素地址。

*6.指针数组和数组指针 

6.1指针数组

  1. 定义:存放指针的数组(int* arr[])。我们知道有整型类型的数组int arr[],还有字符类型的数组char arr[],指针数组就是指针类型的数组。

  2. 以下举个例:

int main()
{
	int a = 0;
	int b = 1;

	int* p1 = &a;
	int* p2 = &b;

	int* arr[] = { p1,p2 };//指针数组
	int* arr[] = { &a,&b };//指针数组

	return 0;
}

*6.2数组指针 

  1. 定义: 指向数组的指针int (*)[]。
  2. 应用:遍历整个二维数组
void my_print(int(*p)[5], int x, int y)
{
	int i = 0;
	for (i = 0; i < x; i++)
	{
		int j = 0;
		for (j = 0; j < y; j++)
		{
			printf("%d ", *(*(p + i) + j));
			//printf("%d ", p[i][j]);相似的写法
		}
		printf("n");

	}
}

int main()
{
	int arr1[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	my_print(arr1, 3, 5);
	return 0;
}

 6.3举例区别含义

  • int arr[5]               //名为arr的数组,数组里有5个元素,每个元素是int
  • int* p1[5]              //指针数组,数组里有5个元素,每个元素是int*
  • int (*p2)[5]            //数组指针,一个指向数组(里面有五个元素,每个元素是int)的指                                  针
  • int (*p3[5])[5]        //p3[5]是一个有5个元素的数组,数组里每个元素是int(*)[5]

7.数组参数和指针参数

7.1数组参数

  • 一维数组传参: int arr[10](形参部分数组大小可以不写)、int* arr
  • 二维数组传参: int arr[3][5]、int arr[][5](行可以省略,列不能)、int (*arr)[5]                                          不能直接传数组名,二维数组的首地址是第一行元素的首地址

7.2指针传参

  • 一级指针传参:int* p
  • 二级指针传参:int** p

8.函数指针 

  1. 定义:指向函数的指针int (*pf) (int,int),pf函数返回的类型是int (*)(int,int)
  2. 对于函数来说如:Add和&Add,意义和值都一样。这一定要区别于数组。
  3. 特别的,当要调用函数时,定义一个pf的函数指针指向函数。那么int ret=(*pf)(2,3)和int ret=pf (2,3)等价。

 **对下面代码的理解:

(*(void(*)())0)()                            //   将0处强制类型转换为函数指针类型,再对0地址                                                                       进行调用

void(*signal(int,void(*)(int)))(int)        //这是函数的声明,singal是一个函数,传进去两个                                                                    参数的类型是int和void(*)(int),signal函数的返回值                                                                  类型是void(*)(int)。

9.函数指针数组

  1. 定义:存放函数指针类型元素的数组
  2. 应用:实现计算器
#include<stdio.h>

int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}


void menu()
{
	printf("****************n");
	printf("* 1.Add  2.Sub *n");
	printf("* 3.Mul  4.Div *n");
	printf("***  0.exit  ***n");
	printf("****************n");
}


int main()
{
	menu();
	int input = 0;
	printf("请选择:");
	scanf("%d",&input);
	int ret = 0;
	//转移表
	int(*pfarr[])(int, int) = { 0,Add,Sub,Mul,Div };
	do
	{
		if (input == 0)
		{
			printf("退出n");
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			int x = 0;
			int y = 0;
			printf("请输入两个操作数:");
			scanf("%d%d",&x,&y);
			ret = pfarr[input](x,y);
			printf("结果是%dn",ret);
			break;
		}
		else
		{
			printf("选择错误!");
		}

	} while (input);

	return 0;
}

9.1指向函数指针数组的指针

1.定义:int (*(*parr)[4])(int  int)=&parr

*10.回调函数

  • qsort()函数: 是一个库函数,基于快速排序算法实现的一个排序的函数。优点是,任意类型的数据都能排序
  • qsort()函数的形参定义:

void qsort(void* base(起始地址),size_t num(元素个数),size_t width(一个元素的字节长度),int (*cmp)(const void* e1,const void* e2)(自定义比较函数))

  • qsort()函数应用:模拟计算器,排序结构体
//模拟计算器,对上面的代码进行优化
#include<stdio.h>

void print(int* str, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", str[i]);
	}
	printf("n");
}

void swap(char* e1, char* e2, int width)
{
	int temp = 0;
	int i = 0;
	for (i = 0; i < width; i++)
	{
		temp = *e1;
		*e1 = *e2;
		*e2 = temp;
		e1++;
		e2++;
	}
}

int cmp(const void* e1, const void* e2)
{
	return (*(int*)e1 - *(int*)e2);//e1>e2-> >0;e1=e2-> 0;e1<e2-> <0
}

int bubble_sort(void* base, int num, int width, int(*cmp)(const int* e1, const int* e2))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < num - 1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

int main()
{
	int arr[] = { 9,8,7,3,5,4,2,1,6,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp);
	print(arr, sz);
	return 0;
}
//排序结构体
#include<stdio.h>
struct Student
{
	char name[20];
	int age;
	double score;
};


int cmp_name(const void* e1, const void* e2)
{
	return strcmp(((struct Student*)e1)->name, ((struct Student*)e2)->name);
}


int main()
{
	struct Student arr[] = { {"zhang",17,80.6},{"wang",20,85.2},{"li",19,92.0} };
	int sz = sizeof (arr) / sizeof (arr[0]);
	qsort(arr,sz,sizeof (arr[0]),cmp_name);
	return 0;
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>