【问题标题】:Modify pointer of pointer of struct修改struct指针的指针
【发布时间】:2016-07-04 22:25:21
【问题描述】:

我创建了一个结构,将指针的指针传递给函数,然后调用malloc()。这一切都很好。 但是,如果我尝试访问内存,程序就会冻结。如果我调用另一个函数并更改访问内存,那么一切正常。

void test(TFeld *Feld, TEinstellung E)
{
int i;

    for (i=0;i<E.Groesse_X*E.Groesse_Y;i++)
    {
        Feld[i].Schiff_Vorhanden = false;
        Feld[i].Schiff_Versunken = false;
        Feld[i].Ueberprueft = false;
    }   
}

void initField (TEinstellung E, TFeld **Feld)
{
    int i;

    *Feld = (TFeld*)malloc(E.Groesse_X*E.Groesse_Y*sizeof(TFeld));

    test(*Feld,E);

    /*  for (i=0;i<E.Groesse_X*E.Groesse_Y;i++)
    {
        Feld[i]->Schiff_Versunken = (bool*)false;
    //  (*Feld[i]).Schiff_Versunken = false;
        //Feld[i]->Ueberprueft = false;
    } */
}

TFeld 的定义:

typedef struct TFeld
{
    bool Schiff_Vorhanden = false;
    bool Ueberprueft = false;
    bool Schiff_Versunken = false;
} TFeld;

我注释掉的部分只是在使用测试功能时使程序崩溃。

谁能解释一下这种行为。

【问题讨论】:

  • 你写Feld[i]-&gt;的地方可能是指(*Feld)[i].
  • 在您的struct typedef 中,您可以在第一次使用TFeld 时省略它。如typedef struct { ... } TFeld;

标签: c pointers struct malloc


【解决方案1】:

有什么问题?

InitField() 中,参数Feld 被声明为指向TFeld 的指针。

*Feld 因此是指向TFeld 的指针。它被正确初始化为正确大小的新分配的内存区域。

然后你调用test() 传递*Feld 作为参数。不幸的是,您也将参数称为Feld,因此这些变量的类型不同,可能会引起一些麻烦。但这不是问题。测试功能应该做你所期望的。

当您返回InitField() 时,您将尝试访问已初始化的元素:

Feld[i]->Schiff_Versunken = ... //ouch !!!

这将指针指向指针并访问该表中的第 i 个指针。但是由于你的指针只是一个指针而不是一个数组,你会得到一个完全损坏的指针。然后,您使用-&gt; 取消引用这个流氓指针,假装它指向TFeld。然后,当您为此流氓地址分配一个值时,您有未定义的行为(可能是段错误,可能是冻结,可能是任何东西)。

编辑:关于指针解引用的附加信息:

运算符*-&gt;[] 有一个您必须习惯的order of precendence。来看看TFeld **Feld

  • *Feld[i]*(Feld[i]) 相同,因为首先应用 [],然后仅应用 *。顺便说一句,再进一步,应用指针算术规则,它与*(*(Feld+i)) 相同。无论如何,这不是你想要的
  • *Feld[i].xxx*((Feld[i]).xxx) 相同,因为 . 的优先级高于 *。这不会编译,因为Feld[i] 不是TFeld 类型
  • Feld[i]-&gt;xxx(Feld[i])-&gt;xxx 相同(因为[]-&gt; 具有相同的优先级,但是当两者出现时,它们是从左到右应用的)。这与(*(Feld[i])).xxx 进一步相同。仍然不是你想要的。

但让我们可视化以了解以不同顺序应用解引用的影响。最重要的是你需要的。底部是你应该避免的:

如何解决?

我建议把注释掉的部分改成:

for (i=0;i<E.Groesse_X*E.Groesse_Y;i++)
{
    (*Feld)[i].Schiff_Versunken = false;
    (*Feld)[i].Schiff_Versunken = false;
    (*Feld)[i].Ueberprueft = false;
} 

或者如果你不喜欢星号和括号:

    Feld[0][i].Schiff_Versunken = false;
    Feld[0][i].Schiff_Versunken = false;
    Feld[0][i].Ueberprueft = false;

双重间接总是有点​​棘手。每当您有疑问时,请添加一些括号。

最后一句话:我在这里假设InitField() 是用指向TFeld 的指针的有效指针调用的,因此*Feld 毫无疑问会指向指向TFeld 的指针。如果是这种情况,您的内存分配指针可能会写入内存中的任何位置,从而再次导致内存损坏。如果有疑问,请对您的问题进行编辑,显示调用代码,以便我检查。

【讨论】:

  • 好的,所以我猜想这样称呼它 (*Feld[i]).Schiff_Versunken = false;等于 *Feld[i]->Schiff_Versunken = false;正确的?因为那是我首先不明白的 (*Feld)[i].Schiff_Versunken = false;不同于 (*Feld[i]).Schiff_Versunken = false;
  • @nuclear 我理解你的困惑。请参阅我关于优先顺序的编辑。我还添加了一个模式来显示 (*Feld)[i]*Feld[i] 的不同含义
【解决方案2】:

Feld 是指向结构数组的指针。所以你必须 (1) 取消引用指针,然后 (2) 索引到数组,最后 (3) 访问结构内的字段。

for 循环中有两种写法;你更喜欢哪个是品味问题:

(*Feld)[i].Schiff_Versunken = false;  // option 1
(*Feld + i)->Schiff_Versunken = false; // option 2

【讨论】:

    【解决方案3】:

    在函数内部执行和直接执行是有区别的。因为这里Feld是指向test()中结构的指针,而它是指向initField()中结构的指针。

    当你使用 malloc() 时,指向结构的指针被初始化,因此在test() 内访问时它没有崩溃。但是指向指针的指针仍然未初始化,因此导致initField()中的seg错误。

    所以你必须正确分配双指针。检查此链接以供参考:C - dynamic memory allocation using double pointer

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-19
      • 1970-01-01
      • 2016-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多