2

Somebody kindly helped me a couple days ago with a large part of this in another SO question. At that time, the size of struct base was known in advance. I'd like to alter member path to be a character array the size of which isn't known until right before allocating memory for a new instance of base in the base_new() function.

In the previous version, all files were required to be stored in the same directory and only the file name was added; and it was limited to length 256. Now I'd like to permit user added subdirectories under the ../../databases directory and not limit the length.

Is it possible to set the size of path before or after the db[i] = malloc( sizeof ( struct base ) ) in base_new()?

Or, perhaps, I should simply ask, how can this be accomplished?

Thank you.

/* Global declaration */
struct base {
 ...
 char path[];
};

struct base **db;

/* in main() */
db = malloc( n * sizeof *db );
for (size_t i = 0; i < n; ++i)
  db[i] = NULL;

/* Function to assign pointer db[i] to newly allocated struct base */
int base_new( void )
{
  /* declarations */
  // Assign pointer to beginning of memory allocation for the new instance of struct base.
  if ( ( db[i] = malloc( sizeof ( struct base ) ) ) == NULL )
    {
      printf( "Error attempting to malloc space for new base.\n" );
      return 1;
    }

  // When size of path was fixed, just truncated name to 256. */
  l = sizeof( db[i]->path );
  rc = snprintf( db[i]->path, l, "%s%.*s", "../../databases/", 256, name );
  if ( rc > l - 1 || rc < 0 )
    {
      // Not enough space; truncate and add the '\0' at l.
      db[i]->path[l] = '\0';
    }  

  // When size of path variable and writing path. */
  l = sizeof( db[i]->path ) - 16;
  rc = snprintf( db[i]->path, l, "%s%s", "../../databases/", path );
  if ( rc > l - 1 || rc < 0 )
    {
      
      db[i]->path[l] = '\0';
    }  

}

I got a message at the top of my question asking if an existing question answers this one. It is closely related and helpful, but the answer I received here is better I think and discusses a few other related points. I don't know how it is supposed to work but I picked No because this answer is better, or at least I can understand it better. This answer shows how to malloc the variable member of the struct and discusses freeing the memory of the struct by member before freeing the pointer to the struct. The other question is a bit more general but still helpful. Thanks.

Gary
  • 2,393
  • 12
  • 31
  • 1
    Does this answer your question? [Array of variable length in struct](https://stackoverflow.com/questions/26183751/array-of-variable-length-in-struct) – kaylum Dec 29 '20 at 04:03
  • 1
    You can do a single allocation using a *flexible array member* as described in the above duplicate post or alternatively you can change the `path` to be `char *` and allocate the `path` with a second `malloc` call. – kaylum Dec 29 '20 at 04:05
  • @kaylum Yes that is helpful. Thank you. I'm a bit confused about what the struct is since all its members are now pointers. It doesn't take up much data at all itself and all its members can be allocated anywhere available. Its an organized collection of pointers pointing to disorganized blocks of memory. – Gary Dec 29 '20 at 04:19

1 Answers1

1

Instead of an array, you can have a pointer inside your struct:

struct base {
    ...
    char *path;
};

Later, allocate memory to this pointer whenever you need it:

base.path = malloc(n * sizeof(char)); // n is the variable size you will set before

Since you allocate memory dynamically now, don’t forget to free to avoid any memory leaks. In C, there is the requirement of every struct having a fixed byte length, so that, for example, sizeof(struct base) can be evaluated at compile time. In your case, the variable length array's size cannot be determined at compile time, so it is illegal do something like char path[l] where l is unknown at compile time.

Btw, a correction regarding

l = sizeof( db[i]->path );

First of all, even if path was declared as an array, this won't give you the size of it in terms of its length, it would return you the complete byte size occupied by the array, you gotta divide it by sizeof (char) to get the length. Now that you have declared it as a pointer however, I guess you don't really need to do this.

Jarvis
  • 8,494
  • 3
  • 27
  • 58
  • Thank you. Will `free( db[i] )` free the separate `malloc` for `base.path` also, or do both `malloc` statements need to be freed separately? – Gary Dec 29 '20 at 04:12
  • You should `free` the members first, like `free(db[i]->path)` and then the individual structs. – Jarvis Dec 29 '20 at 04:14
  • That last part about determining the length of an array makes sense but I don't understand something. In the previous version `path` was declared as `char[285]` and I messed around with it using `100` to see if it would truncate the names and it did where the 100 was 100 characters. Why would that work then? – Gary Dec 29 '20 at 04:27
  • I used it in `snprintf` because I was misusing it terribly and @dxiv helped me at the close of the answer [here](https://stackoverflow.com/questions/64939755/novice-c-question-concerning-building-strings-using-pointers-in-sprintf-and-snpr). – Gary Dec 29 '20 at 04:31
  • I'm using minGW-W64 and sizeof(char) returns 1. Is that not typical or differ by compiler? Thanks. – Gary Dec 29 '20 at 05:00
  • size of(char) is 1 byte generally in C++ and 4 bytes in C if it is a char literal, otherwise it is 1 byte if declared explicitly as char. – Jarvis Dec 29 '20 at 05:08
  • Thanks. I'll divide by sizeof(char) to be safe to cover all cases. – Gary Dec 29 '20 at 05:17