C语言双指针的常见用法

鉴于工作经验有限,目前遇到的双指针的用法有如下几种情况,后续工作中如发现有其他的用法,会更新上去。

1、用作函数的返回值,比较常见的是返回分配的堆内存地址。

下面用一个例子进行说明下:

/*****************************************************************************/
/**
* \brief       分配指定大小size的堆空间
* \param[out]  pst      分配的内存的地址
* \param[in]   size		需要分配的内存大小
* \return      返回值描述
* \remarks     其它信息
******************************************************************************/
bool get_memory(char *pst, unsigned int size)
{
	if (0 == size)
	{
		pst = NULL;
		return false;
	}

	pst = (char*)malloc(size);
	if (NULL == pst)
	{
		return false;
	}
	memset(pst, 0, size);

	return true;
}

int use_get_memory()
{
	char *pStr = NULL;
	char buf[] = "hello world.";
	get_memory(pStr, 1024);

	memcpy(pStr, buf, sizeof(buf));

	return 0;
}
当我们运行这段程序的时候,会发现程序崩溃了,出错的原因是对内存的非法访问,为什么会出现这种情况呢,我们明明已经分配了内存的啊,那我们就进行调试一下,看看问题究竟出现在哪里,vs的调试截图如下:

发现调用get_memory函数之后,pStr所指向的内存竟然是空的,可见问题就出现在这里。

函数参数的传递是按值传递的,指针类型的参数其实也是按值进行传递的,只不过传递的是变量的地址,按值传递会进行拷贝,下面用一个图进行解释。

调用get_memory后,pStr参数会进行拷贝传给get_memory,这里假设拷贝之后的参数为_pStr,执行malloc之后,_pStr指向的是分配的堆空间,而pStr指向的仍然是NULL,所以使用pStr进行操作的时候,会报内存非法访问的错误,而此时,get_memory返回后,新分配的内存(_pStr所指向的空间)没发使用,还会导致内存泄露。

正确的用法为

/*****************************************************************************/
/**
* \brief       分配指定大小size的堆空间
* \param[out]  pst      分配的内存的地址
* \param[in]   size		需要分配的内存大小
* \return      返回值描述
* \remarks     其它信息
******************************************************************************/
bool get_memory(char **pst, unsigned int size)
{
	if (0 == size)
	{
		pst = NULL;
		return false;
	}

	(*pst) = (char*)malloc(size);
	if (NULL == *pst)
	{
		return false;
	}
	memset(*pst, 0, size);

	return true;
}

int use_get_memory()
{
	char *pStr = NULL;
	char buf[] = "hello world.";

	if (get_memory(&pStr, 1024) == false)
	{
		return -1;
	}
	memcpy(pStr, buf, sizeof(buf));

	printf("%s\n", pStr);

	free(pStr);
	pStr = NULL;

	return 0;
}


传入的是pStr的地址,进入函数后进行解引用操作,就是对pStr实际地址进行操作。


2、用于申请动态一维数组,只是这个一维数组中存储的是指针类型。

一维数组中的每个指针又可以动态分配一个一维数组,即最终可以形成一个二维数组。

int** array_init(unsigned int size)
{
	int **ppArray = NULL;
	if (0 == size)
	{
		return NULL;
	}

	ppArray = (int**)malloc(sizeof(int*) * size);
	if (ppArray == NULL)
	{
		return NULL;
	}
	memset(ppArray, 0, sizeof(int*) * size);

	return ppArray;
}

void use_array()
{
	int **ppArr = NULL;
	int i = 0;
	ppArr = array_init(10);
	
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	for (i = 0; i < 10; i++)
	{
		ppArr[i] = &array[i];
	}

	for (int i = 0; i < 10; i++)
	{
		printf("%d\n", *(int*)(ppArr[i]));
	}

}

3、表示指针的地址,即指向指针的指针。

这里举个双向队列TAILQ的结构定义中使用的双指针。

#define TAILQ_ENTRY(type)                        \
struct {                                \
    struct type *tqe_next;    /* next element */            \
    struct type **tqe_prev;    /* address of previous next element */    \
}
这是TAILQ对两个指向前后两个元素指针的抽象,抽象为TAILQ_ENTRY结构体:tqe_next是指向下一个元素的指针,tqe_prev是一个二级指针,指针变量的地址,是前一个元素的tqe_next的地址,解引用(*tqe_prev)之后就是本元素的内存地址。


  • 21
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值