当前位置:  开发笔记 > 编程语言 > 正文

指向指针的指针如何在C中工作?

如何解决《指向指针的指针如何在C中工作?》经验,为你挑选了10个好方法。

如何指针指针在C中工作?你什么时候使用它们?



1> Stephan202..:

我们假设一台8位计算机具有8位地址(因此只有256字节的存储器).这是该内存的一部分(顶部的数字是地址):

  54   55   56   57   58   59   60   61   62   63   64   65   66   67   68   69
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|    | 58 |    |    | 63 |    | 55 |    |    | h  | e  | l  | l  | o  | \0 |    |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+

你在这里看到的是,在地址63处,字符串"hello"开始.所以在这种情况下,如果这是内存中唯一出现的"hello"那么,

const char *c = "hello";

...定义c为(只读)字符串"hello"的指针,因此包含值63. c必须将其本身存储在某处:在上面的示例位置58.当然,我们不仅可以指向字符,还要其他指针.例如:

const char **cp = &c;

现在cp指向c,也就是说,它包含地址c(即58).我们可以走得更远.考虑:

const char ***cpp = &cp;

现在cpp存储地址cp.因此它具有值55(基于上面的示例),并且您猜对了它:它本身存储在地址60处.


至于为什么一个人使用指针指针:

数组的名称通常会产生其第一个元素的地址.因此,如果数组包含类型的元素,则对数组t的引用具有类型t *.现在考虑一个类型数组的数组t:自然地,对这个2D数组的引用将具有type (t *)*= t **,因此是指向指针的指针.

即使字符串数组听起来是一维的,它实际上也是二维的,因为字符串是字符数组.因此:char **.

如果要更改类型的变量,函数f将需要接受类型的参数.t **t *

许多其他原因太多,无法在此列出.


是的好例子..我明白它们是什么......但是如何以及何时使用它们更重要..现在......
char*c ="hello"应该是const char*c ="hello".另外,最多会误导"数组存储为第一个元素的地址".数组存储为...数组.它的名字通常会产生指向其第一个元素的指针,但并非总是如此.关于指针的指针,我只想说当函数必须修改作为参数传递的指针时,它们很有用(然后你将指针传递给指针).
除非我误解了这个答案,否则看起来不对.c存储在58并指向63,cp存储在55并指向58,并且cpp未在图中表示.
斯蒂芬很好地复制了Kernighan和Richie的The C Programming Language中的图表.如果您正在编写C语言,并且没有本书并且使用纸质文档很酷,我强烈建议您明白这一点,(相当)适度的费用将在生产力方面很快收回成本.它的例子中往往非常清楚.

2> Brian R. Bon..:

如何指针指针在C中工作?

首先,指针是一个变量,就像任何其他变量一样,但它保存变量的地址.

指向指针的指针是一个变量,就像任何其他变量一样,但它保存变量的地址.该变量恰好是一个指针.

你什么时候使用它们?

当您需要返回指向堆上某些内存的指针但不使用返回值时,可以使用它们.

例:

int getValueOf5(int *p)
{
  *p = 5;
  return 1;//success
}

int get1024HeapMemory(int **p)
{
  *p = malloc(1024);
  if(*p == 0)
    return -1;//error
  else 
    return 0;//success
}

你这样称呼它:

int x;
getValueOf5(&x);//I want to fill the int varaible, so I pass it's address in
//At this point x holds 5

int *p;    
get1024HeapMemory(&p);//I want to fill the int* variable, so I pass it's address in
//At this point p holds a memory address where 1024 bytes of memory is allocated on the heap

还有其他用途,例如每个C程序的main()参数都有一个指向argv指针的指针,其中每个元素都包含一个chars数组,这些字符是命令行选项.你必须小心,但是当你使用指针指针指向2维数组时,最好使用指向2维数组的指针.

它为什么危险?

void test()
{
  double **a;
  int i1 = sizeof(a[0]);//i1 == 4 == sizeof(double*)

  double matrix[ROWS][COLUMNS];
  int i2 = sizeof(matrix[0]);//i2 == 240 == COLUMNS * sizeof(double)
}

以下是正确完成2维数组指针的示例:

int (*myPointerTo2DimArray)[ROWS][COLUMNS]

如果要为ROWS和COLUMNS支持可变数量的元素,则不能使用指向2维数组的指针.但是当你事先知道时,你会使用二维数组.



3> VonC..:

我喜欢这个指向指针用法的"真实世界"代码示例,在Git 2.0中,提交7b1004b:

莱纳斯曾经说过:

我实际上希望更多人理解真正的核心低级编码.不是很大,很复杂的东西,如无锁名称查找,但只是很好地使用指针指针等
.例如,我看到太多的人通过跟踪"prev"条目删除单链接列表条目,然后删除条目,做类似的事情

if (prev)
  prev->next = entry->next;
else
  list_head = entry->next;

每当我看到这样的代码时,我就会去"这个人不理解指针".而且很遗憾很常见.

理解指针的人只使用" 指向条目指针的指针 ",并使用list_head的地址初始化它.然后当他们遍历列表时,他们可以在不使用任何条件的情况下删除条目,只需执行一个

*pp =  entry->next

http://i.stack.imgur.com/bpfxT.gif

应用这种简化使我们即使在添加2行注释时也会从此函数中丢失7行.

-   struct combine_diff_path *p, *pprev, *ptmp;
+   struct combine_diff_path *p, **tail = &curr;

克里斯指出,在评论 2016年视频" Linus Torvalds的的双指针的问题通过" 菲利普Buuck.


kumar在博客文章" Linus on Understanding Pointers " 的评论中指出,Grisha Trubetskoy解释说:

想象一下,你有一个链表定义为:

typedef struct list_entry {
    int val;
    struct list_entry *next;
} list_entry;

您需要从头到尾迭代它并删除其值等于to_remove值的特定元素.
更明显的方法是:

list_entry *entry = head; /* assuming head exists and is the first entry of the list */
list_entry *prev = NULL;

while (entry) { /* line 4 */
    if (entry->val == to_remove)     /* this is the one to remove ; line 5 */
        if (prev)
           prev->next = entry->next; /* remove the entry ; line 7 */
        else
            head = entry->next;      /* special case - first entry ; line 9 */

    /* move on to the next entry */
    prev = entry;
    entry = entry->next;
}

我们上面做的是:

迭代列表直到输入NULL,这意味着我们已到达列表的末尾(第4行).

当我们遇到一个我们想删除的条目时(第5行),

我们将当前下一个指针的值赋给前一个指针,

从而消除了当前元素(第7行).

上面有一个特例 - 在迭代开始时没有先前的条目(prevNULL),因此要删除列表中的第一个条目,你必须修改head本身(第9行).

Linus所说的是,通过使前一个元素成为指针而不仅仅是一个指针,可以简化上面的代码.
然后代码如下所示:

list_entry **pp = &head; /* pointer to a pointer */
list_entry *entry = head;

while (entry) {
    if (entry->val == to_remove)
        *pp = entry->next;

    pp = &entry->next;
    entry = entry->next;
}

上面的代码与前面的变体非常相似,但请注意我们不再需要关注列表第一个元素的特殊情况,因为pp它不在NULL开头.简单而聪明.

此外,该线程中有人评论说这更好的原因是因为*pp = entry->next是原子的.它绝对不是原子的.
上面的表达式包含两个解引用运算符(*->)和一个赋值,这三个东西都不是原子的.
这是一个常见的误解,但是在C中几乎没有任何东西应该被认为是原子的(包括++--运算符)!


这有助于更好地理解 - http://grisha.org/blog/2013/04/02/linus-on-understanding-pointers/

4> Edd..:

在涵盖大学编程课程的指针时,我们得到了两个关于如何开始学习它们的提示.第一个是查看Pointer Fun With Binky.第二个是考虑刘易斯卡罗尔穿越镜子的黑道'之眼

"你很伤心,"骑士用一种焦虑的语气说:"让我唱一首歌来安慰你."

"这很长吗?"爱丽丝问道,因为那天她听到了很多诗歌.

"这很长,"骑士说,"但它非常非常漂亮.每个听到我的人都会唱歌 - 无论是给他们的眼睛带来泪水,还是 - "

"或者是什么?"爱丽丝说,因为骑士突然停顿了一下.

"或者它没有,你知道.这首歌的名字叫做'Haddocks'Eyes.'"

"哦,这就是这首歌的名字,是吗?"爱丽丝说,试着感兴趣.

"不,你不明白,"骑士说,看起来有点烦恼."这就是所谓的名字.这个名字真的是'老年人'."

"然后我应该说'这就是这首歌的名字'?"爱丽丝纠正了自己.

"不,你不应该:那是另一回事!这首歌被称为'方式和手段':但这只是它的名字,你知道!"

"那么,那首歌是什么?"爱丽丝说,他此时已经完全不知所措了.

"我来了,"骑士说."这首歌真的是'A-Sitting On A Gate':这首曲子是我自己的发明."



5> aJ...:

你可能想读这个:指针指针

希望这有助于澄清一些基本的疑虑.



6> epatel..:

指向指针的指针也称为句柄.一种用法通常是在对象可以在内存中移动或移除时.一个人经常负责锁定和解锁对象的使用,因此在访问对象时不会移动它.

它经常用在内存受限的环境中,即Palm OS.

computer.howstuffworks.com链接>>

www.flippinbits.com链接>>



7> Alex Balasho..:

当需要引用指针时.例如,当您希望修改在被调用函数内的调用函数范围内声明的指针变量的值(地址指向)时.

如果将单个指针作为参数传递,则将修改指针的本地副本,而不是调用范围中的原始指针.使用指针指针,您可以修改后者.



8> msc..:

考虑下面的图和程序,以更好地理解这个概念.

双指针图

根据该图,ptr1是一个单指针,它具有变量num的地址.

ptr1 = #

类似地,ptr2指向指针(双指针)的指针,它具有指针ptr1的地址.

ptr2 = &ptr1;

指向另一个指针的指针称为双指针.在这个例子中,ptr2是一个双指针.

上图中的值:

Address of variable num has : 1000
Address of Pointer ptr1 is: 2000
Address of Pointer ptr2 is: 3000

例:

#include 

int main ()
{
   int  num = 10;
   int  *ptr1;
   int  **ptr2;

   // Take the address of var 
   ptr1 = #

   // Take the address of ptr1 using address of operator &
   ptr2 = &ptr1;

   // Print the value
   printf("Value of num = %d\n", num );
   printf("Value available at *ptr1 = %d\n", *ptr1 );
   printf("Value available at **ptr2 = %d\n", **ptr2);
}

输出:

Value of num = 10
Value available at *ptr1 = 10
Value available at **ptr2 = 10



9> Luke Schafer..:

它是指向指针地址值的指针。(我知道那太可怕了)

基本上,它使您可以将指针传递到另一个指针的地址值,因此您可以修改子函数指向另一个指针的位置,例如:

void changeptr(int** pp)
{
  *pp=&someval;
}



10> Igor Oks..:

你有一个包含某个地址的变量.那是一个指针.

然后你有另一个变量,它包含第一个变量的地址.那是指向指针的指针.

推荐阅读
放ch养奶牛
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有