【问题标题】:Assign a pointer to a struct that is a member of a struct of the same type to another pointer to a struct of the same type将指向同类型结构成员的结构的指针分配给指向同类型结构的另一个指针
【发布时间】:2019-03-18 04:58:10
【问题描述】:

即使对我来说,这个问题听起来也非常令人困惑,而且它可能看起来很明显或已经回答,但我已经搜索了很多,虽然我发现了有趣的东西,但我没有找到完全适合我的问题的答案。这是一些 C 代码,可以更好地显示我的疑问:

typedef struct Node_struct {
   char keyLine[100];
   int occurrences;
   struct Node* leftChild;
   struct Node* rightChild;
   struct Node* parent;
} Node;

typedef struct Tree_struct {
   Node* root;
} Tree;

int insertNode(Tree* myTree, Node* newNode) {
    ...
    Node* currentNode = myTree->root;
    ...
    if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
        currentNode = (Node*)currentNode->leftChild;
    }
    ...
 }

这段代码正确吗?由于currentNodeNode* 类型,而currentNode->leftChildstruct Node* 类型,我必须转换(Node*)currentNode->leftChild 以便它可以分配给currentNode。但我不确定这是否正确、必要,或者是否有更好的方法来做同样的事情。

同样,我也有这个:

Node* coverNode = NULL;
...
coverNode->leftChild = (struct Node*)newNode;

【问题讨论】:

  • 名称Nodestruct Node 的同义词。 (对于任何typedef X Y;Y 成为X 的同义词——在您的情况下,Xstruct NodeYNode。)currentNode = (Node*)currentNode->leftChild; 中的演员是不必要的,因为它是一个空操作——struct Node *Node * 类型是相同指针类型的两个名称。
  • @JonathanLeffler typedef struct Node_struct { /* ... */ struct Node* leftChild; /* ... */ } Node; ... struct Node_structstruct NodeNode 一样吗?我知道Nodestruct Node_struct 相同,但是用于声明结构成员的struct Node * 呢?
  • @JonathanLeffler 你以后可能会好好感谢我。嘘......去修理你的;)
  • @Swordfish:感谢您指出我误读了这个问题。我已经“调整”(扩展)我的答案来处理“实践”和“理论”。

标签: c pointers struct


【解决方案1】:

c++ 中,当你说struct Node 时,Node [立即] 变成了一个类型。所以,你可以说:

struct Node {
    char keyLine[100];
    int occurrences;
    Node *leftChild;
    Node *rightChild;
    Node *parent;
};

struct Tree {
    Node *root;
};

int
insertNode(Tree *myTree, Node *newNode)
{
    Node *currentNode = myTree->root;
    if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
        currentNode = currentNode->leftChild;
    }
}

但是,在c 中,它只是在“tag”命名空间中,并且没有定义了一个类型。因此,你想要:

typedef struct Node {
    char keyLine[100];
    int occurrences;
    struct Node *leftChild;
    struct Node *rightChild;
    struct Node *parent;
} Node;

typedef struct Tree_struct {
    Node *root;
} Tree;

int
insertNode(Tree *myTree, Node *newNode)
{
    Node *currentNode = myTree->root;
    if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
        currentNode = currentNode->leftChild;
    }
}

作为替代方案,您可以使用前向声明

// forward declaration
struct Node;
typedef struct Node Node;

struct Node {
    char keyLine[100];
    int occurrences;
    Node *leftChild;
    Node *rightChild;
    Node *parent;
};

typedef struct Tree_struct {
    Node *root;
} Tree;

int
insertNode(Tree *myTree, Node *newNode)
{
    Node *currentNode = myTree->root;
    if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
        currentNode = currentNode->leftChild;
    }
}

请注意,struct 名称确实必须与类型名称匹配:

// forward declaration
struct Node_struct;
typedef struct Node_struct Node;

struct Node_struct {
    char keyLine[100];
    int occurrences;
    Node *leftChild;
    Node *rightChild;
    Node *parent;
};

typedef struct Tree_struct {
    Node *root;
} Tree;

int
insertNode(Tree *myTree, Node *newNode)
{
    Node *currentNode = myTree->root;
    if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
        currentNode = currentNode->leftChild;
    }
}

为了允许您的两个结构的交叉链接,我们可以这样做:

// forward declaration
struct Node_struct;
typedef struct Node_struct Node;

struct Tree_struct;
typedef struct Tree_struct Tree;

struct Node_struct {
    char keyLine[100];
    int occurrences;
    Node *leftChild;
    Node *rightChild;
    Node *parent;
    Tree *tree;
};

struct Tree_struct {
    Node *root;
};

int
insertNode(Tree *myTree, Node *newNode)
{
    Node *currentNode = myTree->root;
    if (caseSenCmpString(newNode->keyLine, currentNode->keyLine) == -1) {
        currentNode = currentNode->leftChild;
    }
}

【讨论】:

  • 你的意思是“标签”命名空间? C11§6.2.3 Name spaces of identifiers
  • @JonathanLeffler 谢谢。一如既往的准确。而且,我惊叹于您调用在线文档、找到相关部分并在此处发布链接的速度有多快。
  • 我“作弊”——我刚刚写了一个非常相似的 comment 来回答一个老问题的另一个答案,而且链接仍然隐藏在我的粘贴缓冲区中,甚至。
【解决方案2】:

应该写什么

假设问题中的代码是这样写的:

typedef struct Node {     // Not Node_struct as in the question!
   char keyLine[100];
   int occurrences;
   struct Node* leftChild;
   struct Node* rightChild;
   struct Node* parent;
} Node;

那么名称Node 将是struct Node 的同义词(别名)。 (对于任何typedef X Y;Y 成为X 类型的同义词——在您的情况下,X 将是 struct NodeY 将是节点。)

演员:

currentNode = (Node *)currentNode->leftChild;

将是不必要的(但大多数情况下是无害的),因为它是无操作的——struct Node *Node * 类型将是同一指针类型的两个名称。类似的:

coverNode->leftChild = (struct Node *)newNode;

演员阵容是不必要的,但(大部分)是无害的。将人们与演员混淆的风险很小。最好尽可能避免使用类型转换,并且最好不要使用类型转换:

currentNode = currentNode->leftChild;
coverNode->leftChild = newNode;

写了什么

typedef struct Node_struct {
   char keyLine[100];
   int occurrences;
   struct Node* leftChild;
   struct Node* rightChild;
   struct Node* parent;
} Node;

现在我们使用了三个类型名称:struct Node_structstruct NodeNode。在这种情况下,struct Node_structNode 是同义词,而struct Node 是不完整的结构类型(或者,至少它不是由问题中的任何代码完成的)。它与struct Node_structNode 完全无关,除非巧合的是它在结构内部被引用。

使用这种表示法,强制转换是“必要的”,因为您在指向不相关类型(struct Node *struct Node_struct *)的指针之间进行转换。幸运的是,有一些规则规定所有结构类型指针都是可相互转换的,并且必须具有相同的大小和对齐要求(C11 §6.2.5 Types ¶28§6.3.2.3 Pointers ¶7)。

但是您应该删除Node_struct_struct 部分,以使此答案第一部分的规则适用。在 C 中,使用 (IMO) 是明智的:

typedef struct SomeTag SomeTag;

以便您随后可以使用SomeTag * 等。第一个SomeTag 位于标签名称空间中,并且与第二个SomeTag 不冲突,后者位于普通标识符名称空间中。见 C11§6.2.3 Name spaces of identifiers

另见:

【讨论】:

    猜你喜欢
    • 2013-12-13
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 2018-05-16
    • 1970-01-01
    • 2015-01-24
    • 2021-12-01
    • 1970-01-01
    相关资源
    最近更新 更多