提问人:JamesM 提问时间:4/20/2022 更新时间:6/3/2023 访问量:85
这里怎么了?而 (temp->ptr_Next != NULL) 失败,如果 (temp->ptr_Next == NULL) 有效
what's wrong here ? while (temp->ptr_Next != NULL) fails, if (temp->ptr_Next == NULL) works
问:
我一直在制作一个双链表,而 while 语句有点不稳定。我确定有一个简单的解释,但我没有看到它。谁能帮忙?
这正在工作(Embarcadero RAD studio 11.1 - C++ Builder - 经典编译器(不是Clang))
TDoubleLinkedNode* __fastcall TDoubleLinkedList::CreateNode ( int ID
, UnicodeString Name
)
{
// use "malloc" (or "new") to create the node in the heap
// it won't be deleted automatically when the function goes out of scope
// return the pointer to the structure
// let the calling function set PriorNode & NextNode
struct TDoubleLinkedNode* ReturnNode = (struct TDoubleLinkedNode*)malloc(sizeof(struct TDoubleLinkedNode));
ReturnNode->ID = ID;
ReturnNode->Name = Name;
ReturnNode->ptr_PriorNode = NULL;
ReturnNode->ptr_NextNode = NULL;
return ReturnNode;
}
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID
, UnicodeString Name
)
{
struct TDoubleLinkedNode* newNode = CreateNode(ID,Name);
if(this->IsEmpty)
{
// Head Pointer has not been initialised. Set as newNode
this->FHeadNode = newNode;
// This is the first Record.
newNode->ptr_PriorNode = NULL;
newNode->ptr_NextNode = NULL;
return;
}
else
{
struct TDoubleLinkedNode* oldHeadNode = this->FHeadNode;
struct TDoubleLinkedNode* tempNode = this->FHeadNode;
do // keep iterating until a break statement is triggered
{
if (tempNode->ptr_NextNode == NULL) // terminate the do while statement
{
break;
}
tempNode = tempNode->ptr_NextNode; // Move to the "Next" record
}
while (true); // always repeat...
tempNode->ptr_NextNode = newNode;
newNode->ptr_PriorNode = tempNode;
newNode->ptr_NextNode = NULL;
}
}
但是,如果我将
do
{
if (tempNode->ptr_NextNode == NULL)break;
}
while (true);
跟
while (tempNode->ptr_NextNode != NULL)
{
tempNode = tempNode->ptr_NextNode ;
}
当导致 tempNode 设置为 NULL 时,while 语句不会中断,从而使下面的语句失败(因为您无法将数据分配给不存在的对象)。tempNode->ptr_NextNode == NULL
tempNode->ptr_NextNode = newNode
我一直在逐步执行,并且当tempNode->ptr_NextNode == NULL 时肯定在运行,当我的理解是它不应该??
我敢肯定,这并不是唯一一团糟的领域(有很多时间声明)。 我正在添加 6 条测试记录,但只能检索 5 条!所以很明显有些事情发生了。如果你能阐明我不理解的地方,我将不胜感激
谢谢,J
答:
我已经有20年没有使用CBuilder了,但是如果你所说的正是发生的事情,如果将“do while”循环替换为“while”会改变行为,并且你可以清楚地看到当你逐步完成它时发生的疯狂事情,这对我来说也是不合逻辑的。
我不知道现在怎么样了,但是在本世纪初,由于 C++ Builder 与 Object Pascal 库有很多复杂的链接,因此遇到非常奇怪的情况并不少见,在调试中发生了疯狂的事情。过去有帮助的是做一个“重建所有”,可能删除我在项目中找到的所有临时文件。
也许它会有所帮助。
我也支持我们的同事关于更新代码以使用更合适的 C++ 替代方案的评论,如果可能的话,更简单、更高效的替代方案(我知道有时您可能正在使用可能不容易更新的遗留软件)。
实际上,由于内容损坏,您也很可能会看到未定义的行为,正如人们在评论中所说的那样。只有你能确定。
更新:我刚刚看到了 Remy Lebeau 刚刚发布的另一个答案,我想补充一点,他对 malloc 的不当使用是正确的。搜索谷歌,我看到UnicodeString似乎是Object Pascal中的一个对象,是吗?真的似乎是一个非 POD,无论如何你都会遇到麻烦,不要将 malloc 用于 C++ 对象。
评论
你的循环代码很好(尽管编码很奇怪,而且效率低下)。while
很有可能,您正在代码中的其他位置调用未定义/非法的行为,这可能会影响循环作为副作用。while
例如,不要在 C++ 中使用 /,而是使用 /。结构包含一个成员,该成员显然是非 POD 类类型,因为它被分配了一个值。当用于分配实例时,该成员的构造函数将不会运行,因此在将参数分配给成员时会调用未定义的行为。malloc()
free()
new
delete
TDoubleLinkedList
Name
UnicodeString
malloc()
TDoubleLinkedList
Name
ReturnNode->Name
话虽如此,您应该完全摆脱它,而是为自己添加一个适当的构造函数,例如:CreateNode()
TDoubleLinkedNode
struct TDoubleLinkedNode
{
int ID;
UnicodeString Name;
TDoubleLinkedNode *ptr_PriorNode;
TDoubleLinkedNode *ptr_NextNode;
TDoubleLinkedNode(int id, UnicodeString name, TDoubleLinkedNode *priorNode = NULL, TDoubleLinkedNode *nextNode = NULL) :
ID(id),
Name(name),
ptr_PriorNode(priorNode),
ptr_NextNode(nextNode)
{
}
};
然后可以简化:Add_ToEnd()
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID,
UnicodeString Name
)
{
TDoubleLinkedNode* newNode = new TDoubleLinkedNode(ID, Name);
if (!FHeadNode)
{
// Head Pointer has not been initialised. Set as newNode
FHeadNode = newNode;
return;
}
TDoubleLinkedNode* tempNode = FHeadNode;
while (tempNode->ptr_NextNode) {
tempNode = tempNode->ptr_NextNode; // Move to the "Next" record
}
tempNode->ptr_NextNode = newNode;
newNode->ptr_PriorNode = tempNode;
}
这实际上可以进一步简化:
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID,
UnicodeString Name
)
{
TDoubleLinkedNode** tempNode = &FHeadNode;
while (*tempNode) {
tempNode = &((*tempNode)->ptr_NextNode);
}
*tempNode = new TDoubleLinkedNode(ID, Name, *tempNode);
}
如果您向班级添加成员,则更是如此:FTailNode
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID,
UnicodeString Name
)
{
TDoubleLinkedNode **tempNode = (FTailNode) ? &(FTailNode->ptr_NextNode) : &FHeadNode;
FTailNode = new TDoubleLinkedNode(ID, Name, FTailNode);
*tempNode = FTailNode;
}
话虽如此,更好的解决方案是根本不手动创建链表。在标头中使用标准的 std::list
容器,例如:<list>
#include <list>
struct TNodeData
{
int ID;
UnicodeString Name;
};
class TDoubleLinkedList
{
private:
std::list<TNodeData> FData;
public:
...
void __fastcall Add_ToEnd(int ID, UnicodeString Name);
...
};
void __fastcall TDoubleLinkedList::Add_ToEnd ( int ID,
UnicodeString Name
)
{
FData.push_back(TNodeData{ID, Name});
}
评论
malloc()
free()
new
delete
TDoubleLinkedNode
Name
std::list
CreateNode
new
TDoubleLinkedNode
while
nullptr
NULL
// use "malloc"
-- 当包含或类似的非 POD 时,您的程序就会蓬勃发展。TDoubleLinkedNode
std::string
TDoubleLinkedNode::Name
UnicodeString