Я пишу эту программу для своего класса ОС и застрял на том, что кажется простой проблемой. У меня есть структура 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
Я позволю кому-нибудь объяснить, почему это происходит.
Вы можете подробнее рассказать об этом?
Конечно, как только вы разместите Минимальный, полный и проверяемый пример.
отредактирован, чтобы включить полный код. Он использует отношения сервера и клиента для добавления и удаления блокировок из связанного списка.
Итак, вы создаете список из миллиона блокировок. Возьмите своего резиновая утка на прогулку по парку и объясните ему, откуда берется память для миллиона записей в списке.
Расскажите, как работают локальные переменные в C.
Я не уверен, к чему вы клоните. Я создаю локальную переменную, и мне кажется (учитывая, что я все еще относительно новичок в этом языке), что то, что у меня есть, должно работать. Очевидно, что это не так, поэтому я прошу помощи. Я знаю, что локальные переменные можно использовать только в той области, в которой они созданы, но я не понимаю, как это применимо.
@ cpgreen2 Поскольку связанный список содержит адрес этой переменной, а связанный список используется вне области, в которой эта переменная была объявлена.
Перейдите по ссылке в комментарии от @ n.m. - и поговорите с уткой - действительно, это помогает. (и .... переменная, объявленная как локальная для функции, имеет автоматическая продолжительность хранения и уничтожается (память стека функций освобождается для повторного использования), когда функция возвращается. Вы можете динамически выделять в функции, предоставляющей переменную выделенная продолжительность хранения, с временем жизни, которое продолжается до free
вызывается для освобождения памяти)
О чем "утиная" ссылка: ericlippert.com/2014/03/05/how-to-debug-small-programs
По поводу памяти и локальных переменных в целом вопрос: слышали ли вы о функциях malloc()
или calloc()
? Если вы не прочитали о них в школьном учебнике, они, вероятно, упоминались несколько страниц назад; иначе вы не смогли бы работать над этим заданием.
Я понимаю утиную ссылку. Раньше я использовал malloc и calloc, @ DavidC.Rankin, поэтому следует ли мне использовать malloc для выделения места для блокировки, чтобы она не была разрушена?
@ DavidC.Rankin Я использовал malloc и смог заставить его работать. Спасибо за очень информативный комментарий
Мне кажется, вы берете адрес локальной переменной.