Creating a argv[] to send args to another function - c

I know there is a couple of entries on this and I looked through them and couldn't quite get to where I wanted to be.
I'm building function where it can read the contents of a directory and pass the regular files to another function to build an archive.
I'm trying make an argv[] like item called items, that I can pass to my function, the issue is that I need to populate it with the file names. Rather that statically declaring it (which I saw in a lot of examples) I want to use malloc based on the file count.
Here is the function header for quick:
void quick(int argc, char *argv[])
Here is the append function:
void append(char* argv[]){
struct dirent *dp;
int count, i = 3;
char *files;
char items [*files][255];
DIR *dirp = opendir(".");
if(dirp == NULL){
fail('f');
}
printf("ar: adding files in current directory to archive: %s", argv[2]);
while((dp = readdir(dirp)) != NULL){
if(dp->d_type == DT_REG){
count++;
}
}
rewinddir(dirp);
files = malloc(count*sizeof(char));
//copy argv[2] archive name, into files, so we can pass to quick
strcpy(items[2], argv[2]);
while((dp = readdir(dirp)) != NULL){
errno = 0;
dp = readdir(dirp);
//leave is dir is empty
if(dp == NULL)
break;
// Skip . and ..
if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
continue;
//if file is not the archive and a regular file add to list
if(strcmp(dp->d_name, argv[2]) != 0 && dp->d_type == DT_REG){
strcpy(items[i], dp->d_name);
i++;
}
}
closedir(dirp);
quick(i, items);
}
I'm getting an error on the items arg, as incompatible pointer type, and I'm guessing that I didn't do my malloc (and possibly my array) correctly because I have still not mastered the mysteries of those.

Your declaration of char items [*files][255] is incorrect.
Basically, you want files to be an array of arrays; hence it needs to be a pointer-to-pointer (i.e. char**) type. Then you'll need to allocate each array in that array to hold the actual strings, like so:
char** files;
files = malloc(count * sizeof(char*)); // allocate the array to hold the pointer
for (size_t i = 0; i < count; i += 1)
files[i] = malloc(255 * sizeof(char)); // allocate each array to hold the strings
Free the memory appropriately when you're done with it:
for (size_t i = 0; i < count; i += 1)
free(files[i]);
free(files);

Here's how you can grow an "array" of "strings":
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int AddString(char*** strings, size_t* count, const char* newStr)
{
char* copy;
char** p;
if (strings == NULL ||
newStr == NULL ||
(copy = malloc(strlen(newStr) + 1)) == NULL)
return 0;
strcpy(copy, newStr);
if ((p = realloc(*strings, (*count + 1) * sizeof(char*))) == NULL)
{
free(copy);
return 0;
}
*strings = p;
(*strings)[(*count)++] = copy;
return 1;
}
void PrintStrings(char** strings, size_t count)
{
printf("BEGIN\n");
if (strings != NULL)
while (count--)
printf(" %s\n", *strings++);
printf("END\n");
}
int main(void)
{
char** strings = NULL;
size_t count = 0;
PrintStrings(strings, count);
AddString(&strings, &count, "Hello World!");
PrintStrings(strings, count);
AddString(&strings, &count, "123");
AddString(&strings, &count, "ABCDEF");
PrintStrings(strings, count);
return 0;
}
Output (ideone):
BEGIN
END
BEGIN
Hello World!
END
BEGIN
Hello World!
123
ABCDEF
END

Look here:
char *files;
char items [*files][255];
You'd better use malloc after files would be initialized.
As I understand, this should be for example
char items [count][255];
int C99 style.
BTW, count used uninitialized. Set it to zero at beginning.

With the length of the columns statically know, you could also allocate
char (*items )[255];
// obtain row count
items = malloc(rowCount * sizeof(*items));
memory to a pointer to char[255]. That way, you get a contiguous block of memory, which may give better locality, and you need only free one pointer.
If the number of rows is not too large, using a variable length array,
rowCount = whatever;
char items[rowCount][255];
may however be the best option if C99 or later is supported.

Related

Why does my string_split implementation not work?

My str_split function returns (or at least I think it does) a char** - so a list of strings essentially. It takes a string parameter, a char delimiter to split the string on, and a pointer to an int to place the number of strings detected.
The way I did it, which may be highly inefficient, is to make a buffer of x length (x = length of string), then copy element of string until we reach delimiter, or '\0' character. Then it copies the buffer to the char**, which is what we are returning (and has been malloced earlier, and can be freed from main()), then clears the buffer and repeats.
Although the algorithm may be iffy, the logic is definitely sound as my debug code (the _D) shows it's being copied correctly. The part I'm stuck on is when I make a char** in main, set it equal to my function. It doesn't return null, crash the program, or throw any errors, but it doesn't quite seem to work either. I'm assuming this is what is meant be the term Undefined Behavior.
Anyhow, after a lot of thinking (I'm new to all this) I tried something else, which you will see in the code, currently commented out. When I use malloc to copy the buffer to a new string, and pass that copy to aforementioned char**, it seems to work perfectly. HOWEVER, this creates an obvious memory leak as I can't free it later... so I'm lost.
When I did some research I found this post, which follows the idea of my code almost exactly and works, meaning there isn't an inherent problem with the format (return value, parameters, etc) of my str_split function. YET his only has 1 malloc, for the char**, and works just fine.
Below is my code. I've been trying to figure this out and it's scrambling my brain, so I'd really appreciate help!! Sorry in advance for the 'i', 'b', 'c' it's a bit convoluted I know.
Edit: should mention that with the following code,
ret[c] = buffer;
printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
it does indeed print correctly. It's only when I call the function from main that it gets weird. I'm guessing it's because it's out of scope ?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEBUG
#ifdef DEBUG
#define _D if (1)
#else
#define _D if (0)
#endif
char **str_split(char[], char, int*);
int count_char(char[], char);
int main(void) {
int num_strings = 0;
char **result = str_split("Helo_World_poopy_pants", '_', &num_strings);
if (result == NULL) {
printf("result is NULL\n");
return 0;
}
if (num_strings > 0) {
for (int i = 0; i < num_strings; i++) {
printf("\"%s\" \n", result[i]);
}
}
free(result);
return 0;
}
char **str_split(char string[], char delim, int *num_strings) {
int num_delim = count_char(string, delim);
*num_strings = num_delim + 1;
if (*num_strings < 2) {
return NULL;
}
//return value
char **ret = malloc((*num_strings) * sizeof(char*));
if (ret == NULL) {
_D printf("ret is null.\n");
return NULL;
}
int slen = strlen(string);
char buffer[slen];
/* b is the buffer index, c is the index for **ret */
int b = 0, c = 0;
for (int i = 0; i < slen + 1; i++) {
char cur = string[i];
if (cur == delim || cur == '\0') {
_D printf("Copying content of buffer to ret[%i]\n", c);
//char *tmp = malloc(sizeof(char) * slen + 1);
//strcpy(tmp, buffer);
//ret[c] = tmp;
ret[c] = buffer;
_D printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
//free(tmp);
c++;
b = 0;
continue;
}
//otherwise
_D printf("{%i} Copying char[%c] to index [%i] of buffer\n", c, cur, b);
buffer[b] = cur;
buffer[b+1] = '\0'; /* extend the null char */
b++;
_D printf("Buffer is now equal to: \"%s\"\n", buffer);
}
return ret;
}
int count_char(char base[], char c) {
int count = 0;
int i = 0;
while (base[i] != '\0') {
if (base[i++] == c) {
count++;
}
}
_D printf("Found %i occurence(s) of '%c'\n", count, c);
return count;
}
You are storing pointers to a buffer that exists on the stack. Using those pointers after returning from the function results in undefined behavior.
To get around this requires one of the following:
Allow the function to modify the input string (i.e. replace delimiters with null-terminator characters) and return pointers into it. The caller must be aware that this can happen. Note that supplying a string literal as you are doing here is illegal in C, so you would instead need to do:
char my_string[] = "Helo_World_poopy_pants";
char **result = str_split(my_string, '_', &num_strings);
In this case, the function should also make it clear that a string literal is not acceptable input, and define its first parameter as const char* string (instead of char string[]).
Allow the function to make a copy of the string and then modify the copy. You have expressed concerns about leaking this memory, but that concern is mostly to do with your program's design rather than a necessity.
It's perfectly valid to duplicate each string individually and then clean them all up later. The main issue is that it's inconvenient, and also slightly pointless.
Let's address the second point. You have several options, but if you insist that the result be easily cleaned-up with a call to free, then try this strategy:
When you allocate the pointer array, also make it large enough to hold a copy of the string:
// Allocate storage for `num_strings` pointers, plus a copy of the original string,
// then copy the string into memory immediately following the pointer storage.
char **ret = malloc((*num_strings) * sizeof(char*) + strlen(string) + 1);
char *buffer = (char*)&ret[*num_strings];
strcpy(buffer, string);
Now, do all your string operations on buffer. For example:
// Extract all delimited substrings. Here, buffer will always point at the
// current substring, and p will search for the delimiter. Once found,
// the substring is terminated, its pointer appended to the substring array,
// and then buffer is pointed at the next substring, if any.
int c = 0;
for(char *p = buffer; *buffer; ++p)
{
if (*p == delim || !*p) {
char *next = p;
if (*p) {
*p = '\0';
++next;
}
ret[c++] = buffer;
buffer = next;
}
}
When you need to clean up, it's just a single call to free, because everything was stored together.
The string pointers you store into the res with ret[c] = buffer; array point to an automatic array that goes out of scope when the function returns. The code subsequently has undefined behavior. You should allocate these strings with strdup().
Note also that it might not be appropriate to return NULL when the string does not contain a separator. Why not return an array with a single string?
Here is a simpler implementation:
#include <stdlib.h>
char **str_split(const char *string, char delim, int *num_strings) {
int i, n, from, to;
char **res;
for (n = 1, i = 0; string[i]; i++)
n += (string[i] == delim);
*num_strings = 0;
res = malloc(sizeof(*res) * n);
if (res == NULL)
return NULL;
for (i = from = to = 0;; from = to + 1) {
for (to = from; string[to] != delim && string[to] != '\0'; to++)
continue;
res[i] = malloc(to - from + 1);
if (res[i] == NULL) {
/* allocation failure: free memory allocated so far */
while (i > 0)
free(res[--i]);
free(res);
return NULL;
}
memcpy(res[i], string + from, to - from);
res[i][to - from] = '\0';
i++;
if (string[to] == '\0')
break;
}
*num_strings = n;
return res;
}

How to return an array of character pointers from a function back to main?

I'm trying to store a list of files with a .txt extension into an array from my current working directory. I've gotten as far as getting the array together, now I just need to return it. I'm new to C and I'm not used to the pointer situation. How can I return an array of char pointers? I have the array created, and its allocated based on how many Text files I find in the directory.
I'm getting two errors when I try to compile which leads me to believe that my understanding of pointers is not what I thought it was. I also have been reading that my array will be destroyed when returning from the function, because it is on the stack. I'm not sure how to fix that. Any help or criticism would be welcome.
// prototypes
char* getLine();
void replaceWord();
char * getTxtFilesInDir(char*);
bool isTxt(char *);
int main(int argc, char **argv) {
// Check to see if correct number of arguments is given
if (argc < 4) {
printf("ERROR: Not enough parameters supplied. Please supply a find,"
"replace, and prefix parameter and try again.\n");
return -1;
}
// Initialize variables
char *find, *replace, *prefix;
find=argv[1];
replace=argv[2];
prefix=argv[3];
char cd[1024];
// Get the Current working directory to grab files
if (getcwd(cd, sizeof(cd)) != NULL) {
printf("Current working dir is: %s\n", cd);
} else {
perror("getcwd() error");
}
// Get the values of the arguments
printf("Value of find: %s\nValue of replace: %s\nValue of prefix: %s\n",
find, replace, prefix);
// create an array of the files that are valid
char* files = getTxtFilesInDir(cd);
return 0;
}
char* getTxtFilesInDir(char* directory) {
DIR *d;
struct dirent *dir;
d = opendir(directory);
int n=0, i=0;
// get the number of text files in the directory
if (d) {
while((dir = readdir(d)) != NULL) {
if (isTxt(dir->d_name)) {
n++;
}
}
}
rewinddir(d);
// Create the array of text files depending on the size
static char* txtFiles[n];
// add the text files to the array
if (d) {
printf("Found: \n");
while ((dir = readdir(d)) != NULL)
{
if (isTxt(dir->d_name)) {
printf("%s\n", dir->d_name);
txtFiles[i]= (char*) malloc (strlen(dir->d_name)+1);
strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));
i++;
}
}
closedir(d);
}
return txtFiles;
}
bool isTxt(char *file) {
size_t len = strlen(file);
return len > 4 && strcmp(file + len -4, ".txt") == 0;
}
If you want that getTxtFilesInDir returns an array of strings, it should
return a char**, not a char*.
Also you don't need to declare the variable as static. You declare a variable
in a function as static, when you want that the variable remains the same for
all calls of the function. In this case this is probably not what you want to
do.
Without modifying too much of your initial code, you can first allocate memory
for an arrray of strings, and then resize it when you've got a new entry. In
this example I do that at once, because realloc(NULL, somesize) is the same as
doing malloc(somesize). That's why it's important to initialize *tmp = 0 and
txtFiles = NULL, so this trick works. You should also pass a pointer to an size_t where you store the number of entries:
char **getTxtFilesInDir(const char* directory, size_t *len) {
if(directory == NULL || len == NULL)
return NULL;
...
*len = 0;
char **txtFiles = NULL, **tmp;
char *str;
if (d) {
printf("Found: \n");
while ((dir = readdir(d)) != NULL)
{
if (isTxt(dir->d_name))
{
tmp = realloc(txtFiles, (len+1) * sizeof *textFiles);
if(tmp == NULL)
return txtFiles; // return what you've got so far
str = malloc(strlen(dir->d_name) + 1);
if(str == NULL)
{
if(txtFiles == NULL) // first time, free everything
{
free(tmp);
return NULL;
}
return tmp; // return all you've got so far
}
strcpy(str, dir->d_name); // no need of strcnpy, you've allocated
// enough memory
txtFiles = tmp;
txtFiles[(*len)++] = tmp;
}
}
closedir(d);
return txtFiles;
}
The important bits here is 1. how you expand the memory with realloc. Then it
allocates memory for the string using malloc. I do that before txtFiles = tmp;,
so that I don't have to write to many if(...==NULL). If something goes
wrong along the way, the function returns all the file names it already has
stored.
Now in main you do:
int main(int argc, char **argv)
{
...
size_t len = 0;
char **files = getTxtFilesInDir(cd, &len);
if(file == NULL)
{
fprintf(stderr, "No files found\n");
return 1;
}
for(size_t i = 0; i < len; ++i)
printf("file: %s\n", files[i]);
// freeing memory
for(size_t i = 0; i < len; ++i)
free(files[i]);
free(files);
return 0;
}
I also want to comment on your orginal way of copying:
strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));
strncpy is great when you want to limit the number of bytes to be copied. In
your case you've already allocated enough memory, so there is no need to. And
when you use strncpy, the limit should be bound to the number of bytes
available to the destination, not the source, otherwise you might copy more
bytes than it should. In your case you won't get a valid c-string, because you
are limiting to copy to the lenth of dir->d_name, so the '\0'-terminating
won't be copied.
man strcpy
#include <string.h>
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
[...]
The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n
bytes of src, the string placed in dest will not be null-terminated.
When you use strncpy you must make sure that the copy is a valid string by
setting the '\0'-terminating byte yourself. Because you've allocated
strlen(dir->d_name)+1 bytes for the string:
strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));
textFiles[i][strlen(dir->d_name)] = 0;
Also the exit value of a program is a unsigned value, it goes from 0 to 255.
In main you should return 1 instead of -1 on error.

Buffer to array (segmentation fault)

I'm trying to open a file, read the content line by line (excluding the empty lines) and store all these lines in an array, but seems I cannot come to the solution.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char buffer[500];
FILE *fp;
int lineno = 0;
int n;
char topics[lineno];
if ((fp = fopen("abc.txt","r")) == NULL){
printf("Could not open abc.txt\n");
return(1);
}
while (!feof(fp))
{
// read in the line and make sure it was successful
if (fgets(buffer,500,fp) != NULL){
if(buffer[0] == '\n'){
}
else{
strncpy(topics[lineno],buffer, 50);
printf("%d: %s",lineno, topics[lineno]);
lineno++;
printf("%d: %s",lineno, buffer);
}
}
}
return(0);
}
Considering "abc.txt" contains four lines (the third one is empty) like the following:
ab
2
4
I have been trying several ways but all I'm getting now is segmentation fault.
It is mostly because you are trying to store the read line in a 0 length array
int lineno = 0;
int n;
char topics[lineno]; //lineno is 0 here
There are more mistakes in your program after you correct the above mentioned one.
strncpy() needs a char* as its first parameter, and you are passing it a char.
If you want to store all the lines, in a manner such that array[0] is the first line, array[1] is the next one, then you would need an `array of char pointers.
Something like this
char* topics[100];
.
.
.
if (fgets(buffer,500,fp) != NULL){
if(buffer[0] == '\n'){
}
else{
topics[lineno] = malloc(128);
strncpy(topics[lineno],buffer, 50);
printf("%d: %s",lineno, topics[lineno]);
lineno++;
printf("%d: %s",lineno, buffer);
}
NOTE:
Use the standard definition of main()
int main(void) //if no command line arguments.
Bonus
Since you have accidentally stepped onto 0 length array, do read about it here.
This declaration of a variable length array
int lineno = 0;
char topics[lineno];
is invalid because the size of the array may not be equal to 0 and does not make sense in the context of the program/
You could dynamically allocate an array of pojnters to char that is of type char * and reallocate it each time when a new record is added.
For example
int lineno = 0;
int n;
char **topics = NULL;
//...
char **tmp = realloc( topics, ( lineno + 1 ) * sizeof( char * ) );
if ( tmp != NULL )
{
topics = tmp;
topics[lineno] = malloc( 50 * sizeof( char ) );
//... copy the string and so on
++lineno;
}

Why is my array of pointers getting overwritten after dynamic allocation?

I'm working on a little C program for a class that reads the lines in from a file and then sorts them using qsort. Long story short, I am dynamically allocating memory for every line of a file, stored as a char*, in an array of char*. The reading in and storing ostensibly works fine based upon the output (see below), but when I print out the lines, they are all duplicates of the last line in the file. Can anyone point out my (most likely painfully obvious) error?
Here is the relevant code to the problem I'm currently running into:
char* trim_white_space(char* str);
char* get_line(FILE* infile, char temp[]);
int main(int argc, char* argv[]) {
FILE* infile;
char* input_file = argv[1];
int cnt = 0;
char temp[MAX_LINE_LENGTH]; //to hold each line as it gets read in
char* tempPointer = temp;
if (argc < 2) {
printf("No input file provided");
return EXIT_FAILURE;
}
//determine the number of lines in the file
infile = fopen(input_file, "r");
int num_lines_in_file = num_lines(infile);
fclose(infile);
//allocate pointers for each line
char** lines = (char**) malloc(num_lines_in_file * sizeof(char*));
//temporarily store each line, and then dynamically allocate exact memory for them
infile = fopen(input_file, "r");
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
tempPointer = get_line(infile, temp);
lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
lines[cnt] = trim_white_space(tempPointer);
printf("%d: %s\n", cnt, lines[cnt]);
}
fclose(infile);
//print the unsorted lines (for debugging purposes)
printf("Unsorted list:\n");
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
printf("%s\n", lines[cnt]);
}
char* get_line(FILE* infile, char temp[]) {
fgets(temp, MAX_LINE_LENGTH-1, infile);
char* pntr = temp;
return pntr;
}
char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace(*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace(*end)) end--;
// Write new null terminator
*(end+1) = 0;
return str;
}
I have this sample input file 5-1input.dat:
Hi guys
x2 My name is
Slim Shady
For real
And here's the output I'm getting:
user#user-VirtualBox ~/Desktop/Low-level/HW5 $ ./homework5-1 5-1input.dat
0: Hi guys
1: x2 My name is
2: Slim Shady
3: For real
Unsorted list:
For real
For real
For real
For real
As in the comments, you should change your loop to:
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
tempPointer = get_line(infile, temp);
lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
strncpy(lines[cnt], trim_white_space(tempPointer), strlen(tempPointer)+1);
printf("%d: %s\n", cnt, lines[cnt]);
}
The size in strncpy is based on the size of malloc you've used.
Of course you can optimize this code, e.g. to count strlen only once, etc.

Using 2D array of char pointer in C

I want to read a file and write each line to array of char. As I don't know the amount of lines, therefore I thought the most efficient way is to use 2D array of char pointer. However I get segmentation fault.
My question might be duplicate of this one :
2D array of char pointers --> Segmentation fault?
But I couldn't figure the correct syntax for C so I couldn't try.
Here's my code:
FILE *file = fopen ( filename, "r" );
if ( file != NULL )
{
char line [ 128 ]; /* or other suitable maximum line size */
char **new_line;
int i = 0;
while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */
{
strcpy(new_line[i], line);
i++;
}
Memory is not allocated for new_line which causes the segmentation fault.
If you know the no of lines, then you can declare that as local array itself. In that case your accessing method will works fine.
#define MAX_LINES 20
#define MAX_CHARS 128
...
char new_line[MAX_LINES][MAX_CHARS] = {0};
...
Your problem here is you dont know the maximum number of lines. So you have selected double pointer. In that case you need to first malloc with some n number of lines and then you need to keep on using realloc to increase the buffer size.
#define MAX_CHARS 128
#define N_NO_OF_LINES 10
...
char line[MAX_CHARS] = {0};
char **new_line = NULL;
int noOfLines = 0;
int lineCount = 0;
new_line = malloc(sizeof(char*) * N_NO_OF_LINES);
noOfLines = N_NO_OF_LINES;
while (fgets (line, sizeof line, file) != NULL) /* read a line */
{
if (lineCount >= noOfLines)
{
new_line = realloc(new_line, (sizeof(char*)*(noOfLines+N_NO_OF_LINES)));
noOfLines += N_NO_OF_LINES;
}
new_line[lineCount] = strdup(line);
lineCount++;
}
Note : Take care of null check for malloc and realloc
new_line is not initialized to a valid chunk of memory.
Roughly:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
FILE *file = fopen ( "test.txt", "r" );
if ( file != NULL )
{
#define MAXLINES 128
#define MAXLINELEN 100
char line [ MAXLINELEN ]; /* or other suitable maximum line size */
char **new_line = (char **)malloc(sizeof(char *) * MAXLINES);
int i = 0;
if (!new_line) exit(-1);
while ( i < MAXLINES && (fgets ( line, sizeof line, file ) != NULL )) /* read a line */
{
new_line[i] = strdup(line);
i++;
}
printf("read %d lines\n", i);
}
exit(0);
}
You didn't allocate any memory for the new_line array. You need something like:
char **new_line = malloc(sizeof(char *) * MAX_LINES);
And then for each line, don't use strcpy, which will copy into a garbage pointer (the uninitialized new_line array). You probably want strdup(3):
new_line[i] = strdup(line);
You declare new_line as a pointer to char *, but it is never initialized so it points to some invalid memory address. You get the error when you write to that address.
You will probably want to allocate the memory, assign it to new_line, and then you can copy strings to it.
You need to alloc space for your strings.
Malloc returns a memory slot with the size you want but doesn't allow memory reallocation. For that you have realloc.
With malloc you would end up with a fixed size for your table, just like if you had only declared it has static but with later initialization (Well, I'm a bit agains this sentence because malloc is much more than that, but for this purpose it is safe to say it).
Realloc does that, reallocates memory, but it can be pretty dangerous if you don't use i correctly. And, in my opinion, is not the most correct way to do it.
When you want to save something that you don't know the size, dynamic structures are the way to go.
You can use 'linked lists like' data structures so you can have as many words as you want and then convert that list to an array.
I would go with something like this:
typedef struct _words{ //Structure to the dynamic insertion of words
char *word;
struct _words *next;
}words;
words *last; //Easier implementation for this case. Not always the best solution
words *init(){ //Is good practice to start with an empty structure for reference passing
words *new = malloc(sizeof(words));
if(new == NULL) exit(0);
new->next = NULL; //A good end/next reference
return new;
}
void insertWord(char *word){
words *new = malloc (sizeof(words));
if(new == NULL) exit(0);
new->word = malloc(strlen(word)*sizeof(char));
if(new->word == NULL) exit(0);
new->next = NULL; //Or new->next = last->next; wich is also null.
last->next = new;
last = new;
}
int main(){ //Or the name of your function
FILE *file = fopen ( filename, "r" );
words *list = init();
last = list;
if ( file != NULL )
{
char line [ 128 ]; /* or other suitable maximum line size */
int i = 0;
while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */
{
insertWord(line);
i++;
}
//Here, you already have all the words in your dynamic structure. You can now save them into an array
char **array = malloc(i*sizeof(char*)); //i is the number of words.
if(array == NULL) exit(0);
word *aux = list->next;
if(aux != NULL){
for(int j=0;j<i && aux != NULL;j++){
array[j] = malloc(strlen(aux->word)*sizeof(char));
if(array[j] == NULL) exit(0);
strcpy(array[j], aux->word);
aux = aux->next; // jump to the next word
}
}
...
}
I think this might work but I didn't try it. Is just to give you an idea on how to implement dynamic structures.
It misses frees and is not an actual stack, even if is close.
Hope this helps.

Resources