Why is fgets resetting my double pointer element? - c

I'm trying to store an array of string in a double pointer but it doesn't seem to be doing so.
char **pointerA;
char *pointerB;
int count;
FILE* file = fopen("textfile.ini", "r");
pointerA = (char **) malloc (sizeof(*pointerA));
pointerB = (char *) malloc (sizeof(*pointerB));
while(fgets(pointerB, 200, file) !== NULL)
{
pointerA = (char **)realloc(pointerA, sizeof(char *) * (strlen(pointerB) + 1));
pointerA[count] = pointerB;
count++;
}
fclose(file);
I expect every element to only store it's own string but it seems like all the element is storing the last string.

you need to allocate each element of pointerA like so :
int nbLines = 10; //number of lines to read in file
char** pointerA = ( char** ) malloc ( sizeof ( char* ) * nbLines ); //allocate 2D array. Each element points to another string
for ( int i = 0; i < nbLines; ++i ) {
char line [ 200 ];
fgets ( line , 200 , file ); //get a line from the file
pointerA [ i ] = ( char* ) malloc ( sizeof ( char ) * strlen ( line ) ); //allocate a string with the size of that line
pointerA [ i ] = line;
}

pointerB = (char *) malloc (sizeof(*pointerB));
you allocate 1 char and read in the fgets up to 200.
edit
It has to be something like this
char **pointerA;
char *pointerB;
#define MAXSTRING 200
FILE* file = fopen("textfile.ini", "r");
pointerA = NULL;
size_t nlines = 0;
do
{
pointerB = malloc(MAXSTRING);
pointerA = realloc(pointerA, sizeof(char *) * (nlines+1));
pointerA[nlines] = pointerB;
nlines++;
}while(pointerB & fgets(pointerB, , file) !== NULL)
fclose(file);

If you want the program to work with n strings you could do something like this:
int cur_lines = 0;
char **pointerA = malloc(sizeof(char *) * (cur_lines + 1));
char line [200];
while(fgets(line, 200, file))
pointerA[cur_lines] = malloc(sizeof(char) * 200);
strcpy(pointerA[cur_lines],line);
cur_lines += 1;
pointerA = realloc(pointerA, sizeof(char *) * (cur_lines + 1));
}
Of course you should check that the results of the malloc and realloc are not NULL before using these variables, usually you don't directly overwrite the pointer you are using whith a realloc but create a new one instead and overwrite the old one if it's not NULL to avoid having memory leaks.
If you want better performance you shouldn't increase pointerA by only 1 at each loop but more than that (usually double) and keep a counter of used spaces. Also keep in mind that having a line of 200 chars means that the maximum line length is actually 199 since the last character is \0.
The problem with this approach is that you'll have one last unused malloc'd space that you need to take care of later.

Related

Problem with memory allocation 3d char array C

I am trying to read a 2d array of values from a file and then trying to copy that to a 2d array of Strings in C. When I ran my code using gdb I got an error that said "munmap_chunk(): invalid pointer" for the line where I "free( tmval )". Then when I ran the code using valgrind I get several "Invalid write size of 8" that happen whenever I try to read or write a string in the 2d array.
The problem happens during this portion of the code:
//allocate memory for transition matrix
tm = (char ***) malloc( numstates*NUM_CLASSES-1*4*sizeof( char ) );
//initialize variables needed for loop
int i = 0;
char *tmval = (char *) calloc( 4, sizeof( char ) );
// read and process all the remaining lines from the file
while( fgets(buf, MAXBUF, fp) != NULL ) {
//add comment detection
if( buf[0] !='#' ){
//allocate transition states for each row using calloc
tm[i] = (char **) calloc( NUM_CLASSES-1, (4*sizeof( char )) );
//strok first with space to skip row number
ptr = strtok( buf, " " );
for( int j = 0;j<NUM_CLASSES-1;j++ ){
//allocate space for string in array
tm[i][j] = (char *) calloc( 4, sizeof( char ) );
tm[i][j] = "-1d";
}
//loop through line to get class numbers and corresponding states
ptr = strtok( NULL, " " );
while( ptr!=NULL ){
int cls = strtol( ptr, end, 10 );
tmval = *end+1;
tm[i][cls] = tmval;
ptr = strtok( NULL, " " );
}
//iterate i
i++;
}
}
//free up tmval and file
fclose( fp );
free( tmval );
free( end );
I believe this maybe the root of the problem, but I'm not sure how to fix it:
tm[i] = (char **) calloc( NUM_CLASSES-1, (3*sizeof( char )) );
The input file for the program is formatted like this
0 0/0d 1/0d 2/1s 3/3s 4/2s 5/2s 6/5s 7/4s 8/4s 10/9d
1 0/9d 1/9d 2/1s 3/1s 4/1s 5/1s 10/9d
The first number is the row number and the number before the / column number for the value that comes after the /.
I think that many of the Valgrind errors are happening because my memory allocation sizes don't match, but I'm not sure how I can fix that because to me they look ok. Also what is causing the error when I try to free up the space for tmval.
You have the line char *tmval = (char *) calloc( 4, sizeof( char ) ); , which allocates some memory and assigns its address to tmval. You later have the line tmval = *end+1; which reassigns tmval the address of some memory that is in buf. Reassigning the value discards your only copy of the address of the memory that the calloc allocated, and is effectively a memory leak. The subsequent free(tmval) is invalid, because the memory that tmval now addresses was not created by calloc, but is inside buf.
Assigning to a pointer does not copy memory. You need memcpy or strcpy (or something similar) to make a copy of the data.
The allocation size in tm = (char ***)malloc( numstates*NUM_CLASSES-1*4*sizeof(char)) is bogus because
NUM_CLASSES-1 must be parenthesized.
you compute the size of a 3D packed array but use types that are inconsistent with this: char ***tm has nothing to do with char tm[numstates][NUM_CLASSES-1][4];
Parsing the line of input is incorrect too:
tmval = *end+1; makes tmval point inside the array buf, so the next statement tm[i][cls] = tmval; makes the entry in the pseudo-2D array point inside the buf array, which will be overwritten immediately upon reading the next line.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_CLASSES 20
#define MAXBUF 1024
// load_matrix allocates and returns a 3D matrix loaded with the transition states
char (*load_matrix(FILE *fp, int numstates))[NUM_CLASSES - 1][4] {
char buf[MAXBUF];
//allocate memory for transition matrix
char (*tm)[NUM_CLASSES - 1][4] = calloc(sizeof(*tm), numstates);
//initialize transition states for all rows
for (int i = 0; i < numstates; i++) {
for (int j = 0; j < NUM_CLASSES - 1; j++)
strcpy(tm[i][j], "-1d");
}
// read and process all the remaining lines from the file
while (fgets(buf, MAXBUF, fp) != NULL) {
char *ptr, *end;
//add comment detection
if (buf[0] == '#')
continue;
//strok first with space to skip row number
ptr = strtok(buf, " \t\n");
if (!ptr)
continue;
int row = strtol(ptr, &end, 10);
if (ptr == end || row < 0 || row >= numstates) {
// invalid row
continue;
}
//loop through line to get class numbers and corresponding states
while ((ptr = strtok(NULL, " \t\n")) != NULL) {
int cls = strtol(ptr, &end, 10);
if (end > ptr && *end++ == '/' && strlen(end) < 4 && cls >= 0 && cls < NUM_CLASSES - 1)
strcpy(tm[row][cls], end);
}
}
return tm;
}
The matrix is allocated in a single call to calloc() and can be freed with free(tm). Here is an example:
int main() {
char (*tm)[NUM_CLASSES - 1][4] = load_matrix(stdin, 100);
[...]
free(tm);
return 0;
}

Dynamic array allocation returns only last element in all indices C

Okay so I am trying to input data from text file into dynamic arrays of strings. Each data tuple has 6 attributes. Hence 6 arrays. The problem is that when I populate all the arrays, It prints right values in loop.
But when I try to access any of the array's element outside the loop, it gives the last word of text file as output. I have tried all the solutions available and none of them seems to work.
The code is:
int n(FILE **fp, int size, char **presenters, char **birth_numbers, char **room_codes,
char **authors, char **post_titles, char **presentation_types, char **presentation_times,
char **dates){
// Buffer to get input from the file.
char buffer[200];
// To check if the file is open or not
if (*fp == NULL){
printf("File not Open");
}
else{
char *file = "konferencny_zoznam.txt";
fseek(*fp, 0, SEEK_SET);
if (size >= 1){
int length_of_arrays = sizeof presenters / sizeof *presenters;
if (length_of_arrays > 1){
printf("in null if");
free(presenters);
free(birth_numbers);
free(room_codes);
free(authors);
free(post_titles);
free(presentation_times);
free(presentation_types);
free(dates);
}
presenters = malloc((size+1)* sizeof(char*));
birth_numbers = malloc((size+1)* sizeof(char*));
room_codes = malloc((size+1)* sizeof(char*));
authors = malloc((size+1)* sizeof(char*));
post_titles = malloc((size+1)* sizeof(char*));
presentation_times = malloc((size+1)* sizeof(char*));
presentation_types = malloc((size+1)* sizeof(char*));
dates = malloc((size+1)* sizeof(char*));
const unsigned MAX_BUFFER_LENGTH = 256;
char buffer[MAX_BUFFER_LENGTH];
int i = 0, len = 0;
while(fgets(buffer, MAX_BUFFER_LENGTH, *fp)){
// len = strlen(buffer)+1;
presenters[i] = buffer;
fgets(buffer, MAX_BUFFER_LENGTH, *fp);
birth_numbers[i] = buffer;
fgets(buffer, MAX_BUFFER_LENGTH, *fp);
room_codes[i] = buffer;
fgets(buffer, MAX_BUFFER_LENGTH, *fp);
authors[i] = buffer;
fgets(buffer, MAX_BUFFER_LENGTH, *fp);
post_titles[i] = buffer;
fgets(buffer, MAX_BUFFER_LENGTH, *fp);
presentation_times[i] = buffer;
fgets(buffer, MAX_BUFFER_LENGTH, *fp);
presentation_types[i] = buffer;
fgets(buffer, MAX_BUFFER_LENGTH, *fp);
dates[i] = buffer;
printf("buffer : %s", dates[i]);
fgets(buffer, MAX_BUFFER_LENGTH, *fp);
i++;
}
for (int i=0; i<8; i++){
printf("presenter[0]: %s", dates[i]);
// Outputs 20200406 for each iteration which is the last word of file.
}
}
else {
printf("File not read already. Consider running command v before.");
}
}
}
You cannot do
array[i] = buffer;
because the buffer is a char array and stores the address of the char array. When you assign its value to a new variable, you are basically copying its address to a new variable. That's why each index of your array is pointing towards the same array that holds the latest value from the file.
You need to perform a deep copy by copying the contents of buffer variable either by strcpy or something else.
For starters the expression used as an initializer in this declaration
int length_of_arrays = sizeof presenters / sizeof *presenters;
does not make a sense because it is equivalent to
int length_of_arrays = sizeof( char ** ) / sizeof( char * );
In this while loop
while(fgets(buffer, MAX_BUFFER_LENGTH, *fp)){
// len = strlen(buffer)+1;
presenters[i] = buffer;
//..
all elements dates[i] point to the same array
dates[i] = buffer;
So this loop
for (int i=0; i<8; i++){
printf("presenter[0]: %s", dates[i]);
// Outputs 20200406 for each iteration which is the last word of file.
}
outputs what the last was stored in the array buffer.
It seems you need to allocate arrays that will be pointed to by the elements dates[i] and copy there strings from buffer.
Also the function is very complicated. You should combine all parameters starting from the third parameter in a structure.
Take into account that as all the pointers are passed by value then the reallocation of memory within the function will not be reflected in the pointers used as arguments.
So I figured it out. There were 2 issues. 1: mentioned by Ehmad i.e We need to use strcpy() while using pointers and the other was that at every step of loop, each index of array needed to have memory allocated separately.

pointer of pointer of char in c, assignment crashes

I have a pointer of pointer to store lines I read from a file;
char **lines;
And I'm assigning them like this :
line_no=0;
*(&lines[line_no++])=buffer;
But it crashes why ?
According to my logic the & should give the pointer of zeroth index, then *var=value, that's how to store value in pointer. Isn't it ?
Here is my current complete code :
void read_file(char const *name,int len)
{
int line_no=0;
FILE* file;
int buffer_length = 1024;
char buffer[buffer_length];
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
printf("---%s", buffer);
++line_no;
if(line_no==0)
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
else
{
lines = (char**)realloc(lines,sizeof(*lines) * line_no);
}
lines[line_no-1] = (char*)malloc(sizeof(buffer));
lines[line_no-1]=buffer;
printf("-------%s--------\n", *lines[line_no-1]);
}
fclose(file);
}
You have just a pointer, nothing more. You need to allocate memory using malloc().
Actually, you need first to allocate memory for pointers, then allocate memory for strings.
N lines, each M characters long:
char** lines = malloc(sizeof(*lines) * N);
for (int i = 0; i < N; ++i) {
lines[i] = malloc(sizeof(*(lines[i])) * M);
}
You are also taking an address and then immediately dereference it - something like*(&foo) makes little to no sense.
For updated code
Oh, there is so much wrong with that code...
You need to include stdlib.h to use malloc()
lines is undeclared. The char** lines is missing before loop
if in loop checks whether line_no is 0. If it is, then it allocates lines. The problem is, variable line_no is 0 - sizeof(*lines) times 0 is still zero. It allocates no memory.
But! There is ++line_no at the beginning of the loop, therefore line_no is never 0, so malloc() isn't called at all.
lines[line_no-1] = buffer; - it doesn't copy from buffer to lines[line_no-1], it just assigns pointers. To copy strings in C you need to use strcpy()
fgets() adds new line character at the end of buffer - you probably want to remove it: buffer[strcspn(buffer, "\n")] = '\0';
Argument len is never used.
char buffer[buffer_length]; - don't use VLA
It would be better to increment line_no at the end of the loop instead of constantly calculating line_no-1
In C, casting result of malloc() isn't mandatory
There is no check, if opening file failed
You aren't freeing the memory
Considering all of this, I quickly "corrected" it to such state:
void read_file(char const* name)
{
FILE* file = fopen(name, "r");
if (file == NULL) {
return;
}
int buffer_length = 1024;
char buffer[1024];
char** lines = malloc(0);
int line_no = 0;
while (fgets(buffer, buffer_length, file)) {
buffer[strcspn(buffer, "\n")] = '\0';
printf("---%s\n", buffer);
lines = realloc(lines, sizeof (*lines) * (line_no+1));
lines[line_no] = malloc(sizeof (*lines[line_no]) * buffer_length);
strcpy(lines[line_no], buffer);
printf("-------%s--------\n", lines[line_no]);
++line_no;
}
fclose(file);
for (int i = 0; i < line_no; ++i) {
free(lines[i]);
}
free(lines);
}
Ok, you have a couple of errors here:
lines array is not declared
Your allocation is wrong
I don't understand this line, it is pointless to allocate something multiplying it by zero
if( line_no == 0 )
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
You shouldn't allocate array with just one element and constantly reallocate it. It is a bad practice, time-consuming, and can lead to some bigger problems later.
I recommend you to check this Do I cast the result of malloc? for malloc casting.
You could write something like this:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void read_file(char const *name)
{
int line_no = 0, arr_size = 10;
int buffer_length = 1024;
char buffer[buffer_length];
char **lines;
FILE* file;
lines = malloc(sizeof(char*) * 10);
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
buffer[strlen(buffer)-1] = '\0';
printf("---%s", buffer);
++line_no;
if(line_no == arr_size)
{
arr_size += 10;
lines = realloc(lines, sizeof(char*) * arr_size);
}
lines[line_no-1] = malloc(sizeof(buffer));
lines[line_no-1] = buffer;
printf("-------%s--------\n", lines[line_no-1]);
}
fclose(file);
}
PS, fgets() also takes the '\n' char at the end, in order to prevent this you can write the following line: buffer[strlen(buffer)-1] = '\0';

reading a file to array of strings

I'm new to C and just learning about malloc and realloc and help from the community in understanding how to do this. I have a file with paragraphs that I need to read line by line and store the lines in array o strings while creating the arrays dynamically.
Inillially the MAX number of lines to store is 10 if this is not sufficient we use realloc to double the memory and print a message indicating that we reallocated memory. So far this is what I have and need help to finish
int main(int argc, char* argv[])
{
char* p = malloc(10* sizeof(char));
while(buffer, sizeof(buffer), stdin)
{
}
}
while(buffer, ... does nothing, use fgets:
data.txt:
one
two
three
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_LEN 32
extern char *strdup(const char *);
int main(void)
{
char **arr = NULL;
char buf[BUF_LEN];
size_t i, n = 0;
FILE *f;
f = fopen("data.txt", "r");
if (f == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
while (fgets(buf, BUF_LEN, f)) {
arr = realloc(arr, sizeof(*arr) * (n + 1));
if (arr == NULL) {
perror("realloc");
exit(EXIT_FAILURE);
}
arr[n] = strdup(buf);
if (arr[n++] == NULL) {
perror("strdup");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < n; i++) {
printf("%s", arr[i]);
free(arr[i]);
}
free(arr);
}
You said and you do need array of strings. Come to think of it, a string is a sequence/array of characters, right? So you need array of array of characters.
Now, a char * is capable of pointing to a character and indirectly to the subsequent characters, if there are any. This is what we call as a string, and here's how we have one:
char * astring = malloc( 256 * sizeof * astring );
// astring holds an adress pointing to a memory location
// which has the capacity of 256 *astring s
// astring is a string tha can hold 255 characters
// with the full-stop '\0' at the end
Now you want 10 of such, 10 of char *s. char ** will be able to point at them, just like char * can at chars.
char ** lines = malloc( 10 * sizeof * lines );
for ( int i = 0; i < 10; i++ )
lines[i] = malloc( 256 );
// sizeof may be omittid for chars
If you are planning to increase 10, it's a good idea to store that inside a variable, double it when needed and reallocate accordingly.
int numlines = 10;
int linelength = 256;
char ** lines = malloc( numlines * sizeof * lines );
for( int linenr = 0; fgets( lines[linenr] = malloc( linelength ), linelength, yourfile ) != EOF; linenr++ ) {
if ( linenr + 1 == numlines ) {
numlines *= 2;
lines = realloc( lines, numlines * sizeof * lines );
}
}
Include necessary headers, fill in the gaps and make checks if the allocations and fopen succeeded, make sure 256 is enough, increase that if necessary. You may optionally make that adaptive as well, but that'll require more code.

C reallocating to find end of line, but not getting entire line?

I have a function that is finding the number of lines in a text file and returning these lines. I have to dynamically resize the array. However, it is only displaying the last few letters in each line within the buffer when I print it. I'm new to C. This is the main part of the code:
char * foo( char **buffer, FILE * infile )
...
int buffSizer = 10;
*buffer = calloc( buffSizer, 1);
do {
char * result = fgets(*buffer, buffSizer, infile);
if (result == NULL){
free(*buffer);
return(NULL);
}
char * ptr = strchr(*buffer, '\n');
if (ptr){
return(*buffer);
}
buffSiz = buffSizer * 2;
*buffer = realloc(*buffer, buffSizer);
} while (1);
Every time you need to realloc, you immediately go back and overwrite everything you've read so far. You need to account for an offset into *buffer for charactrers you've already read.
For example, you could add:
int offset = 0;
Along with the buffSiz declaration, and then use it like:
char * result = fgets(*buffer + offset, buffSiz - offset, infile);
And then when you realloc, add:
offset = buffSiz - 1; // -1 to account for null character stored by fgets()
Before this line:
buffSiz = buffSiz * 2;
As a bit of an aside, it's dangerous to reassign *buffer at the same time you pass it to realloc. If an error occurs, you'll leak the original allocation and the data will be unrecoverable. This example is safer:
char *tmp = realloc(*buffer, buffSiz);
if (!tmp)
{
free(*buffer);
return NULL;
}
*buffer = tmp;

Resources