Returning a array of structs - c

I am trying to figure out how to modify my code to actually allow for me to create a array of structs in my readFile and then return the array to the main.
This is my data struct
struct data{
char *model;
float engineSize;
int cost;
char *color;
};
This is my current setup of my readFile function and then the call that I use currently for this function.
struct data * readFile(){
FILE *fp;
int c;
int count = 0;
char *line = NULL;
size_t len = 0;
fp = fopen("hw3.data", "r");
while ((c = fgetc(fp)) != EOF){
count++;
}
if (feof(fp)){
rewind(fp);
struct data *vehicles = malloc((sizeof(struct data))* count);
count = 0;
char *token = NULL;
while (getline(&line, &len, fp)!= -1){
printf("%s", line);
token = strtok(line, " ");
vehicles[count].model = token;
token = strtok(NULL, " ");
vehicles[count].engineSize = atof(token);
token = strtok(NULL, " ");
vehicles[count].cost = atoi(token);
token = strtok(NULL, " ");
vehicles[count].color = token;
}
}
}
This is the main where I have my menu and where I will do my call for my readFile function.
int main(){
int check = 1;
int input;
while (check == 1){
printf("Enter a value corresponding to a option on the menu below\n\n");
printf("1. Sort data by the float value & print high to low\n");
printf("2. Sort data by the float value & print low to high\n");
printf("3. Sort data by the int value & print high to low\n");
printf("4. Sort data by the int value & print low to high\n");
printf("5. Exit\n\n");
printf("Enter a value corresponding to the above menu\n");
scanf("%d", &input);
//readFile()
if(input == 1 || input == 2 || input == 3 || input == 4 || input == 5){
if (input == 5){
exit(0);
}if (input == 1){
//sort float high to low
}if (input == 2){
//sort float low to high
}if (input == 3){
//sort int value high to low
}if (input == 4){
//sort int value low to high
}
}else{
printf("Enter a correct value for the menus above\n\n" );
}
readFile();
}
}
Thanks

It's almost correct, the idea is OK but there are a few issues:
while ((c = fgetc(fp)) != EOF){
count++;
}
That counts the number of bytes, I think based on the later code you want the
number of lines.
while ((c = fgetc(fp)) != EOF){
if(c == '\n')
count++;
}
would give you the number of lines.
Down there
token = strtok(line, " ");
vehicles[count].model = token;
...
token = strtok(NULL, " ");
vehicles[count].color = token;
is valid, but perhaps not what you want. strtok returns on success line + some_offset so if later you need to
add more characters to vehicles[i].mode or vehicles[i].color, you might
overwrite memory. vehicles[i].color is only at an offset of
vehicles[i].model. If you even want to reallocate, realloc will fail,
because you wouldn't be reallocation at the beginning of the requested memory
block. Also by doing this you will lose the beginning of the requested memory,
it will leak memory, because you cannot free it (free(vehicles[i].color) is
not valid)1.
Another problem is that only the initial line woul have the correct amount of
allocated memory and if you call getline with a non NULL pointer and
non-zero length, getline will reallocate memory if necessary and update the
pointer and the length. If the reallocation returns the same address, then your
previous values are going to be overwritten. If the reallocation returns a
different address, you previous pointer will become invalid.
I'd suggest (and I think it is the only safe way here) that you do a copy of token with strdup (if available, or malloc+strcpy)
and after that do:
while (getline(&line, &len, fp)!= -1){
// the strtok calls
...
free(line);
line = NULL;
len = 0;
}
In this way your code won't leak memory and you would not overwrite memory.
edit
I should instead be setting the values of model and color with strcpy instead
You can use strcpy, but you would need to allocate memory first, because
model and color are just pointers. The malloc call only reserved memory,
it does not initialize it. So just doing
strcpy(vehicles[count].model, token);
would be wrong, because it you would try to copy something on an undefined
location. That's what I mean with
I'd suggest (and I think it is the only safe way here) that you do a copy of token with strdup (if available, or malloc+strcpy)
vehicles[count].model = malloc(strlen(token) + 1);
if(vehicles[count].model == NULL)
{
// error handling
// for example
// free everything and return
return NULL;
}
strcpy(vehicles[count].model, token);
The function strdup essentially does that: malloc + strcpy, so if your
system has strdup you could do it like this:
vehicles[count].model = strdup(token);
if(vehicles[count].model == NULL)
{
// error handling
// for example
// free everything and return
return NULL;
}
Another option would be to change your struct and instead of having pointers to
char, use char arrays:
struct data{
char model[100];
float engineSize;
int cost;
char color[100];
};
Now you can save strings with maximal length of 99 chars (which should be enough
for a model name and color) and just use strncpy instead, without the need of
extra memory allocation:
strncpy(vehicles[count].model, token, sizeof vehicles[count].model);
// making sure to terminate the string
vehicles[count].model[sizeof(vehicles[count].model) - 1] = 0;
Also I just haven't had a chance to change the code for free (line) line =null and len =0
I don't know what you mean by that. Just add the lines after
vehicles[count].color = token;
before the end of the while loop.
So then also I should be using the get line like I was in the second iteration through the file because I am currently over allocating?
The second loop is fine, the problem is that you are assigned the same (+
offsets) memory locations to different pointers and when getline reallocates
and gets a different address, the previous pointer becomes invalid. That's why
free(line);
line = NULL;
len = 0;
is important and you definitevly should do that.
To summerize: Your loop is fine, but you need to make these changes:
make copies of token or change the struct to use char arrays
add the
free(line);
line = NULL;
len = 0;
lines at the end of the loop and you'll be fine.
fotenotes
1vehicles[i].mode would only point at the beginning of the memory
block if and only if the line doesn't start with an empty space. As you are
reading a file, you don't have any guarantee that this is true. And even if it's
true, I wouldn't count on that. Better doing the safe thing here and make a
copy. free(vehicles[i].color) is definitely wrong.

Related

How to create dynamically an array of strings

I need to create a dynamic array of strings of same length. User first enters the string length, and then inputs as many strings as it wants. This is what I've done so far:
printf("Insert string length parameter:\n");
if (scanf("%d", &k) == 0) {
printf("Error in parameter read!");
exit(EXIT_FAILURE);
}
char buffer[k+1];
char **dictionary = malloc(sizeof(char*)); //how to allocate enough pointers to str when n° of strs is unkn?
printf("Insert string sequence (allowed chars: a-Z, - e _):\n");
scanf("%s", buffer);
while(strcmp(buffer, "+exit") != 0) {
if ((dictionary[words] = malloc(k * sizeof(char) + 1)) == NULL) {
printf("Memory allocation error!");
exit(EXIT_FAILURE);
}
strcpy(dictionary[words], buffer);
words++;
scanf("%s", buffer);
}
for (i = 0; i < words; i++)
printf("%s\n", dictionary[i]);
My idea is to create an array of pointers to each string. First user inputs the string, then I allocate memory for it and lastly I copy the string from the buffer to the allocated memory. The issue is that the memory behaves weirdly, I can for example set k to 3 and input a string 10 chars long and it would still work. However, if I enter some inputs of the correct length, the first ones get corrupted or something. For example, with k = 3, the series of inputs:
abc
def
ghi
jkl
mno
pqr
+exit
results in:
­¶F☼
☻
def
ghi
jkl
mno
pqr
Any suggestion on why this happens?
Thanks a lot to whoever helps me, it's really appreciated!
As you have correctly noted in your code There is no way to allocate enough space for the string pointers if number of strings is not previously known.
You kind of have 4 options in this scenario
Guess a max number of strings your user could input
Are there other constraints that limit how many strings your user can actually input? If so you can simply allocate the maximum amount of buffer required by doing something like
int MAX_STR = 100; // Could also be a #define somewhere
char **dictionary = (char**) malloc(MAX_STR*sizeof(char*));
This is kind of overkill and uses more memory than it needs. But in most modern systems where memory is not a tight constraint, this should be completely fine. And may even by more performant than using realloc
Make the user tell you beforehand
This is kind of an obvious one. You add one more step to the UX and make the user tell you explicitly how much space you need. Then you can allocate accordingly
int num_str;
printf("Enter number of strings: ");
scanf("%d", &num_str); // Do the due diligence of checking if it is number
char **dictionary = (char**) malloc(num_str*sizeof(char*));
Of course this solution may not always be viable. For example if the 'user' themselves don't know the number.
Reallocate on a need basis
As suggested in the comments, you can use realloc() to reallocate memory as you need
It would go something like this:
int num_str = 0;
char **dictionary = NULL; // Don't initialize yet.
while(strcmp(buffer, "+exit") != 0) {
num_str += 1;
dictionary = (char**) realloc(dictionary, num_str*sizeof(char*));
...
}
You can find more information about the realloc() function here (as mentioned in the comments)
Lucrative as this method may sound, this kind of memory reallocation can be quite resource intensive on a system as everything may have to be copied over to the new location etc.
Use a different data structure
You can use something like a linked list to store list of data whose size can grow or shrink on demand. See this for more info (or practically any other online guide)
char **dictionary = malloc(sizeof(char*)); only allocates memory for 1 pointer. Code needs a new approach to form the array of strings.
#Parth K outlines some alternatives.
Another is recursion. Use the stack to store the strings until "exit" encountered. Then allocate the array of string pointers.
// Untested illustrative code.
char** get_strings(size_t length_max, size_t count) {
char buf[length_max + 3]; // plus 3 for \n \0 and extra detection
printf("Insert string sequence (allowed chars: a-Z, - e _):\n");
if (fgets(buf, sizeof buf, stdin)) {
size_t len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n') {
buf[--len] = '\0';
}
if (len > length_max) {
fprintf(stderr, "Line <%s> too long\n", buf);
exit(EXIT_FAILURE);
}
if (strcmp(buf, "+exit")) {
char **list = get_strings(length_max, count + 1);
if (list == NULL) {
return NULL;
}
list[count] = strdup(buf);
return list;
}
}
char **list = malloc(sizeof *list * (count + 1));
if (list == NULL) {
return NULL;
}
list[count] = NULL; // Mark last entry as NULL
return list;
}
Usage
void foo() {
char buf[40];
printf("Insert string length parameter:\n");
size_t k;
if (fgets(buf, sizeof buf, stdin) && sscanf(buf, "%zu", &k) == 1) {
char **list = get_strings(k, 0);
if (list) {
for (size_t i = 0; list[i]; i++) {
printf("<%s>\n", list[i]);
free(list[i]);
}
free(list);
}
}
}
Tip do not use scanf() until you understand why it is bad.
GTG

Freeing array of char pointers - only works when I have an even number of strings...?

Having a super strange problem in C that I think is related to string memory management but I've poured through this code and can't tell what I'm doing wrong. I'm setting up an array of pointers to strings, where each string in the array is a token broken up from strtok(), which operates on a line made from getline using stdin.
I've been able to break apart the tokens, store them, and print the array with no problem. I just am having trouble freeing each individual pointer afterwards. I get an error message saying "invalid pointer", but oddly enough, it's only when I input an odd number of words in my original string. Even when I input an odd number of words in my string, it still breaks them apart into tokens and stores them in the array, its just the memory freeing that's failing.
here's a snippet of what I have:
char *line = NULL;
size_t len = 0;
getline(&line, &len, stdin);
char *token = strtok(line, " \n");
char **parsed = malloc(sizeof(char*));
if(parsed == NULL){
printf("ruh roh scoob\n");
}
int numList = 0;
while(token!=NULL){
numList++;
parsed = realloc(parsed, sizeof(char*)*numList);
if(parsed == NULL){
printf("realloc failed :(\n");
}
int tokenLen = strlen(token);
parsed[numList-1] = malloc(sizeof(char)*tokenLen);
if(parsed[numList-1] == NULL){
printf("ruh roh raggy\n");
}
strcpy(parsed[numList-1], token);
token = strtok(NULL, " \n");
}
parsed[numList] = NULL;
for(int i = 0; i <= numList; i++){
printf("%d-%s", i, parsed[i]);
}
printf("\n");
for(int j = 0; j < numList; j++){
free(parsed[j]);
}
I thought I was allocating memory correctly and storing the data from my token pointers into the new pointers correctly but perhaps I was wrong. Any advice would be appreciated!
Inside the loop you call:
strcpy(parsed[numList-1], token);
This needs at least strlen(token) + 1 bytes, but you're allocating one too few:
int tokenLen = strlen(token);
parsed[numList-1] = malloc(sizeof(char)*tokenLen);
So the strcpy writes past end of buffer, probably corrupting malloc metadata on a random basis. Note that you may get away with a write past end of buffer, because malloc rounds up the allocation amount, both to avoid memory fragmentation and to ensure that dynamically allocated buffers keep proper alignment (for better performance).

Dynamically prompt for string without knowing string size

In C, what is the best way of prompting and storing a string without wasted space if we cannot prompt for the string length. For example, normally I would do something like the following...
char fname[30];
char lname[30];
printf("Type first name:\n");
scanf("%s", fname);
printf("Type last name:\n");
scanf("%s", lname);
printf("Your name is: %s %s\n", fname, lname);
However, I'm annoyed with the fact that I have to use more space than needed so I do not want to use char fname[30], but instead dynamically allocate the size of the string. Any thoughts?
You can create a function that dynamically allocates memory for the input as the user types, using getchar() to read one character at a time.
#include <stdio.h>
#include <stdlib.h>
void* safeRealloc(void* ptr, size_t size) {
void *newPtr = realloc(ptr, size);
if (newPtr == NULL) { // if out of memory
free(ptr); // the memory block at ptr is not deallocated by realloc
}
return newPtr;
}
char* allocFromStdin(void) {
int size = 32; // initial str size to store input
char* str = malloc(size*sizeof(char));
if (str == NULL) {
return NULL; // out of memory
}
char c = '\0';
int i = 0;
do {
c = getchar();
if (c == '\r' || c == '\n') {
c = '\0'; // end str if user hits <enter>
}
if (i == size) {
size *= 2; // duplicate str size
str = safeRealloc(str, size*sizeof(char)); // and reallocate it
if (str == NULL) {
return NULL; // out of memory
}
}
str[i++] = c;
} while (c != '\0');
str = safeRealloc(str, i); // trim memory to the str content size
return str;
}
int main(void) {
puts("Type first name:\n");
char* fname = allocFromStdin();
puts("Type last name:\n");
char* lname = allocFromStdin();
printf("Your name is: %s %s\n", fname, lname);
free(fname); // free memory afterwards
free(lname); // for both pointers
return 0;
}
From man scanf:
• An optional 'm' character. This is used with string conversions (%s,
%c, %[), and relieves the caller of the need to allocate a
corresponding buffer to hold the input: instead, scanf() allocates a
buffer of sufficient size, and assigns the address of this buffer to
the corresponding pointer argument, which should be a pointer to a
char * variable (this variable does not need to be initialized before
the call). The caller should subsequently free(3) this buffer when it
is no longer required.
this however is a POSIX extension (as noted by fiddling_bits).
To be portable I think that in your usage case I would prepare a function like the following:
char *alloc_answer() {
char buf[1000];
fgets(buf,sizeof(buf),stdin);
size_t l = strlen(buf);
if (buf[l-1]=='\n') buf[l]=0; // remove possible trailing '\n'
return strdup(buf);
}
even if this solution will break lines longer than 1000 characters (but it prevents buffer overflow, at least).
A fully featured solution would need to read input in chunks and realloc the buffer on every chunk...

Free function gave error

I use malloc in a function. End of the function I call free function to avoid memory leak. But if I call free I get segmentation fault when I don't program works normally. Can anybody know why? I marked the places with comments(If I put free(line) here I get seg fault. If I don't it occurs memory leak). Here is my code:
void checkOccurrences(FILE *inp, char *wanted, int lineCount, int limit)
{
char option;
char *line = malloc(INT_MAX * sizeof(char));
int i, dif, lineNumber = 0; /*i for count, dif means difference*/
char *temp, *checkpoint;
while(fgets(line, INT_MAX, inp) != NULL) /*gets line by line*/
{
dif = strlen(line) - strlen(wanted); /*for limit for loop*/
++lineNumber; /*increase line number*/
for(i = 0; i < dif; ++i) /*difference times loop*/
{
temp = strstr(line, wanted); /*first occurrence address on line*/
if(temp != NULL) /*if there is occurrence*/
{ /*temp>checkpoint condition means if there is new occurrences*/
if(temp > checkpoint)
{
++lineCount;
if((lineCount % limit) == 0)
{
printLine(lineNumber);
checkpoint = temp;
printf("more(m) or quit(q) ");
scanf(" %c", &option); /*get option*/
if(option == 'q')
{
/*If I put free(line) here I get seg fault*/
/*If I don't it occurs memory leak*/
exit(0); /*end program*/
}
}
else
{
printLine(lineNumber);
checkpoint = temp;
}
}
}
++line; /*next address on line*/
}
}
/*If I put free(line) here I get seg fault*/
/*If I don't it occurs memory leak*/
}
/*GIT121044025*/
Looks like you're changing line inside the loop, so you're not free'ing the same pointer you got from malloc()
You should make a copy of the pointer for modifying in the loop and leave the original alone.
You might also want to consider doing something less horrid than allocating a potentially enormous amount of memory up front, just in case, and then using it without error checks.
(you don't check the memory was ever allocated, and your fgets always assumes you have INT_MAX bytes available even after you increment the pointer into the buffer).
You lose your base address here:
++line; /*next address on line*/
Store line in a temp variable like this:
char *line = malloc(INT_MAX * sizeof(char));
char *_temp = line ;
And at the end of function:
free(_temp);

how to put char * into array so that I can use it in qsort, and then move on to the next line

I have lineget function that returns char *(it detects '\n') and NULL on EOF.
In main() I'm trying to recognize particular words from that line.
I used strtok:
int main(int argc, char **argv)
{
char *line, *ptr;
FILE *infile;
FILE *outfile;
char **helper = NULL;
int strtoks = 0;
void *temp;
infile=fopen(argv[1],"r");
outfile=fopen(argv[2],"w");
while(((line=readline(infile))!=NULL))
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if(temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
} else {
helper=temp;
}
while (ptr != NULL) {
strtoks++;
fputs(ptr, outfile);
fputc(' ', outfile);
ptr = strtok(NULL, " ");
helper[strtoks-1] = ptr;
}
/*fputs(line, outfile);*/
free(line);
}
fclose(infile);
fclose(outfile);
return 0;
}
Now I have no idea how to put every of tokenized words into an array (I created char ** helper for that purpose), so that it can be used in qsort like qsort(helper, strtoks, sizeof(char*), compare_string);.
Ad. 2 Even if it would work - I don't know how to clear that line, and proceed to sorting next one. How to do that?
I even crashed valgrind (with the code presented above) -> "valgrind: the 'impossible' happened:
Killed by fatal signal"
Where is the mistake ?
The most obvious problem (there may be others) is that you're reallocating helper to the value of strtoks at the beginning of the line, but then incrementing strtoks and adding to the array at higher values of strtoks. For instance, on the first line, strtoks is 0, so temp = realloc(helper, (strtoks)*sizeof(char *)); leaves helper as NULL, but then you try to add every word on that line to the helper array.
I'd suggest an entirely different approach which is conceptually simpler:
char buf[1000]; // or big enough to be bigger than any word you'll encounter
char ** helper;
int i, numwords;
while(!feof(infile)) { // most general way of testing if EOF is reached, since EOF
// is just a macro and may not be machine-independent.
for(i = 0; (ch = fgetc(infile)) != ' ' && ch != '\n'; i++) {
// get chars one at a time until we hit a space or a newline
buf[i] = ch; // add char to buffer
}
buf[i + 1] = '\0' // terminate with null byte
helper = realloc(++numwords * sizeof(char *)); // expand helper to fit one more word
helper[numwords - 1] = strdup(buffer) // copy current contents of buffer to the just-created element of helper
}
I haven't tested this so let me know if it's not correct or there's anything you don't understand. I've left out the opening and closing of files and the freeing at the end (remember you have to free every element of helper before you free helper itself).
As you can see in strtok's prototype:
char * strtok ( char * str, const char * delimiters );
...str is not const. What strtok actually does is replace found delimiters by null bytes (\0) into your str and return a pointer to the beginning of the token.
Per example:
char in[] = "foo bar baz";
char *toks[3];
toks[0] = strtok(in, " ");
toks[1] = strtok(NULL, " ");
toks[2] = strtok(NULL, " ");
printf("%p %s\n%p %s\n%p %s\n", toks[0], toks[0], toks[1], toks[1],
toks[2], toks[2]);
printf("%p %s\n%p %s\n%p %s\n", &in[0], &in[0], &in[4], &in[4],
&in[8], &in[8]);
Now look at the results:
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
As you can see, toks[1] and &in[4] point to the same location: the original str has been modified, and in reality all tokens in toks point to somewhere in str.
In your case your problem is that you free line:
free(line);
...invalidating all your pointers in helper. If you (or qsort) try to access helper[0] after freeing line, you end up accessing freed memory.
You should copy the tokens instead, e.g.:
ptr = strtok(NULL, " ");
helper[strtoks-1] = malloc(strlen(ptr) + 1);
strcpy(helper[strtoks-1], ptr);
Obviously, you will need to free each element of helper afterwards (in addition to helper itself).
You should be getting a 'Bad alloc' error because:
char **helper = NULL;
int strtoks = 0;
...
while ((line = readline(infile)) != NULL) /* Fewer, but sufficient, parentheses */
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if (temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
}
This is because the value of strtoks is zero, so you are asking realloc() to free the memory pointed at by helper (which was itself a null pointer). One outside chance is that your library crashes on realloc(0, 0), which it shouldn't but it is a curious edge case that might have been overlooked. The other possibility is that realloc(0, 0) returns a non-null pointer to 0 bytes of data which you are not allowed to dereference. When your code dereferences it, it crashes. Both returning NULL and returning non-NULL are allowed by the C standard; don't write code that crashes regardless of which behaviour realloc() shows. (If your implementation of realloc() does not return a non-NULL pointer for realloc(0, 0), then I'm suspicious that you aren't showing us exactly the code that managed to crash valgrind (which is a fair achievement — congratulations) because you aren't seeing the program terminate under control as it should if realloc(0, 0) returns NULL.)
You should be able to avoid that problem if you use:
temp = realloc(helper, (strtoks+1) * sizeof(char *));
Don't forget to increment strtoks itself at some point.

Resources