我正在制作一个我基本上工作的链表,但我需要扩展我目前的工作但是我遇到了问题。
我有一个存储来自电话的出站呼叫支路的结构。我将这些调用存储在链接列表中,该列表定义如下:
typedef struct CallLogSearchOutboundStruct
{
char * target;
float duration;
char * cleardownCause;
BOOL allowOverwrite;
struct CallLogSearchOutboundStruct * nextLeg;
} callLogSearchOutboundStruct;
我的基本代码工作正常,我可以使用以下代码成功地将新的外拨电话添加到链接列表的末尾:
void insertOutboundLegToList(callLogSearchOutboundStruct * outboundLeg, char * target, float duration, int cleardown, BOOL overwriteFirstOutboundLegs)
{
f (outboundLeg->target == NULL)
{
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
outboundLeg->nextLeg = NULL;
}
else
{
if (overwriteFirstOutboundLegs == FALSE)
{
while (outboundLeg->nextLeg != NULL)
{
outboundLeg = outboundLeg->nextLeg;
}
}
if (outboundLeg->nextLeg == NULL)
{
outboundLeg->nextLeg = (callLogSearchOutboundStruct*)malloc(sizeof(callLogSearchOutboundStruct));
outboundLeg = outboundLeg->nextLeg;
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->nextLeg = NULL;
}
else
{
outboundLeg->target = NULL;
outboundLeg->duration = 0;
outboundLeg->cleardownCause = NULL;
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
}
}
}
这段代码正在运行但是我需要修改它,这样如果设置了allowOverwrite标志,它将首先在链表中的第一个出站段,覆盖它并将第一段的覆盖设置为false,但所有其他列表中的腿被设置为允许覆盖。
因此,当需要插入新的出站呼叫时,如果覆盖第一个分支设置为false,则程序将需要遍历每个出站分支并检查该分支的允许覆盖是否设置为true并且如果是这样覆盖那条腿然后将overwrite标志设置为false,那么再次在下一个出站支路上,继续循环直到它看到allow overwrite true,overwrite并设置为false,这应该继续直到下一条腿为NULL然后它只是正常情况下将出站腿插入末端。
我认为我的基本逻辑是正确的,然而,当我从循环中断时,我似乎继续对第一条腿进行NULL操作,所以最终没有出站的腿。
以下是我修改代码以尝试实现我需要的方法。
if (overwriteFirstOutboundLegs == TRUE)
{
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
//Loop through existing outbound legs and set overwrite flag to TRUE
while (outboundLeg->nextLeg != NULL)
{
outboundLeg = outboundLeg->nextLeg;
outboundLeg->allowOverwrite = TRUE;
}
outboundLeg->nextLeg = NULL;
}
else
{
if (outboundLeg->target == NULL)
{
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
outboundLeg->nextLeg = NULL;
}
else
{
if (outboundLeg->nextLeg == NULL)
{
outboundLeg->nextLeg = (callLogSearchOutboundStruct*)malloc(sizeof(callLogSearchOutboundStruct));
outboundLeg = outboundLeg->nextLeg;
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
outboundLeg->nextLeg = NULL;
}
else
{
while (outboundLeg->nextLeg != NULL)
{
outboundLeg = outboundLeg->nextLeg;
if (outboundLeg->allowOverwrite == TRUE)
{
break;
}
}
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
outboundLeg->nextLeg = NULL;
}
}
}
我使用以下代码调用该函数:
insertOutboundLegToList(outboundCallLegStartPtr, targetBuffer, durationBuffer, atoi(rowReport[cleardownColIndex]), overwriteFirstOutboundLegs);
下面附图也显示了插入新腿所需的流程。
感谢您提供的任何帮助。
答案 0 :(得分:1)
在您尝试遍历链接列表之前,似乎有一个问题(可能还有其他问题)正在设置outboundLeg->nextLeg = NULL;
。因此,您要终止它,因此永远无法将其余部分设置为允许覆盖。看起来像是一个复制粘贴错误。
编辑:另一个潜在的问题是,如果在列表中遇到overwriteFirstOutboundLegs == FALSE
和传入的outboundLeg->nextLeg != NULL
以及没有outboundLeg->allowOverwrite == TRUE
,那么最后一个列表项将被覆盖(即使它标记为不允许覆盖)而不是分配新的结构以附加到列表的末尾。
答案 1 :(得分:1)
我发现用一个专门针对当前问题的小程序来探索实际问题是有用的。
首先,我认为图中有错误或遗漏。我相信你希望永远清除插入或替换腿的overwrite
标志。那么,以下示例程序的作用是什么:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
typedef struct node {
struct node *next;
int overwrite;
int value;
} node;
node *insert(node **const listptr, int value, int overwrite)
{
/* No list specified? */
if (!listptr) {
errno = EINVAL;
return NULL;
}
/* Empty list? */
if (!*listptr) {
node *newnode;
newnode = malloc(sizeof *newnode);
if (!newnode) {
errno = ENOMEM;
return NULL;
}
newnode->next = NULL;
newnode->value = value;
newnode->overwrite = 0;
*listptr = newnode;
return newnode;
}
if (overwrite) {
node *const currnode = *listptr;
node *temp;
/* Overwrite contents */
currnode->value = value;
currnode->overwrite = 0;
/* Set overwrite flag for all nodes that follow */
temp = currnode->next;
while (temp) {
temp->overwrite = 1;
temp = temp->next;
}
return currnode;
} else {
node **ptr = listptr;
node *currnode = *listptr; /* always equal to *ptr */
/* Find the first overwritable node */
while (currnode && !currnode->overwrite) {
ptr = &currnode->next;
currnode = currnode->next;
}
/* Found an overwritable node? */
if (currnode) {
currnode->value = value;
currnode->overwrite = 0;
return currnode;
}
/* Construct a new node to be appended to the list. */
currnode = malloc(sizeof *currnode);
if (!currnode) {
errno = ENOMEM;
return NULL;
}
currnode->next = NULL;
currnode->value = value;
currnode->overwrite = 0;
/* Append to the list. */
*ptr = currnode;
return currnode;
}
}
void display(const char *const header, const node *list, const char *const footer)
{
if (header)
fputs(header, stdout);
if (list) {
do {
if (list->overwrite)
printf("/%d", list->value);
else
printf("%d", list->value);
list = list->next;
if (list)
putchar(' ');
} while (list);
} else
fputs("(empty)", stdout);
if (footer)
fputs(footer, stdout);
}
int main(int argc, char *argv[])
{
node *list = NULL;
int arg, value;
char dummy;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s VALUE ... VALUE\n", argv[0]);
fprintf(stderr, "Where VALUE is\n");
fprintf(stderr, " /INTEGER to insert-overwrite INTEGER, or\n");
fprintf(stderr, " INTEGER to insert INTEGER normally.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
display("Initial list: ", list, ".\n");
for (arg = 1; arg < argc; arg++) {
if (sscanf(argv[arg], " /%d %c", &value, &dummy) == 1) {
if (!insert(&list, value, 1)) {
fflush(stdout);
fprintf(stderr, "Cannot insert-overwrite %s: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
} else
printf("Inserted %d with overwrite set:\n", value);
} else
if (sscanf(argv[arg], " %d %c", &value, &dummy) == 1) {
if (!insert(&list, value, 0)) {
fflush(stdout);
fprintf(stderr, "Cannot insert %s: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
} else
printf("Inserted %d:\n", value);
} else {
fflush(stdout);
fprintf(stderr, "%s: Not a valid VALUE.\n", argv[arg]);
return EXIT_FAILURE;
}
display("\t", list, ".\n");
}
display("Final list: ", list, ".\n");
return EXIT_SUCCESS;
}
这个想法是你给测试序列作为命令行参数,每个值都是一个整数(大大简化你的腿定义!)。如果应该使用覆盖设置插入值,请使用斜杠取代该值。
您可以使用例如
编译上述内容gcc -W -Wall -O3 example.c -o example
让我们考虑测试序列1 2 3 /4 5 /6
,这意味着我们按顺序插入六个第一个正整数,但4
和6
设置了覆盖标志(并且未设置为全部其他):
./example 1 2 3 /4 5 /6
输出
Initial list: (empty).
Inserted 1:
1.
Inserted 2:
1 2.
Inserted 3:
1 2 3.
Inserted 4 with overwrite set:
4 /2 /3.
Inserted 5:
4 5 /3.
Inserted 6 with overwrite set:
6 /5 /3.
Final list: 6 /5 /3.
插入三条腿后,路径显然只是1 2 3
,因为没有为其中任何一条设置覆盖标志,最初列表为空。
当使用覆盖集插入4
时,我的逻辑会覆盖第一个,清除它的覆盖标记(与问题中的逻辑描述相矛盾),并设置覆盖路径中其余腿的标志。因此,路径变为4 /2 /3
。
插入5
后,它会替换2
,因为2
已设置覆盖标记。同样,与问题中的逻辑描述相矛盾,我清除 5
的覆盖标志。因此,路径变为4 5 /3
。
当使用覆盖集插入6
时,它会覆盖第一个。我再次清除它的覆盖标志,与问题中描述的逻辑相矛盾,但是为路径中所有其余的腿设置,因此路径变为6 /5 /3
。
首先,关于节点结构的一个小注意事项:我将下一个指针放在节点结构的开头这一事实只是一种习惯。
(它可能有助于编译器在某些体系结构上生成更好的代码,因为next
指针然后指向next->next
所在的地址,这可能有助于编译器或处理器执行更简单的指令在执行路径漫游时更好的预取模式。如果将next
指针放在其他位置,next->next
处于该地址的固定偏移处;在某些情况下< em> may 需要额外的指令。在实践中是否重要?通常不是,甚至不是单个CPU周期。)
insert()
函数实现应该非常简单:
如果列表为空,请创建一个新节点,将其overwrite标志设置为false。完成。
否则:
如果需要覆盖,请替换第一个节点,将其overwrite标志设置为false,并将所有其他节点的覆盖标志设置为true。完成。
否则:
如果存在覆盖标志为true的节点,请替换该节点,将其overwrite标志重置为false。完成。
否则:
创建一个新节点,将其overwrite标志设置为false。将新节点附加到列表的末尾。完成。
唯一的&#34;技巧&#34;您可能想知道,ptr
指针是如何使用的。简而言之,它是获取currnode
指针的地址,基本上是currnode = *ptr
。这样,我们只是遍历列表,直到currnode->next
变为NULL,而不是在循环中检查currnode
。然后,*ptr
引用列表中最后一个元素中的->next
指针,我们可以分配给它,将新创建的节点附加到列表中。
我意识到这并没有回答OP的问题,但那是因为我不知道如何同时处理原问题中的两个层面的问题 - 我相信那里既是逻辑问题(使用覆盖标志),也是某种与链接列表管理方式相关的实现问题。
在调试,修复程序或编写新程序时,我总是尝试将可能的问题来源限制在尽可能小的范围内。编写有限的,简化的测试用例,比如上面的程序让我可以一次专注于一件事,而不必在整体逻辑和实现的细节之间切换我的大脑。简而言之,如果这是我自己的代码,这就是我解决OP问题的方式。
现在我有一个示例代码实现了我认为正确的逻辑,并且我可以轻松地对逻辑进行压力测试,我可以实现更复杂的实际腿部结构和腿部插入功能。知道逻辑是有效的(如果我有任何疑问,我可以使用测试程序验证任何角落情况),我可以专注于实际的实现。
显然,现在由OP来决定使用哪个逻辑(他们或我的),如果使用我的,看看我们的实现是如何不同的;我不认为OP发布了足够的代码(完整的插入例程)来告诉实际问题在哪里。至少我无法得到足够的概述以确定。
无论如何,希望这会有所帮助。有问题吗?