C语言基础(七)----指针(二)_编程开发

来源:微信公众号「编程学习基地」

一、指针和数组

区别:

指针可以申请一块内存当作数组使用

数组直接定义使用

相同点

  • 都可以管理一块内存
  • 指针和数组都可以用 * 和 [ ] 访问内存
#include<stdio.h>
int main()
{
	int arr[4] = { 0,1,2,3 };
	int *temp = arr;
    //arr[1]和*(arr+1)等效
	printf("arr[1]:%d\n*(temp+1):%d", *(arr + 1),temp[1]);
	return 0;
}

不同点

  • sizeof(数组名)得到数组大小,sizeof(指针)得到指针大小
  • 指针可以++,–,数组名不可以
  • 指针可以重新指向 可以重新申请一块更大的内存(动态数组大小可变),静态数组定义之后大小就固定了

注意

  • 数组名作为函数实参传参的时候退化成指针
  • sizeof(任何类型的指针)的值都是4(32位系统下)

在解释指针数组和数组指针之前先来解释下数组的存储结构

//定义一个二维数组
int a[3][4] = { { 1,3,5,7 },{9,11,13,15},{17,19,21,23} };

那么它的存储结构如下图:

a[0]是a[ 0 ] [ 0 ]的地址,也是a数组的首地址

同理,C语言基础(七)----指针(二)_编程开发_02a[1]是a[ 1 ] [ 0 ]的地址,a[2]是a[ 2 ] [ 0 ]的地址

指针数组

指针数组:存放指针的数组 本质是数组,数组元素是指针

int*  arr[3];		//指针数组  10个int*
  • 指针数组和数组的搭配使用
#include<stdio.h>
int main()
{
	int arr[3][4] = { { 1,3,5,7 },{9,11,13,15},{17,19,21,23} };
	int *p[3];
	for(int i=0;i<3;i++)
	{
		p[i] = arr[i];	//指针数组指向二维数组每行的首地址
	}
    //输出指针数组指向的数组
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
			printf("%d\t", p[i][j]);
		putchar(10);	//换行
	}
	return 0;
}

数组指针

也称行指针表示方法:*int(p)[n]

数组指针:指向一个整型的一维数组,这个一维数组的长度是n

  • 数组指针一般和数组搭配使用
#include<stdio.h>
int main()
{
	int arr[3][4] = { { 1,3,5,7 },{ 9,11,13,15 },{ 17,19,21,23 } };
	int(*p)[4] = arr;	//指针数组指向数组arr
    //打印指针数组指向的值
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
			printf("%d\t", *(*(p + i) + j)); //printf("%d\t",p[i][j]);
		putchar(10);
	}
	getchar();
	return 0;
}
在这里我想解释下为什么 *(*(p + i) + j)) 和 p[i][j] 等价;
指针数组的表示方法:int(*p)[n]
p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n;
n也可以说是p的步长,也就是说执行p+1时,p要跨过n个整型数据的长度。

*(p+i) == p[i] == &p[i][0] == arr[i] == &arr[i][0] 		(==是等价的意思)
*(*(p + i) + j) == *(p[i] + j) == p[i][j]

在数组的存储结构里就是第i行的第j个元素

二、二级指针

指向指针的指针,称为 二级指针

任何数据都有地址,一级指针的值虽然是地址,但是这个地址做为一个数据也需要空间来存放,而二级指针就是就是来存储这个地址的。

二级指针定义和使用

  • 二级指针的简单用法
#include<stdio.h>
int main()
{
	int a = 10;
	int *p = &a;
	int **pp = &p;
	printf("&a:%p\np:%p\n*pp:%p\n**pp:%d\n", &a, p, *pp, **pp);		//%p打印地址
	return 0;
}

打印结果:

&a:000000B0CB1EF964
p:000000B0CB1EF964
*pp:000000B0CB1EF964
**pp:10
二级指针的一次解引用 *pp    得到所指向一级指针的地址,也就是p的值

二级指针的两次解引用 **pp  得到所指向一级指针指向的值 也就是a的值

二级指针和指针数组

二级指针可以直接访问二维数组吗?答案当然是不行的,那么如何访问二维数组呢?

通过指针数组间接访问数组

#include<stdio.h>
int main()
{
	int arr[3][4] = { { 1,3,5,7 },{9,11,13,15},{17,19,21,23} };
	int *p[3];
	for (int i = 0; i < 3; i++)
	{
		p[i] = arr[i];	//指针数组指向二维数组
	}
	int **pp = p;		//二级指针指向指针p
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
			printf("%d\t", pp[i][j]);
		putchar(10);	//换行
	}
	return 0;
}

三、指针函数和函数指针

指针函数

返回值为指针的函数,本质是一个函数,而该函数的返回值是一个指针

例如 malloc() 函数,或者自定义的函数如:

int* add(int x,int y);

所谓的指针函数也没什么特别的,和普通函数相比就是指针函数返回一个指针

函数指针

指向函数的指针

  • 定义格式:类型 (*指针名) (参数)
int (*p)(int x, int y);

  • 函数指针的调用
#include<stdio.h>
int add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*p)(int x, int y);	//定义函数指针
	p = add;	//函数指针p指向函数add
	printf("第一种方法:%d\n第二种方法:%d", (*p)(1, 2), p(2, 3));//函数指针的两种调用方法
	return 0;
}

四、和指针有关的传参

根据传参类型分类

  • 普通变量 int a;
//函数原型
void fun(int x);
//函数调用
fun(a);

  • 地址 int a;
//函数原型
void fun(int* p);
//函数调用
fun(&a);

  • 数组

一维数组:int arr[3];

//函数原型
void fun(int* p);
void fun(int arr[]);	//推荐这种,让人一看就知道你要传的是数组
//函数调用
fun(arr);

二维数组:int arr[3] [3];

//函数原型
void fun(int(*p)[3]);
void fun(int[][3]);		//行省略,列不能省略 
//函数调用
fun(arr);

指针数组:int*p[3];

//函数原型
void fun(int* p[3]);
void fun(int** p);
//函数调用
fun(p);

函数名:

#include<stdio.h>
void add()
{
	printf("调用add()函数");
}
void fun(void(*p)())
{
	p();	//通过函数地址调用add函数
}
int main()
{
	fun(add);//函数地址作为实参
	return 0;
}

可以思考下为什么要把一个函数的地址作为参数传递给另一个函数,要知道在C语言中,一个函数内部是可以直接调用其他函数的,既然可以直接调用,为什么还要用这么麻烦的办法去把函数当做参数来传递呢。