c - 链表中的所有节点都指向同一个对象

标签 c linked-list

问题就在这里......

char buffer[80];
char *name;
while (1) {
    fgets(buffer, 80, inf); //reads in at most 80 char from a line
        if (feof(inf)) //this checks to see if the special EOF was read
            break;     //if so, break out of while and continue with your main
        name = (char *) malloc(sizeof(char)*20);
        ....
        name = strtok(buffer, " ");//get first token up to space
        stock = newStock(name,...)
        ....
    }

我正在用 C 语言处理通用链表。我做了一个列表实现,我已经测试过并且知道它可以与字符一起使用。我正在尝试将股票(我创建了一个股票结构)添加到链接列表中,链接列表的每个节点都保存一个股票结构,但是当我读完股票时,所有节点都指向同一个结构,并且我不明白为什么。这是我的代码片段

list *list = malloc(sizeof(list));
newList(list, sizeof(stock_t));

while(1) {
    ...
    (read from file)
    ...
    stock_t *stock;
    stock = newStock(name, closes, opens, numshares, getPriceF, getTotalDollarAmountF,getPercentChangeF,toStringF);
    addToBack(list, stock);
}

这是 newStock 函数:

stock_t *newStock(char *name, float closingSharePrice, float openingSharePrice, int numberOfShares, getPrice getP, getTotalDollarAmount getTotal, getPercentChange getPercent, toString toStr) {

    stock_t *stock = malloc(sizeof(stock));
    stock->stockSymbol = name;
    stock->closingSharePrice = closingSharePrice;
    stock->openingSharePrice = openingSharePrice;
    stock->numberOfShares = numberOfShares;
    stock->getP = getP;
    stock->getTotal = getTotal;
    stock->getPercent = getPercent;
    stock->toStr = toStr;
    return stock;
}

在某种程度上我明白出了什么问题。 newStock 每次都会返回一个新指针,但它总是存储在变量“stock”中,这是每个节点所指向的,因此它将等于 newStock 返回的最后一个指针......但我没有看到解决这个问题的方法。我尝试让 newStock 仅返回 stock_t,并执行 addToBack(list, &stock),但这也没有解决问题。

如有任何帮助,我们将不胜感激!

以下是列表中的一些代码:

typedef struct node {
    void *data;
    struct node *next;
}node_t;

typedef struct {
    int length;
    int elementSize;
    node_t *head;
    node_t *tail;
} list;


void newList(list *list, int elementSize) {
    assert(elementSize > 0);
    list->length = 0;
    list->elementSize = elementSize;
   list->head = list->tail = NULL;
}

void addToBack(list *list, void *element) {

    node_t *node = malloc(sizeof(node_t));
    node->data = malloc(list->elementSize);
    node->next = NULL; //back node

    memcpy(node->data, element, list->elementSize);

    if (list->length == 0) { //if first node added
        list->head = list->tail = node;
    }
    else {
        list->tail->next = node;
        list->tail = node;
    }

    list->length++;
}

这是来自 stock 结构的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef float (*getPrice)(void *S);
typedef float (*getTotalDollarAmount)(void *S);
typedef float (*getPercentChange)(void *S);
typedef char *(*toString)(void *S);

typedef struct stock{
    char *stockSymbol;
    float closingSharePrice;
    float openingSharePrice;
    int numberOfShares;
    getPrice getP;
    getTotalDollarAmount getTotal;
    getPercentChange getPercent;
    toString toStr;
   }stock_t;

通用函数可能看起来有点大材小用,但这是为了家庭作业(如果你已经不知道),所以我们被要求专门使用它们。不过我认为这与问题没有任何关系。

这里是这些函数的定义

float getPriceF(void *S) {
    stock_t *stock = (stock_t*)S;
    return stock->closingSharePrice;
}

float getTotalDollarAmountF(void *S) {
    stock_t *stock = (stock_t*)S;
    return ((stock->closingSharePrice) * (stock->numberOfShares));
}

float getPercentChangeF(void *S) {
    stock_t *stock = (stock_t*)S;
    return ((stock->closingSharePrice - stock->openingSharePrice)/(stock->openingSharePrice));
}

char *toStringF(void *S) {
    stock_t* stock = (stock_t*)S;
    char *name = malloc(20*sizeof(char));
    //sprintf(name, "Symbol is: %s. ", (stock->stockSymbol));
    return stock->stockSymbol;
}

void printStock(void *S) {
    char *str = toStringF(S);
    printf("%s \n", str);
}

这就是我遍历列表的方式:

typedef void (*iterate)(void *); //this is in the list.h file, just putting it here to avoid confusion


void traverse(list *list, iterate iterator) {
    assert(iterator != NULL);

    node_t *current = list->head;

    while (current != NULL) {
        iterator(current->data);
        current = current->next;
    }
}

然后在我的主要部分我刚刚打电话

traverse(list, printStock);

最佳答案

我找不到你的代码的任何问题(无论如何,这会导致你的问题 - 有些地方你不检查 malloc() 的返回和类似的东西,但是这些与这个问题无关)。您没有提供stock_t的定义,因此我创建了一个新的数据结构和几个新的函数,否则我只是复制并粘贴您提供的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/*  Your code starts here */

typedef struct node {
    void *data;
    struct node *next;
}node_t;

typedef struct {
    int length;
    int elementSize;
    node_t *head;
    node_t *tail;
} list;


void newList(list *list, int elementSize) {
    assert(elementSize > 0);
    list->length = 0;
    list->elementSize = elementSize;
    list->head = list->tail = NULL;
}

void addToBack(list *list, void *element) {

    node_t *node = malloc(sizeof(node_t));
    node->data = malloc(list->elementSize);
    node->next = NULL; //back node

    memcpy(node->data, element, list->elementSize);

    if (list->length == 0) { //if first node added
        list->head = list->tail = node;
    }
    else {
        list->tail->next = node;
        list->tail = node;
    }

    list->length++;
}

/* Your code ends here */

/*  I made a new struct, rather than stock, since you didn't supply it  */

struct mydata {
    int num1;
    int num2;
};

/*  I use this instead of newStock(), but it works the same way  */

struct mydata * newNode(const int a, const int b) {
    struct mydata * newdata = malloc(sizeof *newdata);
    if ( newdata == NULL ) {
        fputs("Error allocating memory", stderr);
        exit(EXIT_FAILURE);
    }
    newdata->num1 = a;
    newdata->num2 = b;
    return newdata;
}

/*  I added this function to check the list is good  */

void printList(list * list) {
    struct node * node = list->head;
    int n = 1;
    while ( node ) {
        struct mydata * data = node->data;
        printf("%d: %d %d\n", n++, data->num1, data->num2);
        node = node->next;
    }
}

/*  Main function  */

int main(void) {
    list *list = malloc(sizeof(list));
    newList(list, sizeof(struct mydata));

    struct mydata * data;

    data = newNode(1, 2);
    addToBack(list, data);
    data = newNode(3, 4);
    addToBack(list, data);
    data = newNode(5, 6);
    addToBack(list, data);

    printList(list);

    return 0;
}

输出:

paul@MacBook:~/Documents/src$ ./list
1: 1 2
2: 3 4
3: 5 6
paul@MacBook:~/Documents/src$ 

证明您有一个 3 节点列表,所有节点都不同且位于您期望的位置。

您未显示的代码中存在其他问题,或者由于某种原因您认为每个节点都指向相同的struct,但实际上并非如此。

一种可能是您的 stock 结构中有一个 char * 数据成员。从您提供的代码中无法看出,但您可能确实正在创建不同的节点,但它们最终都指向相同的名称,因此它们看起来像是相同的。如果您要分配一个指向 name 的指针,则应确保每次都是新分配的内存,并且您不仅仅是 strcpy()ing放入相同的内存中,并将相同的地址分配给每个股票struct

编辑:看起来这是你的问题。这:

name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");

应该是:

name = (char *) malloc(sizeof(char)*20);
....
strcpy(name, strtok(buffer, " "));

现在,您malloc()新内存并将其引用存储在name中,但是当您用从 strtok() 返回的地址。相反,您需要将该 token 复制到新分配的内存中,如图所示。

关于c - 链表中的所有节点都指向同一个对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23317738/

相关文章:

c - 使用 C 使用随机数填充和打印数组

c - 使用欧拉方法和指针算术的模型不起作用

c - 为什么当 current->next == NULL 时 current = current->next 会出现段错误?

c - 在C中将元素添加到链表的前面

c++ - 链表(删除节点)

c++ - 如何实现这个练习(动态数组)?

c++ - 插入有序链表

c - 如何在c中获取另一个进程的pid?

c++ - OpenMP 库规范

c - 关于类型,c 中的 `int (*)(int)` 是什么意思?