问题
Now i'm learning C and I have a problem with memory alocation, atleast this I understand from my code error.
Code
#ifndef __FILE_H__
#define __FILE_H__
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Files Files;
typedef struct DirList DirList;
typedef struct NodeFile NodeFile;
typedef struct NodeDir NodeDir;
typedef struct Directory {
// The name of the directory
char *name;
// TODO: The list of files of the current directory
Files *files;
// TODO: The list of dirs of the current directory
DirList *dirs;
// The parent directory of the current directory (NULL for the root
// directory)
struct Directory *parentDir;
} Directory;
// DO NOT MODIFY THIS STRUCTURE
typedef struct File {
// The name of the file
char *name;
// The size of the file
int size;
// The content of the file
char *data;
// The directory in which the file is located
Directory *dir;
} File;
typedef struct Files {
NodeFile *first;
NodeFile *last;
} Files;
typedef struct DirList {
NodeDir *first;
NodeDir *last;
} DirList;
typedef struct NodeFile {
struct NodeFile *next;
struct NodeFile *prev;
File *newfile;
} NodeFile;
typedef struct NodeDir {
struct NodeDir *next;
struct NodeDir *prev;
Directory *newdir;
} NodeDir;
// create root of file system
void makeroot(Directory **root)
{
*root = (Directory *) malloc(sizeof(Directory));
(*root)->parentDir = NULL;
(*root)->name = strdup("/");
(*root)->files = NULL;
(*root)->dirs = NULL;
}
// remove root of file system
void deleteroot(Directory *root)
{
root = NULL;
free(root);
}
//add new file to current directory
File *touch(Directory *root, char *nume, char *content)
{
NodeFile *new = (NodeFile *) malloc(sizeof(NodeFile));
new->newfile = (File *) malloc(sizeof(File));
new->newfile->name = (char *) malloc(strlen(nume) + 1);
new->newfile->data = (char *) malloc(strlen(content) + 1);
strcpy(new->newfile->name, nume);
strcpy(new->newfile->data, content);
if (root->files == NULL) {
root->files = (Files *) malloc(sizeof(Files));
root->files->first = (NodeFile *) malloc(sizeof(NodeFile));
root->files->last = (NodeFile *) malloc(sizeof(NodeFile));
//if no file in folder root has first and last position
root->files->first = new;
root->files->last = new;
} else if (strcmp(root->files->first->newfile->name,
new->newfile->name) > 0) {
new->next = root->files->first;
root->files->first = new;
} else if (strcmp(root->files->last->newfile->name,
new->newfile->name) < 0) {
root->files->last->next = new;
root->files->last = new;
} else {
NodeFile *i = root->files->first;
while (i != root->files->last) {
if (strcmp(i->next->newfile->name,
new->newfile->name) > 0) {
if (i == root->files->first->next)
i = root->files->first;
i->next->prev = new;
new->next = i->next;
new->prev = i;
i->next = new;
break;
}
i = i->next;
}
}
return new->newfile;
}
// Create directory
Directory *mkdir(Directory *parent, char *name)
{
NodeDir *new = (NodeDir *) malloc(sizeof(NodeDir));
//new->newdir = (Directory *) malloc(sizeof(Directory));
new->newdir = (Directory *) malloc(strlen(Directory) + 1);
new->newdir->name = (char *) malloc(strlen(name) + 1);
strcpy(new->newdir->name, name);
new->newdir->parentDir = parent;
if (parent->dirs == NULL) {
parent->dirs = (DirList *)malloc(sizeof(DirList));
parent->dirs->first = (NodeDir *) malloc(sizeof(NodeDir));
parent->dirs->last = (NodeDir *) malloc(sizeof(NodeDir));
parent->dirs->first = new;
parent->dirs->last = new;
} else if (strcmp(parent->dirs->first->newdir->name,
new->newdir->name) > 0) {
new->next = parent->dirs->first;
parent->dirs->first = new;
} else if (strcmp(parent->dirs->last->newdir->name,
new->newdir->name) < 0) {
parent->dirs->last->next = new;
parent->dirs->last = new;
} else {
NodeDir *i = parent->dirs->first->next;
while (i != NULL) {
if (strcmp(i->newdir->name, new->newdir->name) > 0) {
if (i == parent->dirs->first->next)
i = parent->dirs->first;
i->next->prev = new;
new->next = i->next;
new->prev = i;
i->next = new;
break;
}
i = i->next;
}
}
return new->newdir;
}
// traverse list and print files and folders names
void ls(Directory *parent)
{
if (parent->files != NULL) {
NodeFile *i;
for (i = parent->files->first; i != NULL; i = i->next)
printf("%s ", i->newfile->name);
}
if (parent->dirs != NULL) {
NodeDir *j;
for (j = parent->dirs->first; j != NULL; j = j->next)
printf("%s ", j->newdir->name);
}
printf("\n");
}
// working directory
void pwd(Directory *dir)
{
if (dir->parentDir == NULL)
return;
if (dir->parentDir != NULL) {
pwd(dir->parentDir);
printf("/%s", dir->name);
}
}
Directory *cd(Directory *dir, char *where)
{
if (strcmp(where, "..") == 0 && dir->parentDir != NULL) {
return dir->parentDir;
} else if (dir->dirs == NULL)
printf("Cannot move to ‘%s’: No such directory!\n", where);
else {
NodeDir *it = dir->dirs->first;
while (it != NULL) {
if (strcmp(it->newdir->name, where) == 0) {
dir = it->newdir;
break;
}
it = it->next;
}
if (it == NULL)
printf("Cannot move to ‘%s’: No such directory!\n",
where);
free(it);
}
return dir;
}
void tree(Directory *parent, int i)
{
if (i == 1)
printf("\n%s\n", parent->name);
if (parent->files != NULL) {
NodeFile *it;
for (it = parent->files->first; it != NULL; it = it->next) {
if (i != 1) {
int j;
for (j = 0; j < i; j++)
printf(" ");
}
printf(" %s\n", it->newfile->name);
}
free(it);
}
if (parent->dirs != NULL) {
NodeDir *it = parent->dirs->first;
while (it != NULL) {
int j;
for (j = 0; j < i; j++)
printf(" ");
printf("%s\n", it->newdir->name);
i = i + 1;
tree(it->newdir, i);
it = it->next;
i = i - 1;
}
free(it);
}
}
void rm(Directory *parent, char *dirname)
{ //it -- item
NodeFile *it;
for (it = parent->files->first; it != NULL; it = it->next) {
if (strcmp(it->newfile->name, dirname) == 0) {
if (it == parent->files->first) {
parent->files->first =
parent->files->first->next;
} else if (it == parent->files->last) {
parent->files->last = it->prev;
} else {
it->prev->next = it->next;
it->next->prev = it->prev;
}
it = NULL;
free(it);
return;
}
}
if (it == NULL) {
printf("Cannot remove ‘%s’: No such file!\n", dirname);
free(it);
}
}
void rmdir(Directory *parent, char *dirname)
{
NodeDir *it;
for (it = parent->dirs->first; it != NULL; it = it->next) {
if (strcmp(it->newdir->name, dirname) == 0) {
if (it == parent->dirs->first) {
parent->dirs->first =
parent->dirs->first->next;
} else if (it == parent->dirs->last) {
parent->dirs->last =
parent->dirs->last->prev;
} else {
it->prev->next = it->next;
it->next->prev = it->prev;
}
it = NULL;
free(it);
return;
}
}
if (it == NULL) {
printf("Cannot remove ‘%s’: No such directory!\n", dirname);
free(it);
}
}
#endif
/* __FILE_H__ */
Valgrind output:
> create fs ls
>
> ls
>
> mkdir test ls
> ==11466== Conditional jump or move depends on uninitialised value(s)
> ==11466== at 0x109025: ls (file.h:189)
> ==11466== by 0x1097D4: main (main.c:86)
> ==11466== Uninitialised value was created by a heap allocation
> ==11466== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==11466== by 0x108D44: mkdir (file.h:134)
> ==11466== by 0x109766: main (main.c:81)
回答1:
In touch you missed to set to NULL the fields next and rev
In mkdir you missed to set to NULL the fields files and dirs, and sometimes next
To simplify your code and makes it robust I encourage you to add for each struct a constructor doing the malloc and initializing all the fields including with NULL when the value is not yet known, and also add a destructor for each to free all allocated element. Of course in your case you can also use calloc rather than malloc to put all to 0
deleteroot does not free memory because you set root to NULL before to call free, and because you do not follow the links to free linked resources.
In rmdir you have the same unexpected assignment to NULL before to call free inside the for. At the end if is useless to check if it is NULL because the continuation test of the for is it != NULL, and the free in the last block is useless
Out of the memory leaks because you never free the resources you also introduce memory leaks in mkdir doing
parent->dirs->first = (NodeDir *) malloc(sizeof(NodeDir)); parent->dirs->last = (NodeDir *) malloc(sizeof(NodeDir)); parent->dirs->first = new; parent->dirs->last = new;
the two first lines must be removed
In
new->newdir = (Directory *) malloc(strlen(Directory) + 1);
strlen(Directory) is invalid, must be
new->newdir = (Directory *) malloc(sizeof(Directory));
In pwd you do
if (dir->parentDir == NULL) return; if (dir->parentDir != NULL) {
in that the two first lines are useless
Furthermore you print nothing in case of root, can be :
void pwd(Directory *dir)
{
if (dir->parentDir != NULL) {
pwd(dir->parentDir);
printf("%s/", dir->name);
}
else
putchar('/');
}
In cd the call to free must be removed
In case of .. if there is a parent you will always indicate an error because you consider the sub dirs. Note that at the root level a cd .. does nothing, so replace
if (strcmp(where, "..") == 0 && dir->parentDir != NULL) { return dir->parentDir; } else if (dir->dirs == NULL)
by
if (strcmp(where, "..") == 0) {
return (dir->parentDir != NULL) ? dir->parentDir : dir;
} else if (dir->dirs == NULL)
I encourage you to rename the field newfile to file and newdir to dir because the new has no reason
In tree the call to free must be removed
Visibly all is in a header file, I encourage you to put only the struct definitions and function declaration in the header file, and to move the function definitions in a source file, else if you #include several times your header file you will have functions multiply defined
来源:https://stackoverflow.com/questions/60913495/shell-simulation-using-lists