因为函数参数是按值传递的,所以要想改变变量,必须传递地址。
二级指针实际上就是指针变量的地址,如果传递二级指针,函数声明必须写**。
(void**)&必须是本质上就是指针变量的地址才可以做这样的转换,并不是说把一个一级指针也可以转换,void**的本质是标识一个二级指针。
&data就是(默认数据类型 **)&data,(void **)&data和&data还是同一块内存,只不过数据类型发生变化了。
如果默认数据类型是int,&data就是(int **)&data
一级指针:
void
swap ( int *a, int *b ){
int temp = 0;
temp = *a;
*a = *b;
*b = temp;
}int
main ( int argc, char **argv ){
int a,b;
a = 16;
b = 32;
swap(&a, &b);
return ( a - b );
}
二级指针:
void swap(int **a, int **b)
{
int t;
t =**a;
**a =**b;
**b=t;
}
int main()
{
int i = 3;
int j = 5;
int *p = &i;
int *q = &j;
swap(&p, &q);
}
文章图片
文章图片
高级一点使用void**只是为了通用,可以交换各种类型。
void swap(void **a, void **b)
{
void *t;
t =*a;
*a =*b;
*b=t;
}
int main()
{
int i = 3;
int j = 5;
int *p = &i;
int *q = &j;
char *s1="abc";
char *s2="def";
swap((void**)&p, (void**)&q);
swap((void**)&s1, (void**)&s2);
}
文章图片
注意char*是字符串指针,需要改变其对应的变量必须用地址,s1就是"abc"的起始地址,是不能被改变,要想改变s1必须用他的地址也就是&s1,所以需要void**:
void swap(void *a, void *b)
{
void *t;
t =a;
a =b;
b=t;
}
int main()
{
char *s1="abc";
char *s2="def";
swap((void*)s1, (void*)s2);
}
文章图片
【C语言|说透一级指针和二级指以及(void**)&在双链表中的应用】(void**)& 本质
在看《算法精解C语言描述》的双链表dlist.c的代码看到这样一段
算法精解:C语言描述\examples_unix\examples\chtbl\ex-1.c
int*data;
*data = https://www.it610.com/article/11;
if (dlist_remove(&list, element, (void **)&data) != 0)
return 1;
算法精解:C语言描述\examples_unix\source\dlist.c
int dlist_remove(DList *list, DListElmt *element, void **data) {/*****************************************************************************
**
*Do not allow a NULL element or removal from an empty list.*
**
*****************************************************************************/if (element == NULL || dlist_size(list) == 0)
return -1;
/*****************************************************************************
**
*Remove the element from the list.*
**
*****************************************************************************/*data = https://www.it610.com/article/element->data;
if (element == list->head) {/**************************************************************************
**
*Handle removal from the head of the list.*
**
**************************************************************************/list->head = element->next;
if (list->head == NULL)
list->tail = NULL;
else
element->next->prev = NULL;
}else {/**************************************************************************
**
*Handle removal from other than the head of the list.*
**
**************************************************************************/element->prev->next = element->next;
if (element->next == NULL)
list->tail = element->prev;
else
element->next->prev = element->prev;
}/*****************************************************************************
**
*Free the storage allocated by the abstract data type.*
**
*****************************************************************************/free(element);
/*****************************************************************************
**
*Adjust the size of the list to account for the removed element.*
**
*****************************************************************************/list->size--;
return 0;
}
其实很简单:
data是指针变量;
&data是data指针变量的地址所以传递他就是void**
看下面这幅调试图就明白了,起始&data就是(int **)&data,(void **)&data和&data还是同一块内存,只不过数据类型发生变化了。
文章图片
注意:原书有问题就是dlist_remove之后没有free()。
相对而言,redis那种风格的删除节点更好,因为是在函数体内释放节点的,而不是外面。关于二者的比较参考:
Redis:adlist.c
/* Remove the specified node from the specified list.
* It's up to the caller to free the private value of the node.
*
* This function can't fail. */
void listDelNode(list *list, listNode *node)
{
if (node->prev)
node->prev->next = node->next;
else
list->head = node->next;
if (node->next)
node->next->prev = node->prev;
else
list->tail = node->prev;
if (list->free) list->free(node->value);
zfree(node);
list->len--;
}
网上也有人遇到这个问题,人家自己写了个destroy函数,其实就是free
/*destroy */
void destroy(void *data)
{
free(data);
return;
}dlist_remove(&dlist_exp, p, (void **)&cb_ptr);
printf("the data of the third element: length = %d, width = %d, height = %d\n",
cb_ptr->length,
cb_ptr->width,
cb_ptr->height);
destroy(cb_ptr);
//free the memory
数据结构学习之双向链表结构数据结构学习之单向链表结构
补充说明:
element是链表结点,这个结点里含有个数据域data,它只是个指针变量,它指向的是应用层分配的空间,
*data指向的不是element内存,而是指向element中的data域,这个域是由应用层来管理的,即当element被删除时,其element内的data域应该保存由应用层开发人员负责释放。
element是DListElmt的一个变量,变量内含有个data指针域, 接口的data域是二级指针,这两个不要混淆了.element被删除后,其data域本身所占用的资源被回收(因为是指针,占4字节),但是并不意味着data所指向的资源也被回收,这是不同的两个概念.
*data = https://www.it610.com/article/element->data; 这句话将element内data域所指向的物理内存地址记录下来,返回给应用层处理.
《算法精解》这个哈希表的例子作者是给出了删除后要free的代码:
C语言描述\examples_unix\examples\chtbl\ex-1.c
if ((data = https://www.it610.com/article/(char *)malloc(sizeof(char))) == NULL)
return 1;
*data ='G';
if ((retval = chtbl_insert(&htbl, data)) != 0)
free(data);
fprintf(stdout, "Trying to insert G again...Value=https://www.it610.com/article/%d (1=OK)/n", retval);
fprintf(stdout, "Removing d, G, and S\n");
c = 'd';
data = https://www.it610.com/article/&c;
if (chtbl_remove(&htbl, (void **)&data) == 0)
free(data);
c ='G';
data = https://www.it610.com/article/&c;
if (chtbl_remove(&htbl, (void **)&data) == 0)
free(data);
其实在作者的代码结尾dlist_destroy函数也说明这一点:
算法精解:C语言描述\examples_unix\examples\chtbl\ex-1.c
dlist_destroy(&list);
算法精解:C语言描述\examples_unix\source\dlist.c
void dlist_destroy(DList *list) {void*data;
/*****************************************************************************
**
*Remove each element.*
**
*****************************************************************************/while (dlist_size(list) > 0) {if (dlist_remove(list, dlist_tail(list), (void **)&data) == 0 && list->
destroy != NULL) {/***********************************************************************
**
*Call a user-defined function to free dynamically allocated data.*
**
***********************************************************************/list->destroy(data);
}}
这里是调用了list->destroy的。
推荐阅读
- C语言学习|第十一届蓝桥杯省赛 大学B组 C/C++ 第一场
- 【C】题目|【C语言】题集 of ⑥
- 单片机|自学单片机好找工作吗(会单片机能找什么工作?)
- 单片机|keil把源代码生成lib的方法
- c语言|一文搞懂栈(stack)、堆(heap)、单片机裸机内存管理malloc
- c语言|C语言初期学习遇到的特殊点 【三子棋详解】【初学者福音,详细总结,复习能手】
- 笔记|C语言数据结构——二叉树的顺序存储和二叉树的遍历
- 个人理解|【C语言基础之类型转换】
- c语言|【C语言】自定义类型 结构体 枚举 联合
- 学习分享|【C语言函数基础】