Desperately seeking the answer to my pointer problem - c

I have been working on a college C assignment, and have been trying to make sense of a bug I seem to be having with my code. Basically, it seems like something is wrong with my pointers (and or memory allocation).
This assignment is primarily about linked lists, so the structs contain pointers to the next element in the list. Obviously, to traverse the list until I find that the current element has a NULL pointer to the next element (and then I change that to be the pointer to the 'new' element I want to add.
The problem I have though, is for some reason my code seems to be completely screwing with my memory pointers, because they're getting garbled somehow. They seem fine for moment but very soon go rubbish.
Here is what my watches in XCode's debugger are showing me:
The first circle shows me the values as the first element in the list, which as far as I can tell are initially set correctly, and should be "C0001\0". The second circle shows the current->nextCategory pointer which should be NULL (0x0) but instead show that weird memory address (look at the size of it!). I assume that these problems are related, but as I am new to C, I don't know how or why.
In either case, when I check current->nextCategory != NULL in my while statement, it throws EXC_BAD_ACCESS:
I've spent the past few hours pulling my hair out becasue I cannot work out what the heck is happening to my program. Am I doing something wrong with my pointers, or using malloc() improperly?
Here is the relevant part of my program:
/****************************************************************************
* Initialises the system to a safe empty state.
****************************************************************************/
void systemInit(GJCType* menu)
{
if (menu == NULL) {
fprintf(stderr, "can't initialize system with a null menu pointer.\n");
exit(EXIT_FAILURE);
}
menu->headCategory = NULL;
menu->numCategories = 0;
}
/****************************************************************************
* Loads all data into the system.
****************************************************************************/
int loadData(GJCType* menu, char* menuFile, char* submenuFile)
{
FILE *fp;
size_t len;
char *line;
char *buffer;
CategoryTypePtr category_p;
ItemTypePtr item_p;
char *catId;
if (menu == NULL)
return FALSE;
fp = fopen(menuFile, "r");
if(fp == NULL) {
fprintf(stderr, "can't open %s\n", menuFile);
return FALSE;
}
buffer = malloc(MAX_BUFFER_SIZE);
len = MAX_BUFFER_SIZE;
catId = malloc(ID_LEN + 1);
while((buffer = fgetln(fp, &len))) {
line = strtok(buffer, "\n\0");
category_p = malloc(sizeof(CategoryTypePtr));
if (!tokenizeCategory(line, category_p)) {
fprintf(stderr, "can't tokenize category:> %s\n", line);
free(category_p);
category_p = NULL;
free(buffer);
free(catId);
return FALSE;
}
pushCategory(menu, category_p);
free(category_p);
category_p = NULL;
}
fp = fopen(submenuFile, "r");
if(fp == NULL) {
fprintf(stderr, "can't open %s\n", submenuFile);
return FALSE;
}
while((buffer = fgetln(fp, &len))) {
line = strtok(buffer, "\n\0");
item_p = malloc(sizeof(ItemTypePtr));
if (!tokenizeItem(line, item_p, catId)) {
fprintf(stderr, "can't tokenize item:> %s\n", line);
free(item_p);
item_p = NULL;
free(buffer);
free(catId);
return FALSE;
}
category_p = findCategory(menu, catId);
pushItem(category_p, item_p);
free(item_p);
item_p = NULL;
}
free(buffer);
free(catId);
return TRUE;
}
void pushItem(CategoryTypePtr category, ItemTypePtr item)
{
ItemTypePtr current;
ItemTypePtr new;
if ((new = malloc(sizeof(ItemTypePtr))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new item pointer.\n");
exit(EXIT_FAILURE);
}
*new = *item;
if (category->headItem == NULL) {
category->headItem = new;
} else {
current = category->headItem;
while (current->nextItem != NULL) {
current = current->nextItem;
}
current->nextItem = new;
}
category->numItems++;
}
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr current;
CategoryTypePtr new;
if ((new = malloc(sizeof(CategoryTypePtr))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new category pointer.\n");
exit(EXIT_FAILURE);
}
*new = *category;
if (menu->headCategory == NULL) {
menu->headCategory = new;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = new;
}
menu->numCategories++;
}
CategoryTypePtr findCategory(GJCType* menu, char* id)
{
CategoryTypePtr current;
current = menu->headCategory;
while (current != NULL) {
if (!strcmp(current->categoryID, id))
return current;
current = current->nextCategory;
}
return NULL;
}
/* This function takes the character delimited string and converts it into
* a category structure at the location of the category pointer supplied.
*/
int tokenizeCategory(char *data, CategoryTypePtr category)
{
char* buffer;
if (category == NULL || strlen(data) < 1)
return FALSE;
buffer = malloc(MAX_BUFFER_SIZE);
strcpy(buffer, data);
strcpy(category->categoryID, strtok(buffer, "|\n"));
category->drinkType = *(strtok(NULL, "|\n"));
strcpy(category->categoryName, strtok(NULL, "|\n"));
strcpy(category->categoryDescription, strtok(NULL, "|\n"));
category->numItems = 0;
category->nextCategory = NULL;
category->headItem = NULL;
free(buffer);
return TRUE;
}
/* This function takes the character delimited string and converts it into
* an item structure at the location of the item pointer supplied.
*/
int tokenizeItem(char *data, ItemTypePtr item, char* categoryId)
{
char* buffer;
int i;
if (item == NULL || strlen(data) < 1)
return FALSE;
buffer = malloc(MAX_BUFFER_SIZE);
strcpy(buffer, data);
strcpy(item->itemID, strtok(buffer, "|\n"));
strcpy(categoryId, strtok(NULL, "|\n"));
strcat(categoryId, "\0");
strcpy(item->itemName, strtok(NULL, "|\n"));
for (i = 0; i < NUM_PRICES; i++)
sscanf(strtok(NULL, "|\n"),"%d.%d",&(item->prices[i].dollars),&(item->prices[i].cents));
strcpy(item->itemDescription, strtok(NULL, "|\n"));
item->nextItem = NULL;
free(buffer);
return TRUE;
}
Header definitions:
/* System-wide constants. */
#define ID_LEN 5
#define MIN_NAME_LEN 1
#define MAX_NAME_LEN 25
#define MIN_DESC_LEN 1
#define MAX_DESC_LEN 250
#define NUM_PRICES 3
#define HOT 'H'
#define COLD 'C'
#define FALSE 0
#define TRUE 1
#define MAX_BUFFER_SIZE 1024
typedef struct category* CategoryTypePtr;
typedef struct item* ItemTypePtr;
/* Structure definitions. */
typedef struct price
{
unsigned dollars;
unsigned cents;
} PriceType;
typedef struct item
{
char itemID[ID_LEN + 1];
char itemName[MAX_NAME_LEN + 1];
PriceType prices[NUM_PRICES];
char itemDescription[MAX_DESC_LEN + 1];
ItemTypePtr nextItem;
} ItemType;
typedef struct category
{
char categoryID[ID_LEN + 1];
char categoryName[MAX_NAME_LEN + 1];
char drinkType; /* (H)ot or (C)old. */
char categoryDescription[MAX_DESC_LEN + 1];
CategoryTypePtr nextCategory;
ItemTypePtr headItem;
unsigned numItems;
} CategoryType;
typedef struct gjc
{
CategoryTypePtr headCategory;
unsigned numCategories;
} GJCType;

It looks to me like you're not allocating memory properly.
category_p = malloc(sizeof(CategoryTypePtr));
This only allocates enough memory to store a single address, not an entire category structure. Try something like:
category_p = malloc(sizeof(CategoryType));

The problem is these lines:
category_p = malloc(sizeof(CategoryTypePtr));
item_p = malloc(sizeof(ItemTypePtr));
These lines, as written, only allocate enough memory to store a pointer, not the structure you want to point to.
Try:
category_p = malloc(sizeof(CategoryType));
item_p = malloc(sizeof(ItemType));
Also, your push functions are overly complicated. There's no need to copy the list nodes before you add them to the list. Just assign the address in the pointer to the new node to the ->next... pointer in the current tail:
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr current;
// no need to allocate space just for a pointer
if (menu->headCategory == NULL) {
menu->headCategory = category;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = category;
}
menu->numCategories++;
}
Then you don't want the free(item_p) and free(category_p) calls in the main routine because the memory you've allocated is now being referenced by the list. You will need to free this memory when you dispose of the list.

To debug this kind of issues, I can only suggest to use valgrind : it will provide you very valuable help on buffer overflow, out of bound writing, memory loss. It's installed in the developper package.

You have multiple issues. Besides incorrectly allocating memory you are doing
*new = *category;
in your pushCategory function expecting to automagically copy the internal contents of the category structure: that simply doesn't work. You'll need to allocate a new CategoryType object and then copy each individual element appropriately. Something like this:
void pushCategory(GJCType* menu, CategoryTypePtr category)
{
CategoryTypePtr newCategory;
CategoryTypePtr current;
if ((newCategory = malloc(sizeof(CategoryType))) == NULL) {
fprintf(stderr, "can't malloc enough memory for new category pointer.\n");
exit(EXIT_FAILURE);
}
// copy individual elements here and set values properly
newCategory->headItem = NULL;
strncpy(newCategory->categoryID, category->categoryID, ID_LEN);
// copy other strings and NULL-initialize other pointers
if (menu->headCategory == NULL) {
menu->headCategory = new;
} else {
current = menu->headCategory;
while (current->nextCategory != NULL) {
current = current->nextCategory;
}
current->nextCategory = newCategory;
}
menu->numCategories++;
}
You will need to do the same thing for pushItem.

In the following code
category_p = malloc(sizeof(CategoryTypePtr));
.
.
.
pushCategory(menu, category_p);
free(category_p);
category_p = NULL;
and
item_p = malloc(sizeof(ItemTypePtr));
.
.
.
pushItem(category_p, item_p);
free(item_p);
item_p = NULL;
You have allocated the memory first, linked it in the list, and that very address you have deallocated. I think this is the problem.
And also you have done:
item_p = malloc(sizeof(ItemTypePtr));
and
category_p = malloc(sizeof(CategoryTypePtr));
the ItemTypePtr and CategoryTypePtr are pointers, so what you allocate is only the size of the pointer, and the memory cannot accomodate the entire data of the structure. You need to do
item_p = malloc(sizeof(ItemType));
category_p = malloc(sizeof(CategoryType));
Also in other locations you need to change the ItemTypePtr to ItemType as needed. I will tell not to typedef the pointers as you have done. This can bring difficulty in reading the code. If you have complex function pointer expressions then typedef ing it okay; in my opinion.

Related

"realloc(): invalid next size" when reallocating memory a char** pointer that is a member of a struct

I just started learning C recently, and am having issues figuring out memory allocation. I have spent about the last 2~3 days in my extra time trying to figure this out, but have not found a solution yet. So first, I have two structs:
struct _list {
// arr is an array of string arrays
char **arr;
// recs tracks how many records are in the list
size_t recs;
// arrSizes records the size of each string array in arr
size_t *arrSizes;
};
typedef struct _list list_t;
and
struct _string {
char *string;
// size is used to store strlen
size_t size;
};
typedef struct _string string_t;
I initialize the above structs respectively in the following ways.
list_t:
list_t *NewList() {
list_t *List = NULL;
List = malloc(sizeof(*List));
if (List == NULL) {
fprintf(stderr, "Failed to allocate memory to list structure.\n");
return NULL;
}
List->arr = malloc(sizeof(List->arr));
if (List->arr == NULL) {
free(List);
fprintf(stderr, "Failed to allocate memory to list array.\n");
return NULL;
}
List->arrSizes = malloc(sizeof(List->arrSizes));
if (List->arr == NULL) {
free(List);
fprintf(stderr, "Failed to allocate memory to size array.\n");
return NULL;
}
List->recs = 0;
return List;
}
string_t:
// a string array read in by the program is passed with "char* record"
string_t *NewString(char *record)
{
string_t *String = NULL;
String = malloc(sizeof * String);
if (String == NULL) {
fprintf(stderr, "Failed to allocate memory to string structure.\n");
return NULL;
}
String->size = strlen(record) + 1;
String->string = malloc(String->size);
if (String->string == NULL) {
free(String);
fprintf(stderr, "Failed to allocate memory to string array.\n");
return NULL;
}
strcpy(String->string, record);
return String;
}
I read lines from a file and load them into a "matching results" buffer using something like the following code. Please ignore exits and the fact that I don't have null handling after struct initialization is complete; I will add something more useful later. Also, sorry about the length. I edited quite a bit to produce the smallest example I could think of that reproduces the issue.
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
// Check if File exists
void FileExists(FILE *FilePath) {
if (FilePath == NULL) {
fprintf(stderr, "Error: File not found.\n");
exit(1);
}
}
// Delete a string_t struct
int delString(string_t *Structure)
{
if (Structure != NULL) {
free(Structure->string);
free(Structure);
return 0;
}
return 1;
}
// Allocate memory for additional elements added to members of list_t struct
void AllocList(list_t *List, size_t StrLen)
{
char **ArrStrArr_tmp;
size_t *SizeArr_tmp;
char *StrArr_tmp;
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
StrArr_tmp = malloc(sizeof(*StrArr_tmp) * StrLen);
if ((ArrStrArr_tmp == NULL) || (SizeArr_tmp == NULL)
|| (StrArr_tmp == NULL)) {
fprintf(stderr, "Failed to allocate memory.\n");
exit(1);
}
else {
List->arr = ArrStrArr_tmp;
List->arrSizes = SizeArr_tmp;
(List->arr)[List->recs-1]= StrArr_tmp;
}
}
// Add a record to a buffer
int AddRecord(list_t *List, char *AppendRecord)
{
string_t *line = NewString(AppendRecord);
List->recs++;
AllocList(List, line->size);
(List->arr)[List->recs - 1] = line->string;
(List->arrSizes)[List->recs - 1] = line->size;
delString(line);
return 0;
}
// Sends entire string array to lowercase
void tolowerString(char *UpperString, size_t StrLen)
{
int i;
for (i = 0; i < (int)StrLen; i++) {
UpperString[i] = (char)tolower(UpperString[i]);
}
}
// Attempt to match string in lines from a file; lines with matches are read into a buffer
int main()
{
char line[80];
int PrintedLines = 0;
list_t *ResultList = NewList();
char *MyString = "theme";
char *Filename = "List.txt";
FILE *in = fopen(Filename, "r");
// Check if file exists
FileExists(in);
while (fscanf(in, "%79[^\n]\n", line) == 1)
{
char LookString[80];
strcpy(LookString, line);
LookString[strlen(LookString) - 1] = '\0';
// send lookstring to lowercase
tolowerString(LookString, strlen(LookString));
// add line to buffer ResultList if it contains MyString
if (strstr(LookString, MyString)) {
AddRecord(ResultList, line);
PrintedLines++;
}
}
// If PrintedLines is at zero after the while statement terminates, return in abnormal state
if (PrintedLines == 0) {
fprintf(stderr, "No matches found. Please check your input if you are sure there is a match.\n");
return 1;
}
fclose(in);
return 0;
}
When trying to read the 5th matching record into my buffer, my program crashes at this line in the AllocList function:
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
I get the following message on the version I have posted above:
realloc(): invalid old size
aborted (core dumped)
My guess is that I'm running into an error after some default amount of memory from my initial malloc is used, but I have no clue what is actually causing this. In my actual code I'm printing all sorts of things (pointer sizes, etc.), but I still can't spot anything. What's strange is, before writing this post, I was actually seeing the error:
realloc(): invalid next size
aborted (core dumped)
But I can't reproduce it now for some reason...
I have also read that I should reallocating memory for my list_t struct whenever I add an element to one of it's members, but reallocating it actually doesn't change where or how this program crashes. In any case, I'm not sure how I should be reallocating memory for my struct. To clarify, my questions are:
What is causing this memory issue?
Should I be reallocating memory for my list struct, and how much should I be reallocating given that I'm adding an extra element to the arr and arrSizes members?
As the crash suggests, the line
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
is wrong.
This have it read an uninitialized buffer allocated via malloc(), whose value is indeterminate.
The intension of this line is to re-allocate the array pointed at by List->arr, which is an array of char*.
Therefore, the line should be
ArrStrArr_tmp = realloc(List->arr, sizeof(*ArrStrArr_tmp) * List->recs);
just like the following line, which is re-allocating an array of size_t.
SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
Also I found 2 more points for improvement:
Firstly, the usage of some malloc() in the function NewList are not good.
The function is creating zero-element array, so you won't need space for List->arr and List->arrSizes.
Also note that realloc() accepts NULL as the buffer to re-allocate.
list_t *NewList() {
list_t *List = NULL;
List = malloc(sizeof(*List));
if (List == NULL) {
fprintf(stderr, "Failed to allocate memory to list structure.\n");
return NULL;
}
List->arr = NULL;
List->arrSizes = NULL;
List->recs = 0;
return List;
}
Secondly, you are copying pointer instead of string in AddRecord,
so you have problems of memory leak and potential use-after-free.
It seems the string should be copied:
(List->arr)[List->recs - 1] = line->string;
should be
strcpy((List->arr)[List->recs - 1], line->string);

Memory management in Linked Lists C

I am trying to implement a linked list data structure that represents a folder tree.
The structures below:
typedef struct SRC_ERROR SRC_ERROR;
struct SRC_ERROR {
int error_code;
char *error;
};
typedef struct SRC_FILE SRC_FILE;
struct SRC_FILE {
char *entry;
char md5[MD5_DIGEST_LENGTH];
};
typedef struct SRC SRC; //Source file tree with md5 entry char for source verification.
struct SRC {
SRC_ERROR error;
char *name;
char *full_path;
SRC_FILE **entries;
SRC *next_dir;
};
The idea was that each directory will be stored in SRC the SRC_FILE is to be used as an array to store the filename and MD5 hash for each file.
The scan_source() below populates the structures.
SRC *scan_source(char *source_path) {
SRC *source = malloc(sizeof(SRC));
source->error.error_code = OK;
int count = 0;
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(source_path))) {
source->error.error_code = ERROR;
source->error.error = "Unable to open source directory.\n";
return source;
}
source->entries = (SRC_FILE **)malloc(sizeof(SRC_FILE *) * count);
if (source->entries == NULL) {
source->error.error_code = ERROR;
source->error.error = "Unable to allocate memory to file entry tree\n";
}
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
char path[PATH_MAX];
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
snprintf(path, sizeof(path), "%s/%s", source_path, entry->d_name);
printf("[%s] - %s\n", entry->d_name, path);
//add new node
source = add_dir(source, insert_dir_node(entry->d_name, path));
scan_source(path);
} else
if (entry->d_type == DT_REG) {
printf("[FILE] - %s\n", entry->d_name);
source->entries[count]->entry = entry->d_name; //SEGFAULT HERE
count++;
source->entries = realloc(source->entries, sizeof(SRC_FILE *) * (count));
}
}
closedir(dir);
return source;
}
I am having issues with memory management. I am getting intermittent seg faults when the directory is structured in certain ways.
I have marked the line that the debugger has flagged
source->entries[count]->entry = entry->d_name; //SEGFAULT HERE
I thought that I allocated memory for each structure but maybe I have not done this correctly or there is an underlying problem with the data structure entirely?
For Example:
test> tree
.
└── Text
0 directories, 1 file
This causes a seg fault. Whereas, this does not:
/test> tree
.
├── another sample
│   └── Text
└── sample folder
2 directories, 1 file
Additional functions that are used:
SRC *add_dir(SRC *file_tree, SRC *new_dir) {
new_dir->next_dir = file_tree;
return new_dir;
}
SRC *insert_dir_node(char *name, char *full_path) {
SRC *next_dir;
next_dir = (SRC *)emalloc(sizeof(SRC));
next_dir->name = name;
next_dir->full_path = full_path;
next_dir->next_dir = NULL;
return next_dir;
}
I started looking at the code, and the first issue I see is that you're storing pointers returned by a readdir() call - you should copy the data contained therein instead.
Change
source = add_dir(source, insert_dir_node(entry->d_name, path));
to
source = add_dir(source, insert_dir_node(strdup(entry->d_name), path));
The reason you're seeing segmentation faults is that you always write after the end of the source->entries array.
You initially create a 0-size array:
int count = 0;
/* ... */
source->entries = (SRC_FILE **) malloc(sizeof(SRC_FILE*) * count);
Then set its 1st (indexed by 0) element:
source->entries[count]->entry = entry->d_name; //SEGFAULT HERE
count++;
source->entries = realloc(source->entries, sizeof(SRC_FILE*)*(count));
Then you expand the array to 1 element, then write to the second index, and so on.
You can either fix the logic (allocate space for count+1 elements always, because you want to have room not only for the existing ones but also for the next one), or, which in this case may be more efficient, switch to a linked list structure here as well.
The next problem is that you're only allocating pointers to SRC_FILE, not SRC_FILE structures - you should change the definition to:
struct SRC {
SRC_ERROR error;
char *name;
char *full_path;
SRC_FILE *entries;
SRC *next_dir;
};
And the initialization to
source->entries = (SRC_FILE *) malloc(sizeof(SRC_FILE) * (count + 1));
Then the critical part to
source->entries[count].entry = strdup(entry->d_name);
count++;
source->entries = realloc(source->entries, sizeof(SRC_FILE) * (count + 1));
There's one more thing to attend to: insert_dir_node creates a new SRC struct, which will need to have a freshly initialized entries member:
next_dir->count = 0;
next_dir->entries = (SRC_FILE *)malloc(sizeof(SRC_FILE) * (1));
and, since we have now separate entries we need to have a count for each of them, so move this variable into the struct as well.
Fixing all of these provided me with an error-free program.
The subject is Memory management in linked lists. Indeed this is a major issue in C program because there is no automatic memory management. You must decide and specify how each object pointed to by a pointer in your structures is handled from a memory management standpoint. Is the pointer the reference for the object life time or is the lifetime handled somewhere else and the pointer just an access point.
Let's analyse your object definitions:
typedef struct SRC_ERROR SRC_ERROR;
struct SRC_ERROR {
int error_code;
char *error;
};
SRC_ERROR is just a way to package an error description. If the error member always stores a pointer to a string literal, it should be defined as const char *. Conversely, if in some cases you allocate a string with information specific to the actual error, such as "error allocating 1023 objects\n", then you either need an indicator specifying the error points to allocated memory that should be freed after use or you should always allocate memory for the error message and always free this memory when discarding the SRC_ERROR object.
typedef struct SRC_FILE SRC_FILE;
struct SRC_FILE {
char *entry;
char md5[MD5_DIGEST_LENGTH];
};
entry should point to allocated memory and this memory should be freed when discarding the SRC_FILE object.
typedef struct SRC SRC; //Source file tree with md5 entry char for source verification.
struct SRC {
SRC_ERROR error;
char *name;
char *full_path;
SRC_FILE **entries;
SRC *next_dir;
};
name and full_path should point to allocated memory and should be freed when discarding the SRC object.
next_dir points to another SRC object, which should be allocated and freed consistently.
entries points to an allocated array, each element of which points to an allocated object. You need a way to tell the number of elements in this array. You could maintain a NULL pointer at the end of the array, but it is simpler to add a count member in SRC for this information. It would also be much simpler to make this a pointer to an allocated array of SRC objects.
The function does not construct a tree, but attempts to construct a list of directories. Whenever to recurse into a directory, you should append the new list from the SRC_ERROR object returned by scan_source to the list already constructed in the SRC_ERROR object allocated by the caller and free the object returned by the recursive call.
Here is a modified version in a test program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#define MD5_DIGEST_LENGTH 16
#define TRACE(x) //x
enum { OK = 0, ERROR, OUT_OF_MEMORY };
typedef struct ERROR_STATE ERROR_STATE;
struct ERROR_STATE {
int code;
const char *message; // always a string literal
};
typedef struct SRC_FILE SRC_FILE;
struct SRC_FILE {
char *name; // points to allocated memory
char md5[MD5_DIGEST_LENGTH];
};
typedef struct SRC SRC; //Source file tree with md5 entry char for source verification.
struct SRC {
char *name; // points to allocated memory
char *full_path; // points to allocated memory
size_t count; // number of elements in entries
SRC_FILE *entries; // allocated array of count elements
SRC *next_dir; // the next SRC
};
static char *basename_dup(const char *full_path) {
char *p = strrchr(full_path, '/');
return strdup(p ? p + 1 : full_path);
}
/* construct a SRC describing the directory contents.
* if there is an error, either return a partially constructed SRC or return NULL
*/
SRC *scan_source(const char *source_path, ERROR_STATE *error) {
char *full_path = strdup(source_path);
char *name = basename_dup(source_path);
SRC *source = calloc(1, sizeof(SRC)); // all members initialized to 0
if (source == NULL) {
error->code = ERROR;
error->message = "Unable to allocate memory.\n";
free(full_path);
free(name);
free(source);
return NULL;
}
error->code = OK;
source->full_path = full_path;
source->name = name;
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(source_path))) {
error->code = ERROR;
error->message = "Unable to open source directory.\n";
return source;
}
while ((entry = readdir(dir)) != NULL) {
char path[PATH_MAX];
int len;
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
len = snprintf(path, sizeof(path), "%s/%s", source_path, entry->d_name);
if (len >= (int)sizeof(path)) {
// the path was truncated.
// you can report this or ignore it...
TRACE(printf("[%s] - %s - path too long, ignored\n", entry->d_name, path));
continue;
}
if (entry->d_type == DT_DIR) {
TRACE(printf("[%s] - %s\n", entry->d_name, path));
SRC *source1 = scan_source(path, error);
if (error->code != OK) {
// either ignore the error or abort?
}
if (source1) {
// append the new directory (and its list of sub-directories)
SRC **tailp = &source->next_dir;
while (*tailp) tailp = &(*tailp)->next_dir;
*tailp = source1;
}
} else
if (entry->d_type == DT_REG) {
TRACE(printf("[FILE] - %s\n", entry->d_name));
// add the file to the entries list
SRC_FILE *entries = realloc(source->entries, sizeof(source->entries[0]) * (source->count + 1));
if (entries == NULL) {
// you should return to the caller with a proper error code
error->code = OUT_OF_MEMORY;
error->message = "cannot reallocate entries array";
break;
}
source->entries = entries;
// source->entries[count] must point to an allocated object
name = strdup(entry->d_name);
if (name == NULL) {
error->code = OUT_OF_MEMORY;
error->message = "cannot allocate entry name";
break;
}
source->entries[source->count].name = name;
memset(source->entries[source->count].md5, 0, sizeof(source->entries[source->count].md5));
source->count++;
//if (md5_sum(full_path, source->entries[source->count].md5)) {
// // error computing the MD5 sum...
//}
}
}
closedir(dir);
return source;
}
void free_source(SRC *source) {
if (source) {
free(source->name);
free(source->full_path);
for (size_t i = 0; i < source->count; i++) {
free(source->entries[i].name);
}
free(source);
}
}
int main(int argc, char *argv[1]) {
ERROR_STATE error = { 0, NULL };
if (argc < 2) {
printf("usage: scansource directory [...]\n");
return 1;
}
for (int i = 1; i < argc; i++) {
SRC *source = scan_source(argv[i], &error);
if (error.code) {
printf("Error %d: %s\n", error.code, error.message);
}
while (source) {
SRC *cur = source;
source = source->next_dir;
printf("{\n"
" name: '%s',\n"
" full_path: '%s',\n"
" count: %zu,\n"
" entries: [\n",
cur->name, cur->full_path, cur->count);
for (size_t j = 0; j < cur->count; j++) {
printf(" { md5: '");
for (size_t k = 0; k < MD5_DIGEST_LENGTH; k++)
printf("%02x", cur->entries[j].md5[k]);
printf("', name: '%s' },\n", cur->entries[j].name);
}
printf(" ]\n},\n");
free_source(cur);
}
}
return 0;
}

Data corruption in malloc'ed memory

When ever I try to access data in memory that I've acquired using malloc, the data is corrupted
I'm writing a program that reads Linux directories and writes the names of the files and sub-directories in a "string array" (char** array in c). It operates using dirent.h functionalities like readdir(). readdir returns a dirent structure that has a dname[256] that's the name of a file/sub-directory in the target directory. I equate the dirent string(char*) to an index of a malloced position in a char** array
I basically have a walk_path() function that reads the directory entries and writes their names into a malloced location then return that location
data_t* walk_path(char* path) {
int size = 0;
if(path == NULL){
printf("NULL path\n");
return NULL;
}
struct dirent* entry;
DIR* dir_l = opendir(path);
if(dir_l == NULL) {
char** data = (char**)malloc(sizeof(char*) * 2);
data[0] = path;
data_t* ret = (data_t*)malloc(sizeof(data_t));
ret->data = data;
ret->size = 1;
return ret;
}
while((entry = readdir(dir_l)) != NULL) {
if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
size++;
}
closedir(dir_l);
char** data = (char**)malloc(sizeof(char*) * size + 1);
int loop_v = 0;
dir_l = opendir(path);
while((entry = readdir(dir_l)) != NULL && loop_v < size) {
if(!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
data[loop_v] = entry->d_name;
loop_v++;
}
closedir(dir_l);
data_t* ret = (data_t*)malloc(sizeof(data_t*));
ret->size = (size_t)size;
ret->data = data;
return ret;
}
and a merge path function that can take two directories and write their data into a single array
char** merge_path(char* path, char* path2) {
data_t* path_data = walk_path(path);
data_t* path2_data = walk_path(path2);
if(path_data == NULL || path2_data == NULL) {
printf("Merge failed, couldn't get path data\n");
return NULL;
}
char** new_dir_info = (char**)malloc(sizeof(char*) * (path2_data->size + path_data->size) );
if(new_dir_info == NULL)
return NULL;
int loop = 0;
while(loop < path_data->size) {
new_dir_info[loop] = path_data->data[loop];
loop++;
}
loop = 0;
while(loop < path2_data->size) {
new_dir_info[loop + path_data->size] = path2_data->data[loop];
loop++;
}
free(path_data);
free(path2_data);
return new_dir_info;
}
The char** array that the merge path function returns always has corrupted data, that is the characters in the character arrays are corrupted and not the pointers themselves, though I expect it to have the strings passed to it from the directory entries it instead has random strings.
I've stepped through the code and found that the data gets corrupted in merge path function, the source of the error could still originate from walk_path().
This
data_t* ret = (data_t*)malloc(sizeof(data_t*));
ought to be
data_t* ret = (data_t*)malloc(sizeof(data_t));
Generally in C void-pointers do not need to be casted, so all casts to malloc in your code can be dropped, which made the above line look like:
data_t* ret = malloc(sizeof(data_t*));
More over to rule out bugs like this one better step away from doubling the type to mallocate inside the call to malloc(), but better use the variable to allocate to along with the dereferencing operator, like this:
data_t* ret = malloc(sizeof *ret);
Also this line
data[loop_v] = entry->d_name;
copies a pointer to the entry name, not the name itself.
Consider using
data[loop_v] = strdup(entry->d_name);
which dynamically allocates room for a copy of where entry->d_name points to.
Alternatively instead of
char**data;
define
char (*data)[sizeof entry->d_name]; /* Array of pointers to char[as many char as entry->d_name is defined to have] */
or
char (*data)[sizeof ((struct dirent*)NULL)->d_name]; /* Array of pointers to char[as many char as entry->d_name is defined to have] */
and allocate to it like this (following the above proposed pattern):
data = malloc((size /* + 1 */) * sizeof *data); /* Not sure what the idea behind this +1 is. */
And instead of
data[loop_v] = strdup(entry->d_name);
do
strcpy(data[loop_v], entry->d_name);
If going this route you need to adjust the definition of data_t.data accordingly.

How to allocate memory for time_t in a string in C

I'm trying to build a string that will go into a logfile with this format: "Executable: Time:... Error: ...". However, I am having trouble allocating for my time variable in my data structure. I have been able to code it so that the time can go before the string later but I cannot figure out how to have the time be between the executable and the error message. If anyone could tell me what I'm doing wrong I'd appreciate it.
Code:
log.h
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
typedef struct data_struct
{
time_t time;
char *string;
} data_t;
int addmsg(data_t data, char *arg0);
void clearlog(void);
char *getlog(void);
int savelog(char *filename);
loglib.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include "log.h"
//Basic template from Excercise 2.13
typedef struct list_struct
{
data_t item;
struct list_struct *next;
} log_t;
static log_t *headPtr = NULL;
static log_t *tailPtr = NULL;
//Like book example with add on for executable name
int addmsg(data_t data, char *arg0)
{
log_t *newnode;
int nodesize;
char timeString[] = ": Time: ";
char errorString[] = " Error: ";
//Allocate size for "Executable: time: Error: "
nodesize = sizeof(log_t) + strlen(data.string) + strlen(arg0) + sizeof(time_t) + strlen(timeString) + strlen(errorString) + 1;
if((newnode = (log_t *)(malloc(nodesize))) == NULL)
{
perror("Malloc failed: ");
return -1;
}
//Makes string "Executable: time: Error: "
newnode->item.time = data.time;
char *timeCode = ctime(&newnode->item.time);
newnode->item.string = (char *)newnode + sizeof(log_t);
strcpy(newnode->item.string, arg0);
newnode->item.string = strcat(newnode->item.string, timeString);
newnode->item.string = strcat(newnode->item.string, timeCode);
newnode->item.string = strcat(newnode->item.string, errorString);
newnode->item.string = strcat(newnode->item.string, data.string);
newnode->next = NULL;
//Puts string as EOF
if(headPtr == NULL)
{
headPtr = newnode;
}
else
{
tailPtr->next = newnode;
}
tailPtr = newnode;
return 0;
}
//Clears log
void clearlog(void)
{
log_t *nextPtr = headPtr;
//Loop through and clear the log
while(nextPtr != NULL)
{
nextPtr = headPtr->next;
free(headPtr);
headPtr = nextPtr;
}
}
char *getlog(void)
{
int size = 1;//Set to 1 because of null terminator
int entryNum = 0; //Number of error logs
log_t *node = headPtr; //Start at beggining
char *wholeLog = NULL; //Used to return the entire log
char *entryString = NULL;
//Get size
while(node != NULL)
{
entryNum++;
size += strlen(node->item.string);
node = node->next;
}
//Memory allocation
wholeLog = malloc(sizeof(char)*(size + 1 + (entryNum * 2)));
if(wholeLog == NULL)
{
perror("Malloc failed: ");
return NULL;
}
//Reset node to beggining
node = headPtr;
//Builds the entire log to return
while(node != NULL)
{
entryString = strcat(entryString, node->item.string);
wholeLog = strcat(wholeLog, entryString);
wholeLog = strcat(wholeLog, "\n"); //New line
node = node->next;
}
//Make space
wholeLog += (strlen(wholeLog) - size - (entryNum-1));
return wholeLog;
}
int savelog(char *filename)
{
FILE *f;
char *logPrinter;
f = fopen(filename, "a");
if(!f)
{
perror("Error opening file: ");
return -1;
}
//Get the log to print
logPrinter = getlog();
if(logPrinter == NULL)
{
printf("Empty Log\n");
return 0;
}
fprintf(f, "%s\n", logPrinter);
fclose(f);
return 0;
}
Your code seems bent on calculating the size of a memory buffer that would hold both the log_t node structure and the concatenated message parts, having the string pointer within the data_t member of the linked list node point within the single memory block, just passed the linked list node content, where the message is stored. In short, a single allocation holding both the node and the message.
That said, exploit the fact that there are standard library API's, notably snprintf that can calculate formatted message buffer length requirements for you, and you can then skip most of the string management malaise in favor of the real purpose of this, managing the linked list structure and the dynamic message content with a single invoke to malloc (and by circumstance, a single invoke to free() when this fiasco needs to be undone):
Determine the length of the formatted string data
Allocate a buffer large enough to hold that data, and the structure that will precede it.
Position the string pointer in the structure to the first char just passed the structure layout.
Perform the formatted message dump into the memory pointed to by that string pointer.
The result is a single allocation of dynamic length, depending on the content of the message being formatted.
Using snprintf Instead
int addmsg(data_t data, const char *arg0)
{
static const char fmt[] = "%s: Time: %s Error: %s";
char stime[32] = ""; // per ctime docs, must be at least 26 chars
int res = -1;
// get time string, trim newline
ctime_r(&data.time, stime);
if (*stime)
stime[strlen(stime)-1] = 0;
// find needed message size
int req = snprintf(NULL, 0, fmt, data.string, stime, arg0);
if (req > 0)
{
// include size of log record, formatted message, and terminator
log_t *node = malloc(sizeof (log_t) + req + 1);
if (node != NULL)
{
node->item.string = (char*)(node+1); // NOTE: see below
node->item.time = data.time;
node->next = NULL;
snprintf(node->item.string, req+1, fmt, data.string, stime, arg0);
// link into list
if (!headPtr)
headPtr = node;
else
tailPtr->next = node;
tailPtr = node;
// good return value
res = 0;
}
else
{
perror("Failed to allocate memory for log mesage: ");
}
}
else
{
perror("Failed to perform message formatting: ");
}
return res;
}
Everything above is fairly straight forward, save for possible NOTE, which I'll explain now. It uses pointer arithmetic. Given a pointer node of some type log_t* the expression:
(node + 1)
calculates the next position in memory where a subsequent log_t object could reside, or the "one-past" memory position in the case of the end of a sequence. When we allocated our memory, we did so to a layout that looks like this:
node ---> |=== log_t ===|===== message text =====|
the expression (node+1), using typed pointer arithmetic, give us:
node ---> |=== log_t ===|===== message text =====|
(node+1)-----------------^
which is then cast to char*, saved in node->data.string and used as the target for the final message formatting using snprintf. Short version: node->item.string points to the extra memory we allocated following the log_t structure pointed to by node.
That's it. The result is a single allocation to a linked list node that contains both the node management data, and also contains a pointer to the dynamic message string stored in the suffix memory of the single allocation past the log_t core data.
If you replaced the log_t construction piece of addmsg with something perhaps like this you would get better results. Your calculation of required memory size is a little off. Might want to also avoid assuming things about memory with your malloc (i.e. Allocating extra memory to store both a structure and the contents of a pointer member of that structure could easily get you into trouble)
...
log_t *newnode = NULL;
void *vp = NULL;
if (NULL == (vp = malloc(sizeof(log_t)))) {
perror("malloc failed (sizeof(log_t))");
return -1;
}
newnode = (log_t *)vp;
newnode->item.time = data.time;
char *timeCode = ctime(&newnode->item.time);
int msgsize = strlen(": Time: Error: ")
+ strlen(arg0)
+ strlen(timeCode)
+ strlen(data.string)
+ 1;
if (NULL == (vp = malloc(msgsize))) {
perror("malloc failed (msgsize)");
free(newnode);
return -1;
}
newnode->item.string = (char *)vp;
sprintf(newnode->item.string, "%s: Time: %s Error: %s", arg0, timeCode, data.string);
...

is it true to do this to malloc array with unknown size

I want to make an array with unknown size , is it true to make it like this ? :
int *array,var,i=0;
FILE *fp;
fopen=("/home/inputFile.txt","r");
fscanf(fp,"%d",&var);
while(fp!=NULL)
{
if(var>0)
{
array=malloc(sizeof(int));
array[i++]=var
}
fscanf(fp,"%d",&var);
}
This is absurdly false, full of memory leaks and undefined behaviors.
However, it's not that far from one of the right ways, the linked list way:
struct linked_int
{
int value;
struct linked_int* pNext;
};
struct linked_int *pHead=NULL;
struct linked_int **ppTail = &pHead;
int* array = NULL;
int cpt=0;
/*Read file, building linked list*/
FILE *fp = fopen("/home/inputFile.txt","r");
if(fp != NULL)
{
int var;
while(fscanf(fp,"%d",&var)==1)
{
if(var>0)
{
struct linked_int *pNew = malloc(sizeof(struct linked_int));
pNew->value = var;
pNew->pNext = NULL;
/*Append at the tail of the list*/
*ppTail = pNew;
ppTail = &(pNew->pNext);
cpt++;
}
}
fclose(fp);
}
/*Copy from the linked list to an array*/
array = malloc(sizeof(int) * cpt);
if(array != NULL)
{
int i;
struct linked_int const *pCur = pHead;
for(i=0 ; i<cpt ; i++)
{
arr[i] = pCur->value;
pCur = pCur->pNext;
}
}
/*Free the linked list*/
while(pHead != NULL)
{
struct linked_int *pDelete = pHead;
pHead = pHead->pNext;
free(pDelete);
}
ppTail = &pHead;
Other ways:
Another right way is the realloc way, which consists in re-allocating the array with an ever expanding size (usually with a geometric growth, i.e. multiplying the array size by a number such as 1.5 every time). A wrong way to do so is to add 1 to the array size every time.
It goes something like this:
int arrayCapacity=0, numberOfItems=0;
int* array = NULL;
int var;
while(fscanf(fp, "%d", &var)==1)
{
if(numberOfItems >= arrayCapacity)
{
/*Need to resize array before inserting*/
const int MIN_CAPACITY = 4;
const double GROWTH_RATE = 1.5;
int newCapacity = arrayCapacity<MIN_CAPACITY ? MIN_CAPACITY : (int)(arrayCapacity*GROWTH_RATE);
int* tmp = realloc(array, newCapacity*sizeof(int));
if(tmp==NULL)
{
/*FAIL: can't make the array bigger!*/
}
else
{
/*Successfully resized the array.*/
array = tmp;
arrayCapacity = newCapacity;
}
}
if(numberOfItems >= arrayCapacity)
{
puts("Cannot add, array is full and can't be enlarged.");
break;
}
else
{
array[numberOfItems] = var;
numberOfItems++;
}
}
/*Now we have our array with all integers in it*/
The obvious result is that in this code, there can be unused space in the array. This isn't a problem.
sizeof(int) will return you 4 (and note that few compilers/settings may say you 2 or 8 in response). So your code is equivalent to allocating a 4 bytes long array.
If you want an array with unknown size, it could be worth to take a loot at STL containers like std::vector (because it will manage allocations and resizes behind the scene). If you plan to stick with "plain C" scope, you may be interested with TSTL2CL library: http://sourceforge.net/projects/tstl2cl
The basic thing is, ARRAY is STATIC not DYNAMIC.

Resources