Как избежать самореференциального связанного списка в C

Я пишу эту программу для своего класса ОС и застрял на том, что кажется простой проблемой. У меня есть структура Lock и структура lockList, которая содержит указатель на Lock в качестве головы. Когда мне нужно добавить блокировку в список, мне нужно добавить ее в конец связанного списка и иметь возможность распечатать их. Проблема в том, что когда я добавляю два из них (добавление одного работает нормально), а затем распечатываю его с помощью написанной мной команды «list», он печатает вторую вещь, которую я добавил, бесконечно. Я решил, что это потому, что второе поле Lock указывает на себя, и я не могу понять, почему и как это исправить.

Вот мой код для добавления блокировки:

if (lockList.head == NULL) {
    Lock lock = {.next = NULL}; 
    strcpy(lock.name, name);
    lockList.head = &lock;
  } else {
    while (contains(name)) {
      pthread_cond_wait(&cond, &mutex);
    }

    Lock *current = lockList.head;
    while (current->next != NULL) {
      current = current->next;
    }

    Lock lock = {.next = NULL}; 
    strcpy(lock.name, name);

    current->next = &lock;
  }

Мои структуры Lock и lockList:

typedef struct Lock {
  char name[25];
  struct Lock *next;
} Lock;

struct LockList {
  Lock *head;
}lockList;

и мой код для перечисления элементов в списке:

else if (strcmp(cmd, "list") == 0) {
  Lock *current = lockList.head;

  while (current != NULL) {
    fprintf(fp, "%s\n", current->name);
    current = current->next;
  } 

}

полный код:

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <errno.h>

/** Port number used by my server */
#define PORT_NUMBER "26136"

pthread_mutex_t mutex;
pthread_cond_t cond;

// Print out an error message and exit.
static void fail( char const *message ) {
  fprintf( stderr, "%s\n", message );
  exit( 1 );
}



typedef struct Lock {
  char name[25];
  struct Lock *next;
} Lock;

struct LockList {
  Lock *head;
}lockList;

bool contains(char title[]) {
  Lock *current = lockList.head;
  while (current != NULL) {
    if (current->name == title) {
      return true;
    }
    current = current->next;
  }

  return false;
}

/** handle a client connection, close it when we're done. */
void handleClient( int sock ) {
  // Here's a nice trick, wrap a C standard IO FILE around the
  // socket, so we can communicate the same way we would read/write
  // a file.
  FILE *fp = fdopen( sock, "a+" );

  // Prompt the user for a command.
  fprintf( fp, "cmd> " );

  char cmd[ 11 ];
  int match;
  while ( ( match = fscanf( fp, "%s", cmd ) ) == 1 &&
          strcmp( cmd, "quit" ) != 0 ) {

    // if command was lock
    if (strcmp(cmd, "lock") == 0) {
      // lock mutex
      pthread_mutex_lock(&mutex);

      char name[25];
      fscanf(fp, "%s", name);

      // while the thing requested is on the locked list, wait      


      // add the new lock to the locked list
      if (lockList.head == NULL) {
        Lock lock = {.next = NULL}; 
        strcpy(lock.name, name);
        lockList.head = &lock;
      } else {
        while (contains(name)) {
          pthread_cond_wait(&cond, &mutex);
        }

        Lock *current = lockList.head;
        while (current->next != NULL) {
          current = current->next;
        }

        Lock lock = {.next = NULL}; 
        strcpy(lock.name, name);

        current->next = &lock;
      }

      // unlock mutex
      pthread_mutex_unlock(&mutex);
    } else if (strcmp(cmd, "unlock") == 0) {

      // lock mutex
      pthread_mutex_lock(&mutex);

      char name[25];
      fscanf(fp, "%s", name);
      // if item with that name is in the list, take it off
      Lock *current = lockList.head;

      if (strcmp(lockList.head->name, name) == 0) {
        lockList.head = lockList.head->next;
      } else {
        while (current->next != NULL) {
          if (strcmp(current->next->name, name) == 0) {
            // if item found is in the middle of the list
            current->next = current->next->next;
            break;        
          }
          current = current->next;
        }
      }

      // broadcast the condition variable
      pthread_cond_broadcast(&cond);

      // unlock mutex
      pthread_mutex_unlock(&mutex);
    } else if (strcmp(cmd, "list") == 0) {
      Lock *current = lockList.head;

      while (current != NULL) {
        fprintf(fp, "%s\n", current->name);
        current = current->next;
      } 

    } else {
      fprintf(fp, "invalid command");
    }



    // Prompt the user for the next command.
    fprintf( fp, "cmd> " );
    fflush( fp );
  }

  // Close the connection with this client.
  fclose( fp );
}

int main() {
  lockList.head = NULL;


  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init(&cond, NULL);
  // Prepare a description of server address criteria.
  struct addrinfo addrCriteria;
  memset(&addrCriteria, 0, sizeof(addrCriteria));
  addrCriteria.ai_family = AF_INET;
  addrCriteria.ai_flags = AI_PASSIVE;
  addrCriteria.ai_socktype = SOCK_STREAM;
  addrCriteria.ai_protocol = IPPROTO_TCP;

  // Lookup a list of matching addresses
  struct addrinfo *servAddr;
  if ( getaddrinfo( NULL, PORT_NUMBER, &addrCriteria, &servAddr) )
    fail( "Can't get address info" );

  // Try to just use the first one.
  if ( servAddr == NULL )
    fail( "Can't get address" );

  // Create a TCP socket
  int servSock = socket( servAddr->ai_family, servAddr->ai_socktype,
                         servAddr->ai_protocol);
  if ( servSock < 0 )
    fail( "Can't create socket" );

  // Bind to the local address
  if ( bind(servSock, servAddr->ai_addr, servAddr->ai_addrlen) != 0 )
    fail( "Can't bind socket" );

  // Tell the socket to listen for incoming connections.
  if ( listen( servSock, 5 ) != 0 )
    fail( "Can't listen on socket" );

  // Free address list allocated by getaddrinfo()
  freeaddrinfo(servAddr);

  // Fields for accepting a client connection.
  struct sockaddr_storage clntAddr; // Client address
  socklen_t clntAddrLen = sizeof(clntAddr);

  while ( true  ) {
    // Accept a client connection.
    int sock = accept( servSock, (struct sockaddr *) &clntAddr, &clntAddrLen);

    // Talk to this client
    handleClient( sock );
  }

  // Stop accepting client connections (never reached).
  close( servSock );

  return 0;
}

Вот как выглядит настоящий MCVE:

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

typedef struct Lock {
    char name[25];
    struct Lock *next;
} Lock;

struct LockList {
    Lock *head;
}
lockList;

int main(void)
{
    lockList.head = NULL;
    char *str[] = { "world", "hello" };
    for (int i = 0; i < 2; i++)
    {
        Lock lock = {.next = lockList.head};
        strcpy(lock.name, str[i]);
        lockList.head = &lock;
    }
    printf("%s\n", lockList.head->name);
    printf("%s\n", lockList.head->next->name);
}

Ожидаемый результат этой программы:

hello
world

Фактический результат

hello
hello

Я позволю кому-нибудь объяснить, почему это происходит.

Мне кажется, вы берете адрес локальной переменной.

user3386109 17.04.2018 06:00

Вы можете подробнее рассказать об этом?

cpgreen2 17.04.2018 06:02

Конечно, как только вы разместите Минимальный, полный и проверяемый пример.

user3386109 17.04.2018 06:05

отредактирован, чтобы включить полный код. Он использует отношения сервера и клиента для добавления и удаления блокировок из связанного списка.

cpgreen2 17.04.2018 06:08

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

n. 1.8e9-where's-my-share m. 17.04.2018 06:40

Расскажите, как работают локальные переменные в C.

user253751 17.04.2018 06:44

Я не уверен, к чему вы клоните. Я создаю локальную переменную, и мне кажется (учитывая, что я все еще относительно новичок в этом языке), что то, что у меня есть, должно работать. Очевидно, что это не так, поэтому я прошу помощи. Я знаю, что локальные переменные можно использовать только в той области, в которой они созданы, но я не понимаю, как это применимо.

cpgreen2 17.04.2018 06:53

@ cpgreen2 Поскольку связанный список содержит адрес этой переменной, а связанный список используется вне области, в которой эта переменная была объявлена.

user3386109 17.04.2018 06:55

Перейдите по ссылке в комментарии от @ n.m. - и поговорите с уткой - действительно, это помогает. (и .... переменная, объявленная как локальная для функции, имеет автоматическая продолжительность хранения и уничтожается (память стека функций освобождается для повторного использования), когда функция возвращается. Вы можете динамически выделять в функции, предоставляющей переменную выделенная продолжительность хранения, с временем жизни, которое продолжается до free вызывается для освобождения памяти)

David C. Rankin 17.04.2018 06:55

О чем "утиная" ссылка: ericlippert.com/2014/03/05/how-to-debug-small-programs

Yunnosch 17.04.2018 07:02

По поводу памяти и локальных переменных в целом вопрос: слышали ли вы о функциях malloc() или calloc()? Если вы не прочитали о них в школьном учебнике, они, вероятно, упоминались несколько страниц назад; иначе вы не смогли бы работать над этим заданием.

Yunnosch 17.04.2018 07:06

Я понимаю утиную ссылку. Раньше я использовал malloc и calloc, @ DavidC.Rankin, поэтому следует ли мне использовать malloc для выделения места для блокировки, чтобы она не была разрушена?

cpgreen2 17.04.2018 07:13

@ DavidC.Rankin Я использовал malloc и смог заставить его работать. Спасибо за очень информативный комментарий

cpgreen2 17.04.2018 07:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
13
63
0

Другие вопросы по теме