C语言:行指针和列指针

本文主要介绍 C 语言二维数组中的行指针及列指针。学习笔记,仅供参考,如有错误,敬请指出。

一.行指针

在说行指针之前,先看几种指针类型(以int为例):

int* a; 
//a是指向整形的指针;

int a[5];
//a是一个一维数组名(该数组包含5个元素,每个元素类型是整形);
//a表示的是数组(a称整形数组);
//为什么要这样表示?答:数组就是这么规定的表示方式。
//a的值表示首元素地址,a+1是第二个元素地址...;

int* a[5]; 
//a也是一个一维数组名(该数组包含5个元素,每个元素类型是指向整形的指针);
//a表示的是数组(a称指针数组);
//为什么要这样表示?答:数组就是这么规定的表示方式。不论元素类型是什么。
//同样,a的值表示首元素地址,a+1是第二个元素地址...;

int (*a)[5]; 
//a是指向一维数组的指针(这里每个一维数组含5个整形元素);
//a是数组指针;
//为什么要这样表示?举个夸张的栗子,假设要申明一万个一维数组int a[5],b[5],c[5]...不太现实
//偷懒的办法,使用一个指针来表示a,b,c...这一万个数组名;
//即:(*a),a表示a[5],a+1表示b[5],a+2表示c[5]...

int (*a)(); 
//a是指向函数的指针(a是函数指针);

int *a();
//函数的返回类型是int *,a只是一个函数名;

常用的一维数组,如:

int a[5];
//a是一个指向非常量的常量指针,即a本身的值不能变如a=a+1就出错,
//但是a指向的地址的值可以变如*a=1123,此处a数据类型即是int * const;
//a的值是该数组第一个元素的地址,即a=&a[0];
//*a即第一个元素的值;

常用的二维数组,如:

int a[3][5];
//此处,a也是一个指向非常量的常量指针,如果要说什么才是行指针?那么这里的a便是!

写法解释指针类型
a+0或&a[0]指向第1个一维数组的地址(指向第1行)行指针
a+1或&a[1]指向第2个一维数组的地址(指向第2行)行指针
a+2或&a[2]指向第3个一维数组的地址(指向第3行)行指针
行指针是指向数组的指针,即上面几种指针类型中的 int (*a)[5];所以,当二维数组要被当做参数进行传递时,可以这样声明:
void TestRowPtr(int (*p)[5], const int row);
或者
void TestRowPtr(int p[][5],const int row);

附上测试代码:

#include <stdio.h>

int main()
{
	void TestRowPtr(int (*rowPtr)[5], const int row);
	void Print(const int* const colPtr, const int row, const int col);

	int example[3][5] =
	{
		{1,2,3,4,5},
		{6,7,8,9,10},
		{11,12,13,14,15}
	};

	//或者
	//int (*example)[5] = new int[3][5]
	//{
	//	{1,2,3,4,5},
	//	{6,7,8,9,10},
	//	{11,12,13,14,15}
	//};

	TestRowPtr(example, 3);
	Print(*example, 3, 5);

	//使用第二种 new 方式初始化,需要手动释放
	//delete[] example;
}

void TestRowPtr(int (*rowPtr)[5], const int row)
{
	int i, j;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < 5; j++)
		{
			*(*(rowPtr + i) + j) = 1; //或者写成 rowPtr[i][j]=1;
		}
	}
}

void Print(const int* const colPtr, const int row, const int col)
{
	int i, j = 0;
	for (i = 0; i < row * col; i++)
	{
		if (j != col - 1)
		{
			j++;
			printf("%d,", *(colPtr + i));
		}
		else
		{
			j = 0;
			printf("%d\n", *(colPtr + i));
		}
	}
	printf("-------------------\n");
}
结果:


 

可以看到,上述函数:

void (int (*p)[5],const int row)或者void (intp[][5],const int row)

的声明方法都不具有可复用性(列数固定),为了使代码具有可复用行,可以考虑使用列指针。

二.列指针

对于一个二维数组:

int a[3][5];

前面已经知道,a即是它的行指针,a+0表示第1行的地址,a+1表示第2行地址…

或者可以说成&a[0]表示第1行的地址,&a[1]表示第2行的地址…

那么a[0]+0,a[0]+1…就表示第1行第1列的地址,第1行第2列地址…

a[1]+0,a[1]+1就表示第2行第1列地址,第2行第2列地址…

写法解释指针类型
a[0]+0或&a[0][0]指向第1行第1列的地址列指针
a[0]+1或&a[0][1]指向第1行第2列的地址列指针
a[1]+0或&a[1][0]指向第2行第1列的地址列指针
a[1]+1或&a[1][1]指向第2行第2列的地址列指针

像上面的a[row]+col即列指针,列指针经过一次解引用就可以转化成二维数组中实际的值,列指针也是指向非常量的常量指针。

所以如果用列指针进行函数传参,可以直接声明如下:
void TestColPtr(int * const colPtr,const int row,const int col);

附上测试代码:

#include <stdio.h>

int main()
{
	void TestColPtr(int* const colPtr, const int row, const int col);
	void Print(const int* const colPtr, const int row, const int col);

	int example[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };

	//或者
	//int (*example)[5] = new int[3][5]
	//{
	//	{1,2,3,4,5},
	//	{6,7,8,9,10},
	//	{11,12,13,14,15}
	//};

	TestColPtr(*example, 3, 5);
	Print(*example, 3, 5);

	//使用第二种 new 方式初始化,需要手动释放
	//delete[] example;
}

void TestColPtr(int* const colPtr, const int row, const int col)
{
	for (int i = 0; i < row * col; i++)
	{
		*(colPtr + i) = 2; //这里是考虑到二维数组本质上也是按顺序排列的
	}
}
结果:


综上所述:对行指针解引用,可以得到列指针,对列指针解引用,可以得到具体的元素值:
但是这并不表示行指针可以作为二级指针进行函数传参!
以下是将行指针作为二级指针的实参进行传递,导致编译错误的代码:
int main()
{
	void WrongFunc(int** rowPtr, const int row, const int col);

	int example[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
	WrongFunc(example, 3, 5);
}

void WrongFunc(int** rowPtr, const int row, const int col)
{
	int i = 0, j = 0;
	for (; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			*(*(rowPtr + i) + j) = 3;
		}
	}
}
编译时,提示:
错误 C2664 无法将参数 1 从“int [3][5]”转换为“int **”
错误(活动) E0167 "int (*)[5]" 类型的实参与 "int **" 类型的形参不兼容

原因是函数的第一个参数类型是int **,而实际接收到的参数却是:int(*)[5];

这是两种不同类型的指针类型!

<think>嗯,用户问的是“函数数组指针”,这个术语有点容易混淆,我得先弄清楚他们具体指的是什么。首先,函数数组指针可能涉及三个概念:函数指针、数组指针,以及它们的组合。可能用户是想了解如何定义指向函数数组的指针,或者指向数组的函数指针?不过更常见的情况可能是指向函数的指针数组,或者数组的指针,但需要结合函数的情况。 首先,我需要回顾一下C/C++中的相关概念。函数指针是指向函数的指针变量,例如int (*funcPtr)(int, int)可以指向一个接受两个int参数并返回int的函数。数组指针则是指向数组的指针,比如int (*arrPtr)[10]指向一个包含10个int元素的数组。而函数数组指针可能是指一个数组,其元素是函数指针,或者是一个指针指向这样的数组。 用户可能是在学习这些复合类型时遇到了困惑。例如,定义一个包含多个函数指针的数组,或者一个指针指向这样的数组。这时候需要明确语法结构,因为C/C++的声明语法有时比较复杂。 比如,函数指针数组的正确声明应该是int (*funcArray[5])(int),这表示funcArray是一个有5个元素的数组,每个元素都是指向返回int且接受一个int参数的函数的指针。而如果要定义一个指向这个数组的指针,应该怎么写?这时候可能需要使用typedef来简化。例如: typedef int (*FuncPtr)(int); FuncPtr (*arrPtr)[5] = &funcArray; 或者直接声明:int (*(*arrPtr)[5])(int) = &funcArray; 这样的声明看起来复杂,但分解起来可能更容易理解。用户可能需要具体的例子来理解如何声明使用这样的指针。 另外,用户可能在实际编程中遇到需要传递函数指针数组的情况,或者需要动态分配这样的数组,这时候正确的指针操作就很重要了。例如,在回调机制或状态机设计中,函数指针数组很常见,而了解如何通过指针来操作这些数组是必要的。 还需要注意,函数指针数组指针在语法上的区别。函数指针的声明需要参数列表,而数组指针则需要指定数组的大小。当它们结合在一起时,声明的可读性可能会变差,因此使用typedef来分步定义可能是个好方法。 总结来说,用户需要了解如何正确声明使用指向函数数组的指针,或者函数指针数组的指针。这时候,分步解析声明语法,提供示例代码,并解释每一步的作用,会很有帮助。同时,提醒用户注意语法中的括号位置,因为错误的放置会导致完全不同的类型解释。例如,int *funcArray[5]()表示一个返回int指针的函数的数组,而int (*funcArray[5])()才是一个函数指针数组,返回int。 最后,可能还需要讨论应用场景,比如在实现策略模式、插件系统或者命令解析时,函数指针数组的指针如何被用来动态调用不同的函数,增强程序的灵活性模块化。</think>好的,我来逐步解释“函数数组指针”的概念: 1. **基础概念分解** - **函数指针**:存储函数地址的变量,如 `int (*pFunc)(int)` - **数组指针**:指向数组的指针,如 `int (*pArr)[5]` - **函数数组指针**:指向“函数指针数组”的指针,属于复合类型 2. **语法结构解析** ```c // 定义函数指针类型 typedef int (*FuncPtr)(int); // 创建函数指针数组 FuncPtr funcArray[3] = {func1, func2, func3}; // 定义指向这个数组的指针 FuncPtr (*pFuncArray)[3] = &funcArray; ``` 等效的直接声明: ```c int (*(*pFuncArray)[3])(int) = &funcArray; ``` 3. **关键符号解析** ``` int (*(*ptr)[N])(params) │ │ │ └── 函数参数列表 │ │ └── 指向的数组长度 │ └── 指针声明符 └── 函数返回类型 ``` 4. **使用场景** - 需要传递整个函数指针数组时 - 需要修改函数指针数组的指向时 - 实现动态函数调度系统 5. **示例代码演示** ```c #include <stdio.h> int add(int a) { return a + 1; } int sub(int a) { return a - 1; } int main() { // 定义函数指针数组 int (*funcs[2])(int) = {add, sub}; // 定义指向数组的指针 int (*(*pFuncs)[2])(int) = &funcs; // 通过指针调用 for(int i = 0; i < 2; i++) { printf("Result: %d\n", (*pFuncs)[i](5)); } return 0; } ``` 6. **易错点注意** - 括号嵌套顺序错误会导致类型改变 - 数组长度必须与目标数组匹配 - 解引用时需要先取数组再访问元素 理解函数数组指针的关键是:它本质上是二级指针,指向一个包含函数指针的数组。通过分步类型定义使用typedef可以显著提高代码可读性。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值