【C语言】指针

0一、指针的概念
二、指针和指针类型
1. 为什么指针有多种类型?
2. 指针+-整数的意义
3. 指针+-指针的意义
4. 得到了变量的地址有什么用呢?(指针解引用)
三、野指针
1. 野指针是什么?
2. 什么情况会造成野指针?
四、指针和数组
五、二级指针
六、字符指针
七、指针数组
1. 指针数组的定义
2. 指针数组的使用
八、数组指针
1. 如何定义数组指针?
九、函数指针
1. 函数指针的定义
2. &(函数名) 
十、函数指针数组zhizh
1. 函数指针数组的定义
2. 函数指针数组的使用
十一、指向函数指针数组的指针
函数指针数组的指针的定义
十二、回调函数
1. 回调函数的定义
2. 回调函数的使用

指针的概念

  1. 指针是内存中一个最小单元的编号,也就是地址
  2. 指针通常指指针变量,一般存放内存地址的变量
    #define _CRT_SECURE_NO_WARNINGS 1
    #include<stdio.h>
    int main()
    {
    	int a = 20;
    	int* pa = &a;//将a的地址放入pa中
    	printf("%p", pa);
    	return 0;
    }
    我们可以通过取地址操作符(&)取出变量的内存与实地址,把地址可以存放到一个变量中,这个变量就是指针变量。

在x86环境下打印出a存放在pa中的地址

指针的类型

有很多不同类型的变量,不同类型的变量应放在对应类型中!!

int main()
{
	char a = 'a';
	char* ch = &a;

	double b = 20.22;
	double* d = &b;
	return 0;
}
指针±整数
int main()
{
	int a = 0;
	int* pa = &a;
	printf("%p %p\n", pa, pa + 1);

	double b = 0.0;
	double* d = &b;
	printf("%p %p\n", d, d + 1);

	char c = 'a';
	char* ch = &c;
	printf("%p %p\n", ch, ch + 1);

	return 0;
}


int*类型的指针 + 1 是跳过四个字节
double*类型的指针 + 1 是跳过八个字节
char*类型的指针 + 1 是跳过一个字节

指针±指针

指针-指针

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int* pa = &arr + 1;
	printf("%d", pa - arr);
	return 0;
}

这里数组取地址加1跳过整个数组,arr表示数组首元素地址等价于&arr[0]

运行结果为两个指针之间所隔的元素个数

指针 + 指针是没有意义的!!!

得到了变量的地址有什么用呢?(指针解引用)指针的作用就是通过地址取访问指针指向的变量。
指针的类型决定了指针解引用能够访问的字节数。

野指针

野指针是指指向未知内存位置或者已经释放内存的指针。

未初始化的指针都会导致野指针

int main()
{
	int* pa = NULL;
	printf("%d", *pa);
	return 0;
}

指针和数组

int main()
{
	int arr[] = { 1,2,3};
	int* pa = &arr[0];
	printf("%p\n", arr);
	printf("%p\n", pa);

	return 0;
}

前面提到

arr表示数组首元素地址等价于&arr[0],所以arr于pa打印的值相等

例外:

  • sizeof(数组名),这里的数组名是数组的地址
  • &(数组名),这里的数组名也是数组的地址
int main()
{
	int arr[] = {1,2,3,4,5,6,7};
	int* pa = &arr[0];
	for (int i = 0; i < 7; i++)
	{
		printf("arr[%d]: %d   <->   *(pa + %d): %d\n", i, arr[i], i, *(pa + i));
	}

	return 0;
}

从上图中可以得知:下标和指针(地址)±整数都能够遍历数组。

!!!arr[i]=*(pa+i)!!!

数组首元素地址可以代替数组名。

二级指针

int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9,10};
	int* pa = &arr[0];
	int** ppa = &pa;
	printf("ppa : %p\n", ppa);
	printf(" pa : %p\n", pa);
	printf("*ppa: %p\n", *ppa);

	return 0;
}

二级指针存储的是一级指针的地址,而指针解引用可以找到被指针指向的变量,那么这里二级指针解引用也可以找到一级指针。

字符指针

字符指针我们常见的使用方法是先创建一个字符变量,再创建一个字符指针变量,将字符变量的的地址赋给字符指针变量。

int main()
{
	char c = 'v';
	char* ch = &c;

	printf("%p", ch);
	return 0;
}

另一种使用方法则是创建一个字符指针变量,将字符串的首元素地址赋给他。、

int main()
{
	char* ch = "sjcjsdhvj";

	printf("%p", ch);
	return 0;
}

指针数组

1.指针数组的定义

指针数组是一种存储指针的数组。

int main()
{
	int arr1[] = {1,2,3,6,5,4,7,8,9,45,1};
	int arr2[] = {2,3,6,5,4,8,8,4,5,4,5};
	int arr3[] = {3,6,45,5,4,5};
	int* arr[] = { arr1 , arr2 , arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%p\n", arr[i]);
	}
	return 0;
}
2. 指针数组的使用

指针数组是存储一级指针的数组,而一级指针指向的是数组

数组指针

1. 如何定义数组指针?
数组指针是用来存放数组地址的指针。
数组指针变量如何定义:(指针变量是 p ,数组存放的 int 类型的变量)
首先它是指针那么就不能与[ ]先结合----(*p)
其次它指向的内容是数组 ---- (*p)[ ] ----[ ]中为数组的元素个数
最后它指向数组存储变量的类型为什么 ---- int(*p)[]

int main()
{
	int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9 ,10};
	int(*p)[10] = &arr;
	printf("%p", p);
	return 0;
}

测试

int main()
{
	int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9 ,10};

	printf("%p\n", &arr);       //取出数组的数组
	printf("%p\n", arr);        //取出数组首元素的地址
	printf("%p\n", &arr[0]);    //取出数组首元素的地址
	printf("%p\n", &arr + 1);
	printf("%p\n", arr + 1);
	printf("%p\n", &arr[0] + 1);

	return 0;
}

1)数组首元素地址 +1 :这里的地址在十六进制的状态下,相比之下地址的大小增加了 4 ,十进制下也是 4 ,数组储存变量是 int 内存大小也是 4 个字节 ,这里我们可以得到数组首元素地址 +1 ,也就是跳过了一个变量大小的字节数。
(2)数组地址 +1 :这里的地址在十六进制的状态下,相比之下地址的大小增加了 28 ,十进制下就是 40 ,数组储存变量是 int 内存大小也是 4 个字节并且数组元素个数是 10 个元素 ,这里我们可以得到数组首元素地址 +1 ,也就是跳过了一个数组大小的字节数。

结论:

  • 数组首元素地址 +1 ,跳过一个变量的大小。
  • 数组地址 +1 , 跳过一个数组的大小

函数指针

1. 函数指针的定义

函数指针是一种指向函数的指针变量,它存储着函数的地址。函数指针的类型由函数的返回值类型和参数类型组成,可以用以下语法定义:

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*p)(int, int) = Add;
	int a = 10, b = 30;
	int c = 0;
	c = (*p)(a, b);
	printf("%d", c);
	return 0;
}

!!!这里add=&add!!!

函数指针数组

1. 函数指针数组的定义
函数指针数组是一个数组用来存储函数指针的。
如何定义一个函数指针数组:假设数组存储的是 int (*)(int ,int)
首先是一个数组:那么就要先于[]结合 - pf[]
然后数组存储的是函数指针:int(*pf[])(int ,int)

2. 函数指针数组的使用

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;
}

cmp(int (*pf)(int, int))
{
	int x = 0, y = 0;
	printf("请输入两个操作数\n");
	scanf("%d%d", &x, &y);
	printf("%d\n", pf(x, y));
}

int main()
{
	int (*pf[4])(int, int) = { add , sub , mul , div };
	int input = 0;
	while (1)
	{
		printf("*************************\n");
		printf("****  1:add    2:sub ****\n");
		printf("****  3:mul    4:div ****\n");
		printf("*************************\n");
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			cmp(add);
			break;
		case 2:
			cmp(sub);
			break;
		case 3:
			cmp(mul);
			break;
		case 4:
			cmp(div);
			break;
		default:
			printf("输入错误,请重新选择\n");
		}
	}
	return 0;
}

这也是转移表

指向函数指针数组的指针

如何定义一个函数指针数组的指针:假设数组存储的是 int (*)(int ,int)
首先是一个指针:那么就要先于*结合 ---- (*pf)
然后指针指向的是函数指针数组:(*pf)[]----方块中的是元素个数
最后数组存储的元素类型是:int(*(*pf)[])(int ,int) ----方块中的是元素个数

回调函数

这里使用回调函数实现 qsort() 函数的模拟(原理不同,这里使用冒泡排序的底层原理)

void bubble_sort(void* base, size_t sz, size_t width, int(*cmp(const void* p1, const void* p2)))
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 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[10] = { 1,5,6,8,7,9,46,2,5,4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz,sizeof(arr[0]),cmp);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}
int cmp(void const* p1, void const* p2)
{
	return *(int*)p1 - *(int*)p2;
}
void print(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(arr + i));
	}
}
int main()
{
	int arr[10] = { 2,5,6,3,2,7,8,9,4,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp);
	print(arr, sz);
	return 0;
}
  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值