I've never used malloc to store more than values but I have to use strdup to order the lines of an input file and I dont get a way to make it work.
I though using strdup() to get a pointer to each line and later, put each one into a space according to the number of lines reserved with malloc().
I dont know if I have to do it like reserved memory was an array to pointers, I mean using char** and later put each pointer to each strdup into reserved space.
I though something like this:
char **buffer;
char *pointertostring;
char *line; // line got using fgets
*buffer = (char*)malloc(sizeof(char*));
pointertostring = strdup(line);
I don't know what to do after that, I don't even know if this is correct, in that case, what should I do to store the pointer to the string in a position of buffer?
Regards
If I understand your requirement correctly. You'll have to do something like:
char **buffer;
char line[MAX_LINE_LEN]; // line got using fgets
int count; // to keep track of line number.
// allocate one char pointer for each line in the file.
buffer = (char**)malloc(sizeof(char*) * MAX_LINES);
count = 0; // initilize count.
// iterate till there are lines in the file...read the line using fgets.
while(fgets(line,MAX_LINE_LEN,stdin)) {
// copy the line using strdup and make the buffer pointer number 'count'
// point to it
buffer[count++] = strdup(line);
}
....
....
// once done using the memory you need to free it.
for(count=0;count<MAX_LINES;count++) {
free(buffer[count]);
}
....
....
Your buffer will only hold one pointer. You need something like:
char **buffer;
char *pString;
int linecount;
buffer = (char **)malloc(sizeof(char *)*MAXIMUM_LINES);
linecount = 0;
while (linecount < MAXIMUM_LINES) {
pString = fgets(...);
buffer[linecount++] = strdup(pString);
}
Related
I have a dynamic array that holds a string containing '\n' characters, so this string is made up of multiple lines. I'm trying to extract the lines and put them all into a 2D char array and I'm getting segmentation errors.
Here's my code:
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
input_lines = extractInput(MAX_LINE_LEN, input_file);
char inputLines_counted[lineCount_input][MAX_LINE_LEN];
char *t = strtok(input_lines, "\n");
for(i = 0; i < lineCount_input; i++) {
strcpy(inputLines_counted[i], t);
// printf("%s\n", inputLines_counted[i]);
t = strtok(NULL, "\n");
}
Upon creating the dynamic array, I use the extractInput(MAX_LINE_LEN, input_file) function to populate the input_lines array with a string containing multiple lines.
Here's the extract function:
char *extractInput(int len, FILE *file) {
char tmp[len];
char *pos;
char *input_lines = malloc(len*sizeof(char));
char *lines;
while(fgets(tmp, len, file)) {
// if((pos = strchr(tmp, '\n')) != NULL) {
// *pos = ' ';
// }
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
strcat(input_lines, tmp);
}
return input_lines;
}
Why am I getting segfaults here?
The function call
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
takes your current allocated memory block and expands it, if it can. you should check the return value of realloc, it may fail.
btw when you allocate memory in C, you always need to have space for the ending \0.
see what happens with this file
hello\n
world\n
The first fgets reads in hello\n into tmp.
you now do realloc even though it is unnecessary, input_lines is already pointing to a buffer that could hold the string
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
now with your realloc
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
you do strlen(input_lines) + len so you make the buffer strlen("hello\n") + len long.
but the important thing you need to notice is the following line
strcat(input_lines, tmp);
you have not initialized the memory that input_lines is pointing to, it can contain anything even \0's so your strcat could potentially put the string anywhere in the buffer and cause the error you describe.
Either do a memset or use calloc when you allocate the buffer.
If you use realloc you should keep track of the total size that you have allocated and how much you are using of it, before you copy into the buffer check if there is enough room. If not, add a certain number of bytes to the buffer.
I also noticed you read from the file line by line, then you concatenated the lines together to later use strtok to divide them again. It would be more efficient to return an array of lines.
I am trying to write a function to convert a text file into a CSV file.
The input file has 3 lines with space-delimited entries. I have to find a way to read a line into a string and transform the three lines from the input file to three columns in a CSV file.
The files look like this :
Jake Ali Maria
24 23 43
Montreal Johannesburg Sydney
And I have to transform it into something like this:
Jake, 24, Montreal
...etc
I figured I could create a char **line variable that would hold three references to three separate char arrays, one for each of the three lines of the input file. I.e., my goal is to have *(line+i) store the i+1'th line of the file.
I wanted to avoid hardcoding char array sizes, such as
char line1 [999];
fgets(line1, 999, file);
so I wrote a while loop to fgets pieces of a line into a small buffer array of predetermined size, and then strcat and realloc memory as necessary to store the line as a string, with *(line+i) as as pointer to the string, where i is 0 for the first line, 1 for the second, etc.
Here is the problematic code:
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#define CHUNK 10
char** getLines (const char * filename){
FILE *file = fopen(filename, "rt");
char **lines = (char ** ) calloc(3, sizeof(char*));
char buffer[CHUNK];
for(int i = 0; i < 3; i++){
int lineLength = 0;
int bufferLength = 0;
*(lines+i) = NULL;
do{
fgets(buffer, CHUNK, file);
buffLength = strlen(buffer);
lineLength += buffLength;
*(lines+i) = (char*) realloc(*(lines+i), (lineLength +1)*sizeof(char));
strcat(*(lines+i), buffer);
}while(bufferLength ==CHUNK-1);
}
puts(*(lines+0));
puts(*(lines+1));
puts(*(lines+2));
fclose(file);
}
void load_and_convert(const char* filename){
char ** lines = getLines(filename);
}
int main(){
const char* filename = "demo.txt";
load_and_convert(filename);
}
This works as expected only for i=0. However, going through this with GDB, I see that I get a realloc(): invalid pointer error. The buffer loads fine, and it only crashes when I call 'realloc' in the for loop for i=1, when I get to the second line.
I managed to store the strings like I wanted in a small example I did to try to see what was going on, but the inputs were all on the same line. Maybe this has to do with fgets reading from a new line?
I would really appreciate some help with this, I've been stuck all day.
Thanks a lot!
***edit
I tried as suggested to use calloc instead of malloc to initialize the variable **lines, but I still have the same issue.I have added the modifications to the original code I uploaded.
***edit
After deleting the file and recompiling, the above now seems to work. Thank you to everyone for helping me out!
You allocate line (which is a misnomer since it's not a single line), which is a pointer to three char*s. You never initialize the contents of line (that is, you never make any of those three char*s point anywhere). Consequently, when you do realloc(*(line + i), ...), the first argument is uninitialized garbage.
To use realloc to do an initial memory allocation, its first argument must be a null pointer. You should explicitly initialize each element of line to NULL first.
Additionally, *(line+i) = (char *)realloc(*(line+i), ...) is still bad because if realloc fails to allocate memory, it will return a null pointer, clobber *(line + i), and leak the old pointer. You instead should split it into separate steps:
char* p = realloc(line[i], ...);
if (p == null) {
// Handle failure somehow.
exit(1);
}
line[i] = p;
A few more notes:
In C, you should avoid casting the result of malloc/realloc/calloc. It's not necessary since C allows implicit conversion from void* to other pointer types, and the explicit could mask an error where you accidentally omit #include <stdlib.h>.
sizeof(char) is, by definition, 1 byte.
When you're allocating memory, it's safer to get into a habit of using T* p = malloc(n * sizeof *p); instead of T* p = malloc(n * sizeof (T));. That way if the type of p ever changes, you won't silently be allocating the wrong amount of memory if you neglect to update the malloc (or realloc or calloc) call.
Here, you have to zero your array of pointers (for example by using calloc()),
char **line = (char**)malloc(sizeof(char*)*3); //allocate space for three char* pointers
otherwise the reallocs
*(line+i) = (char *)realloc(*(line+i), (inputLength+1)*sizeof(char)); //+1 for the empty character
use an uninitialized pointer, leading to undefined behaviour.
That it works with i=0 is pure coindicence and is a typical pitfall when encountering UB.
Furthermore, when using strcat(), you have to make sure that the first parameter is already a zero-terminated string! This is not the case here, since at the first iteration, realloc(NULL, ...); leaves you with an uninitialized buffer. This can lead to strcpy() writing past the end of your allocated buffer and lead to heap corruption. A possible fix is to use strcpy() instead of strcat() (this should even be more efficient here):
do{
fgets(buffer, CHUNK, file);
buffLength = strlen(buffer);
lines[i] = realloc(lines[i], (lineLength + buffLength + 1));
strcpy(lines[i]+lineLength, buffer);
lineLength += buffLength;
}while(bufferLength ==CHUNK-1);
The check bufferLength == CHUNK-1 will not do what you want if the line (including the newline) is exactly CHUNK-1 bytes long. A better check might be while (buffer[buffLength-1] != '\n').
Btw. line[i] is by far better readable than *(line+i) (which is semantically identical).
I want to create an array with malloc and then assign the fields of the array an output of fgets.
char *words;
words = (char *)malloc(lines*sizeof(char));
int k = 0;
words[k] = (char *)malloc(mysize*sizeof(char));
This won't work because of the missing pointer I guess. What can I do?
I want to create an array with malloc and then assign the fields of the array an output of fgets. ? Then hopefully you should declare words as double pointer or array of char pointer.
One way is, by using array of char pointer like below.
char *words[lines]; /* array of pointer, lines is nothing but number of line in file */
for(int row = 0;row < lines; row++) {
/* allocate memory for each line */
words[row] = malloc(mysize);/* mysize is nothing but lines has max no of char i.e max no of char */
/* now read from file */
fgets(word[row],mysize,fp);/* reading from file(fp) & store into word[0],word[1] etc */
}
Or you can use double pointer like char **words; also.
And once job is done, at last don't forget to free the dynamically allocated memory.
Im working on a project and I need to know how to store a line read from a file into a dynamic double pointer. For example assume I have this struct:
typedef struct {
char **data; /*dynamic array of lines */
size_t nused; /*number of lines in the dynamic array */
} lines_t;
and in my readline function:
lines_t readline() {
lines_t line_data;
char line[LINESIZE];
char *data;
line_data.data = malloc(sizeof(char *)*sizeof(line));
line_data.nused = 1;
while(fgets(line,LINESIZE,fp)) {
data = line;
line_data.data = &data;
nalloc++;
}
printf("%s",*line_data.data);
return line_data;
}
But this will always print the very last line in the file, How can I make it so that I can access and print any line in the file?(I think I may have to index the double pointer)
If your system has it, use the POSIX getline. You'll probably need to duplicate the gotten line using strdup, at least if you call getline in a loop with the same arguments.
Notice that André Fratelli's answer is limiting the line size to LINESIZE but getline don't have such limitation (and could read wide lines of many thousand characters). Of course it will fail if the used malloc is failing.
See also this related question.
On GNU systems like Linux you might want to use the GNU readline library, if reading from the terminal. It adds editing abilities (and enables auto-completion).
Hence your readline name is slightly confusing. You might add a comment telling that it is not the GNU readline library, or change the name to something else, like read_a_line ....
BTW, you'll better grow & allocate your vector only once in a while, using some newsize = 3*oldsize/2 + 50; nearly geometric progression. Then you need to keep both allocated and used sizes:
typedef struct {
char **data; /*dynamic array of nallocated lines */
size_t nallocated; // allocated size of `data` above
size_t nused; // count of used lines in the dynamic array
/// invariant: nused <= nallocated
} lines_t;
At last, don't forget to test malloc (& calloc & realloc) and fgets or getline or readline against failure. On Linux, use ulimit in the shell, which calls setrlimit(2) (you want RLIMIT_AS), to stress test against malloc failure.
Try this:
lines_t readline() {
lines_t line_data;
char line[LINESIZE];
char *data;
// Allocates enough space for the array, but not the text
line_data.data = malloc(sizeof(char *)* LINESIZE);
// Zero indexed
line_data.nused = 0;
while(fgets(line,LINESIZE,fp)) {
// Allocates space for each line
line_data.data[line_data.nused] = malloc(sizeof(char) * (strlen(line) + 1));
// Copies the string from the buffer
strcpy(line_data.data[line_data.nused], line);
// One more line processed
line_data.nused++;
// I'm not sure what this is or what it's for
//nalloc++;
}
printf("%s",*line_data.data);
return line_data;
}
BTW, to answer the actual question. You can print all lines like this:
for (int it=0 ; it < line_data.nused ; ++it) {
printf("%s", line_data.data[it]);
}
I have a program that accepts a char input using argv from the command line. I copy the input argv[1] using strcpy to a pointer called structptr(it goes to structptr->words from struct) where memory has been allocated. I then copy character by character from the memory that the pointer structptr points to another pointer called words that points to memory that has been allocated. After i've copied one character i print that element [c] to make sure that it has been copied correctly(which it has). I then finish copying all of the characters and return the result to a char pointer but for some reason it is blank/null. After each copying of the characters i checked if the previous elements were correct but they don't show up anymore([c-2], [c-1], [c]). Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct StructHolder {
char *words;
};
typedef struct StructHolder Holder;
char *GetCharacters(Holder *ptr){
int i=0;
char *words=malloc(sizeof(char));
for(i;i<strlen(ptr->words);i++){
words[i]=ptr->words[i];
words=realloc(words,sizeof(char)+i);
}
words[strlen(ptr->words)]='\0';
return words;
}
int main(int argc, char **argv){
Holder *structptr=malloc(sizeof(Holder));
structptr->words=malloc(strlen(argv[1]));
strcpy(structptr->words, argv[1]);
char *charptr;
charptr=(GetCharacters(structptr));
printf("%s\n", charptr);
return 0;
At first I thought this was the problem:
char *words=malloc(sizeof(char)) is allocating 1 byte (sizeof 1 char). You probably meant char *words = malloc(strlen(ptr->words)+1); - You probably want to null check the ptr and it's member just to be safe.
Then I saw the realloc. Your realloc is always 1 char short. When i = 0 you allocate 1 byte then hit the loop, increment i and put a char 1 past the end of the realloced array (at index 1)
Also your strcpy in main is has not allocated any memory in the holder.
In these two lines,
structptr->words=malloc(strlen(argv[1]));
strcpy(structptr->words, argv[1]);
need to add one to the size to hold the nul-terminator. strlen(argv[1]) should be strlen(argv[1])+1.
I think the same thing is happening in the loop, and it should be larger by 1. And sizeof(char) is always 1 by definition, so:
...
words=realloc(words,i+2);
}
words=realloc(words,i+2); // one more time to make room for the '\0'
words[strlen(ptr->words)]='\0';
FYI: Your description talks about structptr but your code uses struct StructHolder and Holder.
This code is a disaster:
char *GetCharacters(Holder *ptr){
int i=0;
char *words=malloc(sizeof(char));
for(i;i<strlen(ptr->words);i++){
words[i]=ptr->words[i];
words=realloc(words,sizeof(char)+i);
}
words[strlen(ptr->words)]='\0';
return words;
}
It should be:
char *GetCharacters(const Holder *ptr)
{
char *words = malloc(strlen(ptr->words) + 1);
if (words != 0)
strcpy(words, ptr->words);
return words;
}
Or even:
char *GetCharacters(const Holder *ptr)
{
return strdup(ptr->words);
}
And all of those accept that passing the structure type makes sense; there's no obvious reason why you don't just pass the const char *words instead.
Dissecting the 'disaster' (and ignoring the argument type):
char *GetCharacters(Holder *ptr){
int i=0;
OK so far, though you're not going to change the structure so it could be a const Holder *ptr argument.
char *words=malloc(sizeof(char));
Allocating one byte is expensive — more costly than calling strlen(). This is not a good start, though of itself, it is not wrong. You do not, however, check that the memory allocation succeeded. That is a mistake.
for(i;i<strlen(ptr->words);i++){
The i; first term is plain weird. You could write for (i = 0; ... (and possibly omit the initializer in the definition of i, or you could write for (int i = 0; ....
Using strlen() repeatedly in a loop like that is bad news too. You should be using:
int len = strlen(ptr->words);
for (i = 0; i < len; i++)
Next:
words[i]=ptr->words[i];
This assignment is not a problem.
words=realloc(words,sizeof(char)+i);
This realloc() assignment is a problem. If you get back a null pointer, you've lost the only reference to the previously allocated memory. You need, therefore, to save the return value separately, test it, and only assign if successful:
void *space = realloc(words, i + 2); // When i = 0, allocate 2 bytes.
if (space == 0)
break;
words = space;
This would be better/safer. It isn't completely clean; it might be better to replace break; with { free(words); return 0; } to do an early exit. But this whole business of allocating one byte at a time is not the right way to do it. You should work out how much space to allocate, then allocate it all at once.
}
words[strlen(ptr->words)]='\0';
You could avoid recalculating the length by using i instead of strlen(ptr->words). This would have the side benefit of being correct if the if (space == 0) break; was executed.
return words;
}
The rest of this function is OK.
I haven't spent time analyzing main(); it is not, however, problem-free.