【问题标题】:Logic error with global pointer in CC中全局指针的逻辑错误
【发布时间】:2015-02-01 05:56:13
【问题描述】:

我发现了如何解决我的问题,但我不知道它是如何工作或为什么工作。我会非常感谢有人来看看这个:

我正在创建一个链接列表,其中包含一个指向列表头部的全局指针。我在主线程中创建了一个虚拟节点。我想要发生的是能够调用 printList() 并且如果除了虚拟节点之外没有其他节点,则打印“Person:0”(基本上说列表是空的)。

[edit -> 这是我的简洁问题:为什么 printList() 在 main() 中识别 Person *head = NULL 而不是全局指针,当它使用它来设置当前指针等于 head 时?]

使用此代码,我得到以下输出

int main(){
    setvbuf(stdout, NULL, _IONBF, 0); 

    //Person *head = NULL; 
    printf("\nmain head:%p \n", head);

    head = (Person *)malloc(sizeof(Person));
    printf("\nmain head:%p \n", head);
    head->name[0] = '\0'; 
    head->next = NULL;
    head->previous = NULL;

输出:

main head:0000000000000000 

main head:00000000003F1390 
Enter add, insert or delete for Person functions: print

printList head:00000000003F1390 Person:1 Total People:1
Enter add, insert or delete for Person functions:

在 main() 中声明 Person*head 并将其初始化为 NULL,我得到了想要的结果。 为什么会这样?为什么我无法初始化全局指针并获得相同的预期结果?

int main(){
    setvbuf(stdout, NULL, _IONBF, 0); 

    Person *head = NULL; 
    printf("\nmain head:%p \n", head);

    head = (Person *)malloc(sizeof(Person));
    printf("\nmain head:%p \n", head);
    head->name[0] = '\0'; 
    head->next = NULL;
    head->previous = NULL;

以下输出:

main head:0000000000000000 

main head:00000000005E1390 
Enter add, insert or delete for Person functions: print

printList head:0000000000000000 Total People:0
Enter add, insert or delete for Person functions:

这是整个程序供参考:

#include "stdio.h" 
#include "stdlib.h"
#include "string.h"

typedef struct S_PersonInLine{
    char name[16];

    struct S_PersonInLine *next;
    struct S_PersonInLine *previous;
}Person;

//pointer to head of the list
//This isn't a global head pointer(wrong). It doesn't go into main (wrong). False it does go into main but it doesn't give the intended result from the printList
Person *head = NULL; //this allows the functions to access the head pointer

//prototypes 
Person *makePerson();
void *addPerson();
void *insert();
void *delete();
void printList();
void cleanUp();


int main(){
    setvbuf(stdout, NULL, _IONBF, 0); //Figure out what this thing does again and why its necessary
    Person *head = NULL; 
    printf("\nmain head:%p \n", head);

    head = (Person *)malloc(sizeof(Person));
    printf("\nmain head:%p \n", head);
    head->name[0] = '\0'; 
    head->next = NULL;
    head->previous = NULL;

    char input[16];
    char command[16];

    printf("Enter add, insert or delete for Person functions: ");
    while( fgets(input , 15 , stdin) ){ 
    sscanf(input, "%s", command);
        if ( strcmp(command, "quit") == 0 ){
            printf("\n\nBreaking....");
            break;
        } else if ( strcmp(command, "print") == 0 ){
            printList();
        }

    printf("Enter add, insert or delete for Person functions: ");
    }


    return 0;
}

void printList(){
    Person *current = head;
    printf("\nprintList head:%p ", head);
    int count = 0;


    while(current != NULL){
        count++;
        printf("Person:%d %s", count, current->name);
        current = current->next;
    }
    printf("Total People:%d\n", count);
}

【问题讨论】:

  • 这一行:'setvbuf(stdout, NULL, _IONBF, 0);'将 stdout 的输出缓冲设置为“不存在”,因此通过 printf 等输出的任何内容都会立即输出到终端,而不是等待程序退出或输出“\n”或 fflush(stdout);或从标准输入读取
  • 始终检查 malloc(和系列)的返回值,以确保操作成功。在 C 中,不要从 malloc 中转换返回值。它是一个 void *,因此可以与任何接收变量一起使用
  • 这一行:'while( fgets(input , 15 , stdin) ){ ' fgets 正确处理接收缓冲区(没有溢出并为终止 NUL 字节留出空间),因此该行应该是:' while( fgets(input , sizeof(input) , stdin) ){ '
  • @Sankofa 如果您不在main() 中声明一个并重新考虑虚拟节点的不必要想法,printList 肯定会正常工作(就像其他所有期望 head == NULL 表示列表为空)。有些人更喜欢列出带有虚拟节点的房屋指针。我的偏好不是。 ymmv。
  • 为什么会发生what?它们是两个不同的指针。对 main() 中的本地 head 所做的任何事情都不会修改全局。删除main 中的本地并使用虚拟节点启动全局head 意味着您所有期望head 指向NULL 的代码意味着必须更改一个空列表。你不能同时拥有它(一个虚拟的非 NULL 头指针和期望 head 作为 NULL 的函数是一个空列表)。你可以Person *current = head ? head->next : NULL;并保留你当前的列表打印代码,但是yuck

标签: c pointers linked-list logic


【解决方案1】:

没有状态被传递到printList 函数,因此变量head 指向全局实例。它不适合您的原因是因为您的 main 函数没有使用全局实例。当您在 main 函数中键入 Person *head = NULL; 时,它声明了一个局部变量(即,您没有修改全局实例)。相反,您应该通过简单地输入 head = NULL; 来初始化全局实例。

【讨论】:

  • 注意:在main(), head = NULL, ... 中不需要,因为它已经在全局初始化器上完成(也不需要,作为变量静态存储根据 C 标准初始化为零/空)。
  • @WhozCraig 我看到了,并同意没有必要。我只是想解释如何在主函数中正确地将其设置为NULL(即,不创建一个会影响全局变量的局部变量)。
  • 我看到 Person *head = NULL 如何影响全局变量,谢谢。但这并不能解决打印空列表的错误。 @jamesAdkison。我希望它打印一个空列表,我知道它可以用一个“虚拟节点”来完成。我刚刚用赛车创建了一个类似的程序,它工作......
  • @Sankofa 然后丢失main 中的声明并从current = head->next 而不是current = head 开始您的printList。但您最好已经在 main 中启动了全局 head,否则 deref 将调用未定义的行为。
【解决方案2】:

建议从 main() 中(完全)删除以下行 像他们 1) 屏蔽全局变量 'head' 2)将第一个条目放入链表 这就是为什么计数等 当没有任何东西被专门插入到链表中时

Person *head = NULL; 
printf("\nmain head:%p \n", head);

head = (Person *)malloc(sizeof(Person));
printf("\nmain head:%p \n", head);
head->name[0] = '\0'; 
head->next = NULL;
head->previous = NULL;

注意:在打印功能中,检查'head'的内容 并且在进入任何引用某个偏移量的循环之前不为 NULL 从“头”开始,没有那个检查,代码会尝试 取消引用内存地址 0,导致 未定义的行为,并且可能/将导致段错误事件

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多