Search code examples
carraysstructpass-by-referenceconfiguration-files

Pass struct by reference in C, but modifications aren't all there


I am trying to make a simple program to read a config file in C. But the data in my struct members isn't staying where I put it.

Code at the bottom.

This is what I think I am doing:

  • I am creating a struct that
  • Passing the struct by reference
  • Populating the members of the struct from the config file

When I try to access the members, I can only get the data from the last row in the config file.

Thanks!


Edit: I expected it to populate the struct with key / values from the config file. Instead it only had the value of the last line in the config file.

expected output

example.com

actual output

food.txt

Problem is with:

conf->key[i]   = key;
conf->value[i] = value;

main.c

/*
* Goal is to make simple key value storage with struct and pointers
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLEN 100
#define CONFIG_MAX_BUF 1024
#define DELIM "="

struct config_s {
    char *key[MAXLEN];
    char *value[MAXLEN];
};

void read_config_file(struct config_s *conf, char *filename, int *size_of_array);

int main() {
    int i = 0, size_of_array;
    // type config_s has key and value members
    struct config_s c;

    read_config_file(&c, "test.cfg", &size_of_array);
    printf("size of array: %i\n", size_of_array);

    // if i want to print them all out, i can use this, but i'll have 
    // a function to search for a certain key and return the value.
    int l = 0, e = 1;
    printf("********** TEST 1 ***********\n");
    printf("key: [%s] value: [%s] i: [%i]\n", c.key[l], c.value[l], l);
    printf("key: [%s] value: [%s] i: [%i]\n", c.key[e], c.value[e], e);
    printf("********** TEST 2 ***********\n");

    return 0;
}

void read_config_file(struct config_s *conf, char *filename, int *size_of_array) {
    FILE *file;
    file = fopen(filename, "r");

    char line[CONFIG_MAX_BUF];

    int i = 0;

    char *cfline;
    char *key;
    char *value;

    while(fgets(line, sizeof(line), file) != NULL) {
        cfline = strstr((char *)line, DELIM);
        value = cfline + strlen(DELIM);
        value[strlen(value) - 1] = '\0';

        key = strtok((char *)line, DELIM);
        printf("%i %s %s\n", i, key, value);
        conf->key[i]   = key;
        conf->value[i] = value;
        printf("%i %s %s\n", i, (*conf).key[i], (*conf).value[i]);
        i++;
    }
    printf("%s %s\n", (*conf).key[0], (*conf).value[0]);
    fclose(file);
    *size_of_array = i;
    return;
}

test.cfg

server=example.com
port=443
file=food.txt

Solution

  • In your newest version of read_config_file, you're saving pointers to the values that point within the buffer line, which is on the stack, so these pointers will be invalid upon return.

    Even if the buffer were global, the pointers would be colliding with one another for each line because the buffer gets overwritten with each fgets call.

    Change:

    conf->key[i]   = key;
    conf->value[i] = value;
    

    Into:

    conf->key[i]   = strdup(key);
    conf->value[i] = strdup(value);