функции для добавления и чтения из связанных списков

#c #pointers #linked-list

#c #указатели #связанный список

Вопрос:

Я работаю над небольшой тестовой программой, которая может выполнять 2 функции в связанном списке:

  1. добавьте что-нибудь в связанный список.
  2. получить то, что было добавлено.

Итак, я написал 2 функции, которые могут выполнять именно это. Однако это не работает. Если я вызываю функцию append_work() , она выводит, что она добавила работу, а также правильные значения, которые я передал. Однако впоследствии, похоже, он неправильно устанавливает указатель на следующую структуру. Я не знаю, почему это происходит.

Аналогичная вещь происходит, когда я вызываю get_work() , что он правильно выводит текущую работу, но не устанавливает ее на следующую. outptr существует только для того, чтобы он возвращал структуру для выполняемой работы, в то время как stil настраивает work_travel на следующий!

Вероятно, я упускаю что-то действительно очевидное с указателями, но я этого не вижу…

Вот код. он компилирует:

 #define          _POSIX_C_SOURCE                             200809L  
#define          ERRNO_BUFSIZE                               256

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

int strerror_r(int errnum, char *buf, size_t buflen);

typedef struct work work;

struct work {

    char                *str;
    int                 i;
    work                *next;

};

int append_work(work *ptr, char *work, int i) {
    char    strerror_buf[ERRNO_BUFSIZE];

    ptr->next = malloc(sizeof(work));
    if (ptr == NULL) {
        strerror_r(errno, strerror_buf, ERRNO_BUFSIZE);
        printf("malloc() err: %sn",strerror_buf);
        return -1;
    }

    ptr = ptr->next;
    ptr->str=work;
    ptr->i=i;
    printf("appending work: %sn",ptr->str);

    ptr->next = NULL;

    return 0;

}

int get_work(work *inptr, work *outptr) {

    if (inptr == NULL) {
        printf(" No work found...n");
        return -1;
    } else {
        outptr = inptr;
        printf(" work found: str: %s|| int: %dn",inptr->str, inptr->i);
        inptr = inptr->next;
        return 0;
    }
}



int main() {

    char                strerror_buf[ERRNO_BUFSIZE];

    work                *root;
    work                *work_travel;
    work                *add_work;
    work                *curr_work;

    root = malloc(sizeof(work));
    if (root == NULL) {
        strerror_r(errno, strerror_buf, ERRNO_BUFSIZE);
        printf("malloc() err: %sn",strerror_buf);
        exit(1);
    }
    root->str="work 0";
    root->i=0;


    work_travel = root;
    add_work = root;

    append_work(add_work, "work 1", 1);
    append_work(add_work, "work 2", 2);
    append_work(add_work, "work 3", 3);
    append_work(add_work, "work 4", 4);
    append_work(add_work, "work 5", 5);
    append_work(add_work, "work 6", 6);
    append_work(add_work, "work 7", 7);
    append_work(add_work, "work 8", 8);
    append_work(add_work, "work 9", 9);

    get_work(work_travel, curr_work);
    get_work(work_travel, curr_work);
    get_work(work_travel, curr_work);

    exit(1);

}
 

Комментарии:

1. вам может помочь что-то вроде двойного указателя. 🙂

Ответ №1:

Вы никогда не возвращаете новый заголовок для списка, вы меняетесь ptr внутри append_work() , но значение add_work in main() не меняется.

Вместо этого вы должны вернуть новый заголовок списка.

Это связано с тем, что C строго вызывается по значению; аргумент является копией того же значения в контексте вызывающего объекта, и изменение копии не влияет на значение вызывающего объекта. Тип значения не имеет значения, это всегда копия.

Комментарии:

1. Я подумал, что если я изменю указатель в функции, которая должна сохраняться в main? Почему это не так

2. Если это строго вызов по значению. тогда почему моя работа по добавлению все еще добавляется, если я выполняю вручную в main?

3. Если вы измените содержимое объекта, указатель, на который указывает это изменение, сохранится, однако вы меняете то, что вы передаете. И, как указано в ответе, это всего лишь копия исходного указателя.

4. @RG337 Извините, я не совсем понимаю, что вы имеете в виду. Возможно, вам следует опубликовать отдельный вопрос с точным кодом для этого случая, и люди посмотрят.

5. @LaszloLadanyi Я вижу, спасибо. По сути, то, что вы говорите, как только я выйду за пределы указателя на фактическую ячейку памяти, оно сохранится. на самом деле это очевидно…

Ответ №2:

Прежде всего, наличие «struct work», типа с именем «work» и переменной с именем «work» — это действительно плохой стиль программирования.

Тем не менее, проблема в том, что inptr это локальная переменная get_work , ее изменение не изменится work_travel при вызове get_work(work_travel, curr_work) . Вы должны каким-то образом возвращать обновленное значение, либо как возвращаемое значение из get_work, например (хотя с этим решением вы теряете флаг успеха, а не используете его :-)):

 work *get_work (work *inptr, work *outptr) {
   ...
   return inptr;
}
 

и назовите это как:

 work_travel = get_work(work_travel, curr_travel);
 

Или передать указатель на обновляемый объект, например:

 int get_work (work **inptr_p, work *outptr) {
  work *inptr = *inptr_p;
  if (inptr == NULL) {
    printf(" No work found...n");
    return -1;
  } else {
    outptr = inptr;
    printf(" work found: str: %s|| int: %dn",inptr->str, inptr->i);
    inptr = inptr->next;
    *inptr_p = inptr;
    return 0;
  }
}
...
get_work (amp;work_travel, curr_travel);
 

Комментарии:

1. Затем мне просто нужно будет проверить, возвращает ли функция get_work() значение NUll. И, конечно, я знаю, что использование структуры с именем work — плохая практика, но это всего лишь доказательство концепции