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.
Related
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;
}
I'm currently trying to compile a crc calculator I took from github and am having difficulty compiling it on visual studio 2015. I get the error expected constant expression for the line:
char paths[strlen(src) + 1 + strlen(name) + 2 + 1];
Thoughts on how I can resolve the error?
static int create_source(char *src, char *name, FILE **head, FILE **code) {
// for error return
*head = NULL;
*code = NULL;
// create the src directory if it does not exist
int ret = _mkdir(src, 0755);
if (ret && errno != EEXIST)
return 1;
// construct the path for the source files, leaving suff pointing to the
// position for the 'h' or 'c'.
char paths[strlen(src) + 1 + strlen(name) + 2 + 1];
char *suff = stpcpy(path, src);
*suff++ = '/';
suff = stpcpy(suff, name);
*suff++ = '.';
suff[1] = 0;
// create header file
*suff = 'h';
*head = fopen(path, "wx");
if (*head == NULL)
return errno == EEXIST ? 2 : 1;
// create code file
*suff = 'c';
*code = fopen(path, "wx");
if (*code == NULL) {
int err = errno;
fclose(*head);
*head = NULL;
*suff = 'h';
unlink(path);
return err == EEXIST ? 2 : 1;
}
// all good -- return handles for header and code
return 0;
}
Your immediate problem is you are attempting to use a VLA (Variable Length Array), introduced into the standard with C99, with a compiler that does not support VLAs. Without VLA support, arrays must be declared with an integer constant (not simply a const int). As of C11, support for VLAs is optional.
To solve your immediate problem and provide portability, simply allocate storage for paths instead with malloc. The free the memory before you return from your function (either though an error return or on success)
You can do something like:
size_t pathlen = strlen(src) + 1 + strlen(name) + 2 + 1;
char *paths = malloc (pathlen); /* allocate storage for paths */
if (!paths) { /* validate EVERY allocation */
perror ("malloc-paths");
return 3; /* or however you want to handle the error */
}
char *suff = stpcpy(path, src);
...
*head = fopen(path, "wx");
if (*head == NULL) {
free (path); /* free paths */
return errno == EEXIST ? 2 : 1;
}
...
if (*code == NULL) {
int err = errno;
free (path); /* free paths */
fclose(*head);
*head = NULL;
*suff = 'h';
unlink(path);
return err == EEXIST ? 2 : 1;
}
free (path); /* free paths */
return 0;
There is a small overhead for the allocation and free, but it is negligible in your case where there is a single allocation and single free.
As a simple alternative to David's solution, you could also use FILENAME_MAX ...
[...] which expands to an integer constant expression that is the size needed for an array of char large enough to hold the longest file name string that the implementation guarantees can be opened; (§7.21.1, ISO C11)
like this
char paths[FILENAME_MAX];
You might like to check that you don't overflow this size, though.
I am trying to find a way to store the return value of this function...
int createDirectory() {
int buffer = 21; //variable holds buffer count
int pid = getpid(); //variable calls builtin C function to get process id
char* directory = malloc(buffer * sizeof(char)); //dynamically creates array of chars
char* prefix = "schmcory.rooms." ; //prefix is set to my user id and rooms
//counts characters and stores in buffer
snprintf(directory, buffer, "%s%d", prefix, pid);
//printf("%s\n", directory); //for testing purposes
struct stat st = {0}; //initialize system struct
//if statement checks if directory exists
if(stat(directory, &st) == -1) {
mkdir(directory, 0755); //creates directory
}
free(directory); //free dynamically allocated memory
return *directory;
}
...into this variable char* directory = createDirectory();
I am getting the below error:
cast to pointer from integer of different size
I have tried the following:
char* directory = (void*)createDirectory();
char* directory = (int)createDirectory();
char directory = createDirectory(); //compiles w/o warning but prints nothing
I am trying to print the results in my main function.
printf(directory);
2 issue with the code :
return value of the function is char * not int - char * createDirectory();
you do free before you return the pointer
here is the code working (Linux environment)
//free(directory);
return directory;
}
int main( int argc, const char* argv[] )
{
char* directory = createDirectory();
printf ("main says %s \n", directory);
free(directory);
}
If I understand correctly, you want to return the result of mkdir, not the string containing the path, switch to
int ret = -1;
if (stat(directory, &st) != -1) // Notice `!=` instead of `==`
{
ret = mkdir(directory, 0755);
if (ret == -1)
{
perror("mkdir")
}
}
free(directory);
return ret;
and the caller should do something like:
int dir;
if ((dir = createDirectory()) != -1)
{
... do your stuff with dir
}
else
{
... raise exception
}
EDIT:
It seems that you want to return something like: schmcory.rooms.49111
In this case, the prototype should be
char *createDirectory(void) { // Return a pointer to `char`
instead of
int createDirectory() {
But do not call free or you end up deleting the result:
free(directory); // Remove this line
and
return *directory;
should be
return directory; // Do not dereference, you don't want the first char, you want the whole string
I tried many solution for this issue, but none worked properly!
I want to copy value of char** array to a variable of type char*.
char *line;
char **tokens = malloc(....);
So, I tried the following:
for(i=0; i < sizeof(tokens); i++)
strncpy(line, tokens[i], strlen(line));
Or
for(i=0; i < sizeof(tokens); i++)
strncat(line, tokens[i]);
Or
for(i=0; i < sizeof(tokens); i++)
memcpy(line, tokens[i], strlen(line));
My understanding is that tokens[i] would be of type char*, but what I couldn't understand if the error I'm getting.
Segmentation fault (core dumped)
If these ways won't work, how can I do the copying?
Any hints?
char *removesubString(char *path, char **args){
char *dir;
int COUNT;
COUNT = 100;
char **dirs = malloc(sizeof(char*)*COUNT);
int i, position;
for (i = 2; i < sizeof(args); i++) {
if(args[i] == NULL){
break;
}
dir = strtok(path, PATH_DELIM);
position = 0;
while (dir != NULL) {
if(strcmp(dir, args[i]) == 0){
dir = strtok(NULL, PATH_DELIM);
continue;
}
dirs[position] = dir;
position++;
dir = strtok(NULL, PATH_DELIM);
}//end of while
dirs[position] = NULL;
}//end of for
char *line;
line = "";
for (i = 0; i < position; i++) {
strncpy(line, dirs[i], strlen(dirs[i]));
}
return line;
}
The first issue that pops up about your code is that you're wrong with the boundaries:
char *line;
char **tokens = malloc(TOKENS_ARRAY_SIZE);
when you do:
for(i=0; i < sizeof(tokens); i++) {
…
}
it's not returning the size of the allocated memory, but the allocated memory for the tokens pointer itself. From the sizeof manpage:
Returns the size, in bytes, of the object representation of type
It happens that when you do sizeof on a static matrix, it will return the size of the matrix because that's the amount of allocated memory for it. But for a dynamically allocated matrix, it will only return the size of the pointer, i.e. if you do:
char array_static[42];
char* array_dyn = malloc(sizeof(char)*42);
printf("sizeof(array_static) = %d\n", sizeof(array_static));
printf("sizeof(array_dyn) = %d\n", sizeof(array_dyn));
it will return:
sizeof(array_static) = 42
sizeof(array_dyn) = 8
so if the number of items within your dynamic array is less than the returned size of the array's pointer, you'll overflow and you'll get a segfault.
So the right way to handle your situation, is to keep the length of the dynamic array in another variable, update it as you're setting up the size of the allocated memory, and then use that value for iterations.
int tokens_length = 42;
char *line;
char **tokens = malloc(sizeof(char*)*tokens_length);
for(i=0; i < sizeof(tokens_length); i++) {
…
}
so in your case, you should be doing:
// keep somewhere the number of directories that you *can* allocate
int nb_dirs = 100;
char **dirs = malloc(sizeof(char*) * nb_dirs);
…
// keep a pointer on how many directories you've added
int position = 0;
while (dir != NULL) {
…
position++;
// fail loudly if you're adding more directories than you've allocated
// or you might use realloc() to extend the array's length
if (position >= nb_dirs) {
printf("ERROR! Too many directories!");
// RETURN WITH ERROR!
}
…
}
// here you iterate over all the directories you've added
for(i = 0; i <= position; i++){
// here you can do stuff with dirs, and copy only the length of the dirs element
strncpy(<TARGET>, dirs[i], strlen(dirs[i]);
}
Then there's another issue you should think about: in your loop, you're modifying path, given as an argument, where you're strcpy()ing dirs into:
strncpy(path, dirs[i], <LENGTH>);
But that makes little sense, whatever you're trying to do is not what you've written.
Here, considering that the size argument is correctly set, you'd be copying each item of the dirs array into the same variable. So you'd end up having always the last value of the dirs array referenced at the path pointer.
But the issue is that you only have the path pointer, but you know little about how it has been allocated when it's been given to the function. How has it been allocated, and how much memory was allocated? What "useful" size is it (though that one can be guessed with strlen())?
Oh, and finally, don't forget to free() your allocations once you're done with them. Do not leak memory, that's rude! ☺
edit:
ok, here are stuff I can see that are wrong, and some comments about it:
char *removesubString(char *path, char **args){
char *dir;
int COUNT = 100;
char **dirs = malloc(sizeof(char*)*COUNT);
int i, position;
/* for both XXX marks below:
*
* below, sizeof(args) will return the size of the pointer
* not the number of items it contains. You *NEED* to pass
* a "int argc" as parameter to your function, that gives
* the numbers of items in the array.
* Think about why you have argc in the main() function construct:
* int main(int argc, const char** argv)
* _OR_ if the args array of strings is _ALWAYS_ terminated
* by a NULL item, then you should do: */
// int i = 0;
// while(args[i] != NULL) {
// /* do stuff */
// ++i;
// }
for (i = 2; i < sizeof(args) /* XXX */; i++) {
if(args[i] == NULL){ /* XXX */
break;
}
dir = strtok(path, PATH_DELIM);
position = 0;
while (dir != NULL) {
if(strcmp(dir, args[i]) == 0){
dir = strtok(NULL, PATH_DELIM);
continue;
}
/* because within the function you have no guarantee
* on the number of tokens within path, if you have
* more than 100 tokens, you will overflow the dirs array.
* a good idea would be to test whether position is bigger
* than or equal to COUNT, and if it is use realloc to
* extend dirs */
dirs[position] = dir;
position++;
dir = strtok(NULL, PATH_DELIM);
/* you could avoid having twice the former line
* and instead make your loop body being: */
// while (dir != NULL) {
// if(strcmp(dir, args[i]) != 0){
// /* TODO: check position vs COUNT and realloc dirs if necessary */
// dirs[position] = dir;
// ++position;
// }
// dir = strtok(NULL, PATH_DELIM);
// }
}
dirs[position] = NULL;
}
char *line;
line = ""; /* ← here you allocate line with a string of length 1 */
for (i = 0; i < position; i++) {
// so here, you'll write up to the number of characters
// within dirs[i], into a string of length one.
strncpy(line, dirs[i], strlen(dirs[i]));
}
/* And even if it was working you'd be assigning the line
* variable to a new value at each iteration, ending up doing
* something equivalent to the following line, but with "position"
* numbers of iterations: */
// strncpy(line, dirs[position-1], strlen(dirs[position-1]));
/* Don't forget to free the instances you've allocated dynamically
* before leaving the function: */
// free(dirs);
/* And finally there's another issue here, you're returning
* a variable that has been statically allocated above, so that
* when you'll try to use the pointed instance in the calling
* context, that variable won't exist anymore. */
return line;
}
HTH
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.