Array of char* confusion (array of strings)

I have a list of categories seperated by newlines in a file cats.txt

I'm trying to write code that takes each category name and adds it to an array (of category strings). You can see from the output that it isn't doing what it should be.

Here is the file cats.txt
Code:
apples
oranges
bananas

And here is main.c
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    FILE * fp = fopen("cats.txt", "r");
    
    char * line = malloc(80);
    
    char * *category_list = malloc(100); // 100 chosen arbitrarily...
    
    int i = 0;
    
    while (fgets(line, MAX_STRING_LENGTH, fp) != 0) {
        
        /* Remove trailing '\n' from line */
        *(line + strlen(line) - 1) = '\0';
        
        /* Add string to array */
        *(category_list + i) = line;
        
        i++;
        
    }
    
    printf("first line: %s\n", *(category_list + 0));
    printf("second line: %s\n", *(category_list + 1));
    printf("third line: %s\n", *(category_list + 2));
    
    fclose(fp);

    return 0;
}

And when I run it...
Code:
> ./main
first line: bananas
second line: bananas
third line: bananas

Can anyone explain what is going on?
 
You get bananas all over the place because you assign same pointer to every array member, pointer's memory being overwritten with each line. strdup() will malloc a duplicate string, however you will need to free them separately.

Code:
...
*(category_list + i) = strdup(line);
...
 
I have another questions about allocating memory to category_list.

At the moment I allocate 100 because it's big an just works.
Code:
char * *category_list = malloc(100); // 100 chosen arbitrarily...

But what should this number be really? Should it be the number of lines in cats.txt?

Cheers.
 
caesius said:
I have another questions about allocating memory to category_list.

At the moment I allocate 100 because it's big an just works.
Code:
char * *category_list = malloc(100); // 100 chosen arbitrarily...

But what should this number be really? Should it be the number of lines in cats.txt?

Cheers.

should be

Code:
char * *category_list = malloc(sizeof(char*) * lines_in_file);
since pointer would be 2 to 8 bytes each depending on platform.
 
Hmm now I have a new problem. I am actually trying to write a function that takes as an argument a char ** and fills this with the strings from the file. Something like this:

Code:
/**
    @param cats Assume this has been allocated enough memory */
void
expense_cats_get(char ** cats, const char * filename)
{
     FILE * fp = fopen(filename, "r");
    
    char * line = malloc(80);
    
    int lines_in_file = get_num_filelines();

    char * *category_list = malloc(sizeof(char*) * (lines_in_file + 1));
    
    int i = 0;
    
    while (fgets(line, MAX_STRING_LENGTH, fp) != 0) {
   
        // Remove trailing '\n' from line
        *(line + strlen(line) - 1) = '\0';
        
        char * category = malloc(80);
        strcpy(category, line);
        
        *(category_list + i) = category;
        
        i++;
        
    }
    
    *(category_list + i) = NULL;
    
    fclose(fp);
    
    cats = category_list;
    printf("DEBUG addrees of cats: %d\n", cats);
    printf("DEBUG addrees of category_list: %d\n", category_list);
}

And the function that calls it:
Code:
void Money::testSlot()
{
    /* I'll worry about allocating the correct amount later.. */
    char ** categories = (char **)malloc(100);
    
    expense_cats_get(categories, "categories.txt");
    
    printf("DEBUG: address (in calling function) = %d\n", categories);
    
}

Here's the output:
Code:
> ./money
DEBUG addrees of cats: 694194720
DEBUG addrees of category_list: 694194720
DEBUG: address (in calling function) = 694758400
Segmentation fault (core dumped)

Why oh why oh why are the two memory addresses different?
 
You really need to understand how functions work, all arguments are pushed to the function's stack. So basically if you pass something to the function it will duplicate it in its stack. If you modify it, it will not modify the memory caller was using. What happened you passed a pointer, ok you can put something to the memory it is pointing to, but if you modify the pointer itself you will just change your duplicate and original pointer will stay the same. So to go around this you either need to return new pointer or pass pointer of a pointer as an argument.
 
expl said:
You really need to understand how functions work, all arguments are pushed to the function's stack. So basically if you pass something to the function it will duplicate it in its stack. If you modify it, it will not modify the memory caller was using. What happened you passed a pointer, ok you can put something to the memory it is pointing to, but if you modify the pointer itself you will just change your duplicate and original pointer will stay the same. So to go around this you either need to return new pointer or pass pointer of a pointer as an argument.

Thanks for reply again.

> ..or pass pointer of a pointer as an argument.

But aren't I already doing this?

Code:
expense_cats_get(char ** cats, const char * filename)

cats is a "pointer of a pointer" right?

Cheers.
 
Code:
        char * category = malloc(80);
        strcpy(category, line);
Do not do that. You should do like this
Code:
        char * category = malloc(strlen(line)+1);
        strcpy(category, line);
or this
Code:
        char * category = malloc(80);
        strncpy(category, line, 80);
 
Back
Top