C: Pointer to a structure element - c

I tried to read on it from past questions and watch youtube videos but i don't get it.
I have a program with struct called info. I created a function that adds element of the structure and return a pointer to it.
then i want to use the element fields through the pointer.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
struct Info {
char* name;
char type;
char* path;
};
struct Info* AddInfo(char* input);
int main(void) {
char input[128];
fgets(input, sizeof(input), stdin);
struct info *Puser;
Puser=malloc(sizeof(AddInfo(input)));
&Puser=AddInfo(input);
//here is my problem.
return 0;
}
struct Info *AddInfo( char* input) {
struct Info user1;
struct info* Puser=0;
char *s;
//assign to name
for (int i = strlen(input) - 1; i >= 0; i--) {
if (input[i] == '/') {
s = malloc(sizeof(input));
strncpy(s, input + i + 1, i);
user1.name = malloc(sizeof(s));
if (user1.name == NULL) {
fprintf(stderr, "%s\n", "Error in malloc");
}
strcpy(user1.name, s);
user1.name[i] = '\0';
free(s);
break;
}
}
//assign to type
if ((user1.type = malloc(sizeof(user1.type)) == NULL)) {
fprintf(stderr, "%s\n", "Error in malloc");
}
if (input[strlen(input) - 1] == '/') {
user1.type = 'd';
} else {
user1.type = 'f';
}
//assign to path
user1.path = malloc(sizeof(input));
if (user1.path == NULL) {
fprintf(stderr, "%s\n", "Error in malloc");
}
strcpy(user1.path, input);
// printf("%s \n", user1.path);
// printf("%s\n", user1.name);
// free(user1.name);
Puser= &user1;
return Puser;
}
How should i do that correctly ? how can i take user1 and access it through pointer outside the function ?
Thanks in advance

Your function AddUser assigns all the data to a function-local variable then returns a pointer to that data, but as soon as the function returns that local is no longer valid, you need to allocate the new Info in AddUser rather than main and assign the data to that allocated instance and return that pointer.
typedef struct Info {
char * name;
...
} Info;
Info * AddUser(char const * name);
int main()
{
Info * pNewUser = AddUser("Bob");
...
free(pNewUser);
return 0;
}
Info AddUser(char const * name)
{
if(!name || !*name)
return NULL;
Info * pNew = malloc(sizeof(Info));
if(!pNew)
return NULL;
size_t len = strlen(name);
pNew->name = malloc(len+1);
if(!pNew->name)
{
free(pNew);
return NULL;
}
strcpy(pNew->name, name);
return pNew;
}

Related

Dynamic array of structs memory storage

I am trying to create a dynamic array of Clients, but i am not succeding. This is my code. When i run this code the output is
3
� H
3
4332
3
8939
I think it's printing memory stuff, however i don't know why. I put my code down here
int client_counter = 0;
typedef struct client
{
char *pid;
char *message;
}Client;
void store (Client * client_array, char *buf)
{
Client c;
c.pid = strdup (strtok (buf, ":"));
c.message = strdup (strtok (NULL, "\0"));
client_array[client_counter++] = c;
}
int main () {
Client* client_array = malloc (sizeof (struct client));
char buf1[50] = { "1245:message" };
store (client_array, buf1);
char buf2[50] = { "4332:message" };
store (client_array, buf2);
char buf3[50] = { "8939:message" };
store (client_array, buf3);
for (int i = 0; i < client_counter; i++)
{
printf ("%d\n", client_counter);
printf ("%s\n", client_array[i].pid);
}
return 0;
}
I already tried do use this:
client_array = realloc(client_array, sizeof(struct client) * (client_counter + 1));
in store function right after this line.
client_array[client_counter++] = c;
But it's not working too.
You need to allocate extra memory if there's not enough space. Right now, you allocate enough for one, but you try to access three.
Don't forget to return the pointer of the new memory block back to main! In the following, this is done by passing a pointer to the caller's pointer. store modifies the caller's pointer via the passed pointer.
// Sets errno and returns 0 on error.
int store(Client ** client_array_ptr, char *buf) {
Client* new_client_array = realloc(*client_array_ptr, sizeof(Client) * (client_counter + 1));
if (!new_client_array)
return 0;
*client_array_ptr = new_client_array;
// ...
new_client_array[client_counter++] = c;
return 1;
}
int main() {
Client* client_array = NULL;
// ...
if (!store(&client_array, buf1)) {
perror("malloc");
exit(1);
}
// ...
if (!store(&client_array, buf2)) {
perror("malloc");
exit(1);
}
// ...
free(client_array);
return 0;
}
The original code does out-of-range access because it is trying to store multiple data in a buffer which is allocated for only one element.
To use realloc(), you have to note that arguments of functions in C are copies of what are passed. Modifying arguments inside callee function do not affect what is passed in caller. You should pass pointers to what should be modified to have functions modify caller's local things.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int client_counter = 0;
typedef struct client
{
char *pid;
char *message;
}Client;
void store (Client ** client_array, char *buf)
{
Client c;
c.pid = strdup (strtok (buf, ":"));
c.message = strdup (strtok (NULL, "\0"));
*client_array = realloc(*client_array, sizeof(struct client) * (client_counter + 1));
(*client_array)[client_counter++] = c;
}
int main () {
Client* client_array = malloc (sizeof (struct client));
char buf1[50] = { "1245:message" };
store (&client_array, buf1);
char buf2[50] = { "4332:message" };
store (&client_array, buf2);
char buf3[50] = { "8939:message" };
store (&client_array, buf3);
for (int i = 0; i < client_counter; i++)
{
printf ("%d\n", client_counter);
printf ("%s\n", client_array[i].pid);
}
return 0;
}
I would do it a bit different way.
typedef struct
{
char *pid;
char *message;
}client_TypeDef;
typedef struct
{
size_t size;
client_TypeDef clients[];
}clients_TypeDef;
clients_TypeDef *add(clients_TypeDef *clients, const char *pid, const char *message)
{
size_t newsize = clients ? clients -> size + 1 : 1;
client_TypeDef client = {.pid = strdup(pid), .message = strdup(message)};
if(client.pid && client.message)
{
clients = realloc(clients, sizeof(*clients) + newsize * sizeof(clients -> clients[0]));
if(clients)
{
clients -> size = newsize;
clients -> clients[newsize - 1] = client;
}
}
else
{
free(client.pid);
free(client.message);
clients = NULL;
}
return clients;
}

How to dynamically allocate memory for every element in 2D array in C

how to dynamically allocate memory for every element in 2D array?
For example, I have an address which consist of street name(1.column) and street number(2.column) e.g. St.Paul 87/45. And I need to dynamically allocate memory for them.
int main()
{
int c = 1, r = 1; //c-column, r-row
char st_name[] = "St.Paul";
char st_number[] = "87/45";
char *arr = (char *)malloc(c*r*sizeof(char));
c = 0;
r = 0;
*arr[r][c++] = (char *)malloc((strlen(st_name)) * sizeof(char));
*arr[r][c] = (char *)malloc((strlen(st_number)) * sizeof(char));
return 0;
}
Of course it´s not working.
Thanks.
What you need is a matrix, but each matrix item is a c-string so an array.
You can do it using 3 starts pointer, like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int c = 1, r = 1; //c-column, r-row
char st_name[] = "St.Paul";
char st_number[] = "87/45";
char ***arr = malloc(r*sizeof(char *));
c = 0;
r = 0;
if (arr != NULL)
{
arr[r] = malloc(c*sizeof(char *));
if (arr[r] != NULL)
{
arr[r][c] = malloc(strlen(st_name)+1);
if (arr[r][c] != NULL)
{
c++
arr[r][c] = malloc(strlen(st_number)+1);
if (arr[r][c] != NULL)
{
sprintf( arr[0][0], st_name);
sprintf( arr[0][1], st_number);
printf ("arr[0][0] = %s\n", arr[0][0]);
printf ("arr[0][1] = %s\n", arr[0][1]);
}
}
}
}
return 0;
}
Or you can do that using a struct that defines each item of your array, like
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct item
{
char *name;
char *number;
};
int main(void)
{
char st_name[] = "St.Paul";
char st_number[] = "87/45";
int r = 1;
struct item *arr = malloc(r*sizeof(struct item));
if (arr != NULL)
{
arr[0].name = malloc(strlen(st_name)+1);
arr[0].number = malloc(strlen(st_number)+1);
if ((arr[0].name != NULL) && (arr[0].number != NULL))
{
sprintf( arr[0].name, st_name);
sprintf( arr[0].number, st_number);
printf ("arr[0][0] = %s\n", arr[0].name);
printf ("arr[0][1] = %s\n", arr[0].number);
free(arr[0].name);
free(arr[0].number);
}
free(arr);
}
return 0;
}

Need help i get segmentation fault in program in C

First I will explain what I want to program. I want to open a directory and get all the files names and their number (in the directory). I want to allocate certain amount of memory for the number of files I have found, and a certain amount of memory for their names. I will give an example.
Lets say we have got a directory named dir and that directory has 4 files in it. The 4 files are named as follows: lalo, camo, mara, sarw.
I want my program to work as follows
Go into dir and while finding files keep allocating memory in order to store them (like a table of strings where I don't know the exact size because I don't know how many files I will find). I am storing the filenames character by character and I keep allocating memory as needed, every time I read one char I am realocating memory and incrementing by 1. Same logic I follow to the files number where I keep incrementing the number of files and reallocating memory as needed. A table like this (pointers) is what I imagine filenames[numberOfFiles][stringCharacter].
I get a segmentation fault in function GetFiles(...) on the second loop here : *(*(entries+*number)+size) = dp->d_name[size];
int main()
{
int number,i;
char *mainDirectory = NULL,**names = NULL;
printf("Give the movies directory: ");
mainDirectory = ReadMainDirectory();
if(GetFiles(mainDirectory,&names,&number) != 0)
{
system("PAUSE");
return 1;
}
system("PAUSE");
return 0;
}
char* ReadMainDirectory()
{
char *dir,c;
int size = 1;
dir = (char*)malloc(size+1);
dir[size-1] = '\0';
while((c = getchar()) != '\n')
{
dir[size-1] = c;
size++;
dir = (char*)realloc(dir,size+1);
dir[size-1] = '\0';
}
return dir;
}
int GetFiles(char *dir,char ***names,int *number)
{
struct dirent *dp;
DIR *fd;
int size;
char **entries = NULL;
if ((fd = opendir(dir)) == NULL)
{
printf("Can't open directory %s!\n",dir);
return 0;
}
*number = 0;
size = 0;
entries = (char**)malloc((*number+1) * sizeof(char*));
*entries = (char*)malloc((size+1) * sizeof(char));
while ((dp = readdir(fd)) != NULL)
{
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
{
continue;
}
size = 0;
while(dp->d_name[size] != '\0')
{
*(*(entries+*number)+size) = dp->d_name[size];
size++;
*(entries+*number) = (char*)realloc(entries[*number],(size+1) * sizeof(char));
}
entries[*number][size] = '\0';
printf("%s\n",entries[*number]);
(*number)++;
entries = (char**)realloc(entries,(*number+1) * sizeof(char*));
}
*names = entries;
closedir(fd);
return 1;
}
int GetStringSize(char *string)
{
int size = 0;
while(string[size] != '\0')
{
size++;
}
return size;
}
int StringEndsWith(char* string,char* extension)
{
return !strcmp(string + strlen(string) - strlen(extension), extension);
}
A proper implementation of getfiles (gets all the files in a directory in a dynamic array) and provides a deallocator:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
/* returns a NULL terminated array of files in directory */
char **getfilelist(const char *dirname) {
DIR *dp;
char **entries = NULL;
struct dirent *entry;
off_t index = 0;
dp = opendir(dirname);
if (!dp) {
perror(dirname);
return NULL;
}
while((entry = readdir(dp)) != NULL) {
/* increase entries array size by one pointer-size */
entries = realloc(entries, (1+index)*sizeof(char *));
/* strdup returns a newly allocated duplicate of a string that
* you must free yourself
*/
entries[index] = strdup(entry->d_name);
index++;
}
/* need one more entry for NULL termination */
entries = realloc(entries, (1+index)*sizeof(char *));
entries[index] = NULL;
return entries;
}
void putfilelist(char **entries) {
char **p;
if(!entries)
return;
for(p = entries; *p !=NULL ; p++) {
free(*p); /* free all the strdups */
}
free(entries); /* free the array of pointers */
}
An example of how you could use it:
int main(int argc, char **argv) {
char **p, **entries;
char *dir = (argc == 1 ? "." : argv[1]);
entries = getfilelist(dir);
for (p = entries; p && *p; p++) {
printf("%s\n", *p);
}
putfilelist(entries);
}
Updated solution,
In order to implement your solution without using any of the library code, and keep it to system calls, here is an example that implements everything that the above example does, without relying on higher-level library calls.
Note
The bottom functions are the same as the above example.
/*
* A partially low-level implementation using a direct system calls
* and internal memory management
*
* This is an opinionated (linux only) implementation of OP
*
* linux headers are only included for some of the constants and
* where it would be trivial to re-implement system calls (e.g. open, write etc.)
* which I am too lazy to do in this example.
*
*/
#define NDEBUG
#include <stdarg.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fcntl.h>
#include <linux/unistd.h>
/* replace this macro with your own error handling messages */
#define handle_error(msg) \
do { my_fdputs(2, msg); my_exit(-127); } while (0)
#if !defined(NDEBUG)
#define assert(x) do { int __rv = (x); if(!__rv) { my_fdprintf(2, "assertion failed %s\n", #x); my_exit(__rv); } } while(0)
#else
#define assert(x) do {} while(0)
#endif
/* returns a NULL terminated array of files in directory */
/* low-level list builder using getdents */
void my_exit(int x)
{
syscall(SYS_exit_group, x);
}
/* a trivial malloc / realloc / free */
/* simple linked list memory accounting */
struct memblock {
struct memblock *next;
size_t size;
int free; /* flag */
int magic; /* canary value for debugging */
};
#define METASIZE sizeof(struct memblock)
void *global_base = NULL;
struct memblock *find_free_block(struct memblock **last, size_t size)
{
struct memblock *current = global_base;
while(current && !(current->free && current->size >= size)) {
*last = current;
current = current->next;
}
return current;
}
/*
* instead of using sbrk, we should really use mmap on newer
* linux kernels and do better accounting, however this is a
* simple example to get you started
*/
struct memblock *request_space(struct memblock *last, size_t size)
{
struct memblock *block;
void *request;
block = sbrk(0); /* get current program break */
request = sbrk(size + METASIZE);
assert((void *)block == request);
if(request == (void *)-1) {
return NULL;
}
if(last) {
last->next = block;
}
block->size = size;
block->next = NULL;
block->free = 0;
block->magic = 0x12345678;
return block;
}
struct memblock *get_memblock_ptr(void *ptr)
{
return (struct memblock *)ptr - 1;
}
/* a simple memcpy, can be optimized by taking alignment into account */
void *my_memcpy(void *dst, void *src, size_t len)
{
size_t i;
char *d = dst;
const char *s = src;
struct memblock *bd, *bs;
bd = get_memblock_ptr(dst);
for(i = 0; i < len; i++) {
d[i] = s[i];
}
return dst;
}
/* now to implement malloc */
void *my_malloc(size_t size)
{
struct memblock *block;
if(size == 0)
return NULL;
if(!global_base) {
block = request_space(NULL, size);
if(!block)
return NULL;
global_base = block;
}
else {
struct memblock *last = global_base;
block = find_free_block(&last, size);
if(!block) {
block = request_space(last, size);
if(!block) {
return NULL;
}
}
else {
block->free = 0;
block->magic = 0x77777777;
}
}
return (block+1);
}
void my_free(void *ptr)
{
struct memblock *block;
if (!ptr)
return;
block = get_memblock_ptr(ptr);
assert(block->free == 0);
assert(block->magic == 0x77777777 || block->magic == 0x12345678);
block->free = 1;
block->magic = 0x55555555;
}
void *my_realloc(void *ptr, size_t size)
{
struct memblock *block;
void *newptr;
if(!ptr)
return my_malloc(size);
block = get_memblock_ptr(ptr);
if(block->size >= size)
return ptr;
newptr = my_malloc(size);
if(!newptr) {
return NULL;
}
my_memcpy(newptr, ptr, block->size);
my_free(ptr);
return newptr;
}
/* trivial string functions */
size_t my_strlen(const char *src) {
size_t len = 0;
while(src[len])
len++;
return len;
}
char *my_strdup(const char *src)
{
char *dst;
char *p;
size_t len = 0, i;
len = my_strlen(src);
dst = my_malloc(1+len);
if(!dst)
return NULL;
for(i = 0; i < len; i++) {
dst[i] = src[i];
}
dst[i] = 0;
return dst;
}
/* trivial output functions */
my_fdputs(int fd, const char *str)
{
return write(fd, str, my_strlen(str));
}
int my_fdputc(int fd, char c)
{
return write(fd, &c, sizeof(char));
}
/* a very limited implementation of printf */
int my_fdvprintf(int fd, const char *fmt, va_list ap)
{
const char *p;
int count = 0;
for(p = fmt; p && *p; p++ ) {
if(*p == '%') {
p++;
switch(*p) {
case 's':
count += my_fdputs(fd, va_arg(ap, char *));
break;
case '%':
count += my_fdputc(fd, '%');
break;
default:
#ifndef NDEBUG
my_fdputs(2, "warning: unimplemented printf format specifier %");
my_fdputc(2, *p);
my_fdputc(2, '\n');
#endif
break;
}
}
else {
my_fdputc(fd, *p);
}
}
return count;
}
int my_fdprintf(int fd, const char *fmt, ...)
{
int rv;
va_list ap;
va_start(ap, fmt);
rv = my_fdvprintf(fd, fmt, ap);
va_end(ap);
return rv;
}
/* wrapper to linux getdents directory entry call */
/* linux dirent structure */
struct linux_dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
/* system call wrapper */
int getdents(int fd, void *buf, size_t bufsize)
{
return syscall(SYS_getdents, fd, buf, bufsize);
}
/* reimplement getfilelist using our getdents */
#define BUF_SIZE 1024
char **getfilelist(const char *dirname) {
int fd, nread;
char **entries = NULL;
off_t index = 0;
char buf[BUF_SIZE];
struct linux_dirent *d;
int bpos;
/* O_DIRECTORY since kernel 2.1 */
fd = open(dirname, O_DIRECTORY|O_RDONLY);
if (fd < 0) {
handle_error(dirname);
}
for(;;) {
nread = getdents(fd, buf, BUF_SIZE);
if (nread == -1)
handle_error("getdents");
if (nread == 0)
break;
for (bpos = 0; bpos < nread;) {
d = (struct linux_dirent *) (buf + bpos);
entries = my_realloc(entries, (1+index) * sizeof(char *));
entries[index++] = my_strdup(d->d_name);
bpos += d->d_reclen;
}
}
/* need one more entry for NULL termination */
entries = my_realloc(entries, (1+index)*sizeof(char *));
entries[index] = NULL;
close(fd);
return entries;
}
void putfilelist(char **entries) {
char **p;
if(!entries)
return;
for(p = entries; *p !=NULL ; p++) {
my_free(*p); /* free all the strdups */
}
my_free(entries); /* free the array of pointers */
}
int main(int argc, char **argv) {
char **p, **entries;
char *dir = (argc == 1 ? "." : argv[1]);
entries = getfilelist(dir);
for (p = entries; p && *p; p++) {
my_fdprintf(1, "%s\n", *p);
}
putfilelist(entries);
}
Hope you enjoy

How to get only *.txt filenames from directory in c and write it to char ** array?

Its an extension of my previous question: How can I get only txt files from directory in c?. Now I want to save those filenames (I dont really know how many of them are in the dir) to char ** array. I figured out a solution (kind of) but then I realized that I need not a char* but char ** (I know, stupid me :])
Anyway, I got segmentation fault [core dumped] with this code:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdbool.h>
char* allocMemory(int n)
{
char *tab = (char *) malloc(n*sizeof(char));
return tab;
}
void freeMemory(char **tab, int n, int m)
{
int i=0;
for(i=0; i<m; i++)
free(tab[i]);
free(tab);
tab = NULL;
}
bool hasTxtExtension(char const *filename)
{
size_t len = strlen(filename);
return len > 4 && strcmp(filename + len - 4, ".txt") == 0;
}
char** getTxtFilenames(const char *dirname)
{
DIR *directory = NULL;
struct dirent *ent = NULL;
int fileCounter = 0;
char **txtFiles = allocMemory(1);
char **moreTxtFiles = allocMemory(1);
directory = opendir (dirname);
if(directory == NULL)
return NULL;
int i = 0;
while ((ent = readdir (directory)) != NULL)
{
if(hasTxtExtension(ent->d_name))
{
fileCounter ++;
moreTxtFiles = (char**) realloc (txtFiles, fileCounter * sizeof(char*));
if(moreTxtFiles[i] != NULL)
{
txtFiles = moreTxtFiles;
txtFiles[i] = allocMemory(strlen(ent->d_name));
txtFiles[i][fileCounter - 1] = ent->d_name;
}
else
{
freeMemory(txtFiles, 1, fileCounter);
return NULL;
}
}
i ++;
}
if(closedir(directory) < 0)
return NULL;
return txtFiles;
}
int main(int argc, char **argv)
{
char **txtFilenames = getTxtFilenames("dir");
if(txtFilenames == NULL)
return -1;
printf("%s\n", txtFilenames[0][1]);
return 0;
}
EDIT:
I also tried this: (notice that Im a little bit confused with those not-lovely char arrays in C, argh :/)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdbool.h>
char* allocMemory(int n)
{
char *tab = (char *) malloc(n*sizeof(char));
return tab;
}
void freeMemory(char *tab)
{
free(tab);
tab = NULL;
}
bool hasTxtExtension(char const *filename)
{
size_t len = strlen(filename);
return len > 4 && strcmp(filename + len - 4, ".txt") == 0;
}
char* getTxtFilenames(const char *dirname)
{
DIR *directory = NULL;
struct dirent *ent = NULL;
int fileCounter = 0;
char *txtFiles = NULL;
char *moreTxtFiles = NULL;
directory = opendir (dirname);
if(directory == NULL)
return NULL;
while ((ent = readdir (directory)) != NULL)
{
if(hasTxtExtension(ent->d_name))
{
fileCounter ++;
moreTxtFiles = (char*) realloc (txtFiles, fileCounter * sizeof(char));
if(moreTxtFiles != NULL)
{
txtFiles = moreTxtFiles;
txtFiles[fileCounter - 1] = ent->d_name;
}
else
{
freeMemory(txtFiles);
return NULL;
}
}
}
if(closedir(directory) < 0)
return NULL;
return txtFiles;
}
int main(int argc, char **argv)
{
char **txtFilenames = getTxtFilenames("dir");
if(txtFilenames == NULL)
return -1;
printf("%s\n", txtFilenames[0]);
return 0;
}
txtFilenames[0][1] is a character, not a string.
txtFiles[i][fileCounter - 1] is a character, but ent->d_name is a
string.
I don't understand why you are using two index (i and fileCounter). Just use an array of strings.
You should also include <string.h>.
The solution is simpler:
#include <direct.h>
#include <stdlib.h>
#include <string.h>
char **f(const char *s)
{
char **p = NULL;
DIR *dir;
struct dirent *ent;
size_t i = 0;
dir = opendir(s);
while ((ent = readdir(dir)) != NULL) {
if (hasTxtExtension(ent->d_name)) {
p = realloc(p, (i + 1) * sizeof(char *));
p[i] = malloc(strlen(ent->d_name) + 1);
strcpy(p[i], ent->d_name);
++i;
}
}
closedir(dir);
return p;
}
Here is a non-exhaustive list to improve this code:
Handle reallocation errors: if realloc fail, there is a memory leak.
Handle directory errors: especially, treat the case where s is a bad directory path.
Optimize reallocations (don't use realloc with each iteration).
Aside from the questionable indexing Kirilenko pointed out, your first (and core) error is:
char **txtFiles = allocMemory(1);
char **moreTxtFiles = allocMemory(1);
Which seems innocent enough, but allocMemory is defined as:
char* allocMemory(int n)
{
char *tab = (char *) malloc(n*sizeof(char));
return tab;
}
Both txtFiles and moreTxtFiles are pointers to pointers; not pointers to chars. You're returning a pointer to a single allocated char, then storing it in a pointer that expects it to be a pointer to a pointer.
For your case, don't use allocMemory for allocating list entries. Just use it for allocating your strings.

Learning Pointers in C

I have been cutting my teeth for the past 48 hours or so trying to implement this hash table function in C. My code is rather long (I realize it is not the most efficient, some of it is more me playing around with C to get a feel for how it works etc).
The problem I am having is with the last line of my main program at the bottom (printing MyEntry->Name). I am receiving a bus error and am unsure why. I do not believe I am supposed to allocate memory in the main driver for this pointer but I could be wrong.
Sorry about the length of this code. BTW SymEntry is 'struct SymEntry{char *Name, void *Attributes, struct SymEntry *Next}
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>
#include "SymTab.h"
struct SymTab * CreateSymTab(int Size)
{
struct SymTab *symtable;
if(!(symtable=malloc(sizeof(struct SymTab)))) return NULL;
if(!(symtable->Contents=calloc(Size, sizeof(struct SymEntry*)))) {
free(symtable);
return NULL;
}
symtable->Size=Size;
return symtable;
}
/* hash form hash value for string s, taken from 'The C Programming Language'*/
unsigned hash(struct SymTab *ATable, const char *s)
{
unsigned hashval, size;
size = ATable->Size;;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % size;
}
bool EnterName(struct SymTab *ATable,
const char *Name,
struct SymEntry **AnEntry)
{
struct SymEntry *ptr;
unsigned hashvalue;
char *string;
struct SymEntry *previous;
string = malloc(strlen(Name)+1);
AnEntry=(struct SymEntry**)malloc(sizeof(struct SymEntry*));
strcpy(string, Name);
printf("string is: is %s\n",string);
hashvalue = hash(ATable, string);
printf("hv is %d\n",hashvalue);
ptr = ATable->Contents[hashvalue];
previous = NULL;
while(ptr)
{
printf("WHILE LOOP\n");
if(!(strcmp(ptr->Name,string)))
{
printf("if(!strcmp(ptr->Name,string))\n");
*AnEntry = ptr;
return true;
}
previous = ptr;
ptr=ptr->Next;
}
if(previous)
{
printf("IF (PREVIOUS)\n");
if(!(ptr=malloc(sizeof(struct SymEntry)))) return false;
if(!(ptr->Name=string))
{
printf("if(!(ptr->Name=string))\n");
free(ptr);
return false;
}
ptr->Name = string;
previous->Next = ptr;
printf("Previous->Next: %s\n", previous->Next->Name);
*AnEntry = ptr;
return false;
}
else
{
printf("ELSE (PREVIOUS)\n");
if(!(ptr=malloc(sizeof(struct SymEntry)))) return false;
if(!(ptr->Name=string))
{
printf("if(!(ptr->Name=string))\n");
free(ptr);
return false;
}
ptr->Name = string;
ATable->Contents[hashvalue] = ptr;
printf("here\n");
*AnEntry = ptr;
printf("there\n");
return false;
}
}
struct SymEntry * FindName(struct SymTab *ATable, const char *Name)
{
struct SymEntry *Entry;
unsigned hashvalue;
hashvalue = hash(ATable, Name);
Entry = ATable->Contents[hashvalue];
while(Entry)
{
if(strcmp(Name,Entry->Name)==0)
{
return Entry;
}
}
return NULL;
}
main(int argc, char **argv)
{
struct SymTab *mysymtab;
struct SymEntry *myEntry;
mysymtab = CreateSymTab(1);
const char *string1 = "HELLO";
printf("%d\n",6);
EnterName(mysymtab, string1, &myEntry);
printf("first: %s\n", mysymtab->Contents[0]->Name);
EnterName(mysymtab, string1, NULL);
EnterName(mysymtab, "WORLD", NULL);
printf("second: %s\n", mysymtab->Contents[0]->Name);
printf("second->Next: %s\n", mysymtab->Contents[0]->Next->Name);
EnterName(mysymtab, "!##$%", &myEntry);
printf("third: %s\n", mysymtab->Contents[0]->Name);
printf("third->Next: %s\n", mysymtab->Contents[0]->Next->Name);
printf("third->Next->Next: %s\n", mysymtab->Contents[0]->Next->Next->Name);
printf("myEntry->Name: %s\n", myEntry->Name);
}
The problem is this line in EnterName:
AnEntry=(struct SymEntry**)malloc(sizeof(struct SymEntry*));
You need to remove that as you want AnEntry to point to the argument that the caller specified.
Because AnEntry may be NULL, you will also need to change every instance of:
*AnEntry = ptr;
to:
if (AnEntry)
*AnEntry = ptr;
What is happening is that when the function starts, AnEntry is pointing to the pointer the caller wants to change. When you change the value of AnEntry (i.e AnEntry = ...;), your code will not modify the pointer the caller want you to change but some internal pointer. Therefore, when EnterName returns, myEntry is still pointing to some random place in memory.
While you're at learning, there are some stylistic WTFs in your code. Take this part, for example.
if(!(ptr=malloc(sizeof(struct SymEntry)))) return false;
if(!(ptr->Name=string))
{
printf("if(!(ptr->Name=string))\n");
free(ptr);
return false;
}
ptr->Name = string;
It's inconsistent. You cast the return of malloc for AnEntry above, but not this malloc. Either do one or the other, but don't mix it. Better yet, write it in a way that doesn't "need" a cast at all.
You shouldn't assign values within if-statements. While it is still clear what you want to do in the malloc-case, the intention is obfuscated in the string assignment. Especially since it is superfluous. When the if evaluates to true, ptr is immediately freed. When it evaluates to false, the exact same assignment is done again. Additionally, in this case it prevents an obvious optimization.
Here is the same code rewritten:
if (string == NULL)
{
printf("string == NULL\n");
return false;
}
ptr = malloc(sizeof *ptr);
if (ptr == NULL)
{
return false;
}
ptr->Name = string;

Resources