Pointers, files and memory management in C - c

I am new to the world of C programming and at the moment I am exploring a combination of pointers, pointer arithmetic with file IO and memory management, all at once. Please find my code below and here is what I am trying to do.
My program is supposed to allocate 8 bytes of heap memory using malloc, then store the pointer from malloc to a char*, then open a file (text.txt), which contains the following lines of plain text (each 8 bytes long):
chartest
chtest2!
I am then trying to read 8 bytes at a time from text.txt using fread, until the end of file has been reached. The 8 bytes read in fread are stored in the chuck of memory allocated earlier with malloc. I am then using my char* to iterate over the 8 bytes and print each character in stdout using printf. After every 8 bytes (and until EOF) I reset my pointer to the 0th byte of my 8-byte memory chunk and repeat until EOF.
Here is the code:
int main(void)
{
char* array = malloc(8 * sizeof(char));
if (array == NULL)
return 1;
FILE* inptr = fopen("text.txt", "r");
if (inptr == NULL)
return 2;
while (!feof(inptr))
{
fread(array, 8 * sizeof(char), 1, inptr);
for (int i = 0; i < 8; i++)
{
printf("%c", *array);
array++;
}
array -= 8;
}
free(array);
fclose(inptr);
return 0;
}
Please bare in mind that the program has been run through valgrind, which reports no memory leaks. This is the output I get:
chartest
chtest2!
htest2
I don't get where the 3rd line comes from.
Moreover, I don't understand why when I reset my char pointer (array) using
array -= 7;
and running through valgrind it reports:
LEAK SUMMARY:
==8420== definitely lost: 8 bytes in 1 blocks
Logically thinking of the 8 bytes of heap memory as an array of chars we would have to take the pointer back 7 places to reach spot 0, but this approach seems to leak memory (whereas array -= 8 is fine)!
I would be very grateful if someone could analyse this. Thanks!

As pointed to in the comments, you are using feof incorrectly, which explains the extra line. As for subtracting 7 instead of 8: you add 1 to array 8 times, so why would you expect subtracting 7 to get you back to where you started?

I made some changes to your code and everything is working fine. Here it is :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char* array = malloc(9 * sizeof(char)); \\changed
if (array == NULL)
return 1;
FILE* inptr = fopen("file", "r");
if (inptr == NULL)
return 2;
while (!feof(inptr))
{
fread(array, 9 * sizeof(char), 1, inptr); \\changed
int i=0;
for (i = 0; i < 8 ; i++)
{
if(feof(inptr)) \\added
goto next; \\added
printf("%c", *array);
array++;
}
printf("\n"); \\added
next:array =array - i; \\changed
}
free(array);
fclose(inptr);
return 0;
}
You need to take care of the space allocated, the end of file EOF character and the end of line \n and for that reason your program did not work as you were expecting !!!

your file is
c h a r t e s t \n c h t e s t 2 ! \n
first loop reads 8 characters and prints prints chartest
second loop reads 8 characters and prints \nchtest2
third loop reads the last 2 characters and prints !\nhtest2
this because htest2 was left in the buffer after reading to the end of the file.
checking the return value from fread() may be helpful
eg: make these changes:
int n = fread(array, sizeof(char), 8, inptr);
for (int i = 0; i < n; i++)
array -= i;

Related

Need to read a file with multiple lines of integers into an array

I need to read a file of ints into an array in C. A sample of the file I need to read is below, though note the files this will process can have thousands or hundreds of thousands of lines.
127
234
97
8723
I've gotten the file open in C, read how many lines there are so I know how many spaces my array needs, but I can't seem to read/parse each line into the array.
FILE *file;
int N = 0;
char filePath[30];
char endFile;
printf("What file should be used?\n");
scanf("%s", filePath);
file = fopen(filePath, "r");
if(file == NULL) {
printf("This file failed to open.\n");
break;
}
for(endFile = getc(file); endFile!=EOF; endFile=getc(file))
if(endFile == '\n') {
N = N+1;
}
int myArray[N];
while(fscanf(file, "%d\n", &a) != EOF) {
fscanf(file, "%d\n", &a); // I'm not sure this line is needed...
printf("%d\n", a);
M[i] = a;
}
From here, I need to read the file contents into myArray, with each line being the corresponding spot in the array (i.e. line zero is myArray[0], line one is myArray[1], etc.). I can't seem to find a way to do this, though I see several methods to do tab-delimited 2d arrays or csv multi-dimensional arrays.
Please also let me know if creating the array/determining the array size can be done in a better way than literally counting new-line characters...
There's no need to first "count the number of lines".
The following code cautiously grows an array of integers (by increments of 10).
#define GROW 10
int *rec = NULL, nRec = 0, sz = 0;
while( fgets( buf, sizeof buf, ifp ) != NULL ) {
if( nRec == sz ) {
rec = realloc( rec, (nRec+GROW) * sizeof *rec );
/*omitting test for failure */
sz += GROW;
}
rec[ nRec++ ] = atoi( buf );
}
This shows what is possible.
Note that realloc() can fail, returning NULL... It's up to you to add a bit of code to handle that condition.
Further, some conventional thought is to double the size of the allocation when needed (because realloc() may not be 'cheap'.) You can decide if you want to grow the array in increments (of 1024?) or grow it exponentially.

fread() reading too much data

Currently I am trying to read a binary file 16 bytes at a time. This is great until the end of the file where it reads a full 16 bytes regardless of whether the file has ended or not. I have a file with "ABCDEFGHIJKLMNOPQRSTUV" written into it and it reads "QRSTUVGHIJKLMNOP" on the second pass after successfully receiving the first 16. How can I stop it from re reading those bytes? This is what I have currently. It obviously does not print the second pass of data because fread does not return 16. I should note I MUST read 16 bytes at a time.
#define BS 16
unsigned char buffer[BS]; // for storing each 16 bytes read
int i = 0; // for iterating through readbytes
while (fread(buffer, 1, sizeof(buffer), ifp) == BS) {
while (i < BS) {
printf("Read: %c\n",buffer[i]);
i++;
}
i = 0;
}
It doesn't reread the bytes GHI…NOP; they are still in the buffer from the previous successful read. When fread() reports a short read, the contents of the buffer after the data it reports reading are indeterminate. You need to save the return value from fread() — it's a size_t — and use that to guide you. It reports 6 bytes; accessing anything after that leads to undefined (unspecified, indeterminate — generically 'undesirable') behaviour.
You should define int i = 0; inside the outer while loop; then you wouldn't need the trailing i = 0; after the inner loop. Or even just a routine for loop, as M.M commented.
Hence:
#define BS 16
unsigned char buffer[BS];
size_t nbytes;
while ((nbytes = fread(buffer, 1, sizeof(buffer), ifp)) == BS)
{
for (size_t i = 0; i < BS; i++)
printf("Read: %c\n", buffer[i]);
}
printf("Short read (%zu bytes):\n", nbytes);
for (size_t i = 0; i < nbytes; i++)
printf("Short: %c\n", buffer[i]);

Reading integers from txt file in C

I`m making a file reader that reads integers numbers line by line from a file. The problem is that is not working. I think I am using fscanf in a wrong way. Can someone help me?
I had already look for answers in others questions but I can`t find anything that explain why I my code is not working.
int read_from_txt(){
FILE *file;
file = fopen("random_numbers.txt", "r");
//Counting line numbers to allocate exact memory
int i;
unsigned int lines = 0;
unsigned int *num;
char ch;
while(!feof(file)){
ch = fgetc(file);
if (ch == '\n'){
lines++;
}
}
//array size will be lines+1
num = malloc(sizeof(int)*(lines+1));
//storing random_numbers in num vector
for(i=0;i<=lines;i++){
fscanf(file, "%d", &num[i]);
printf("%d", num[i]);
}
fclose(file);
}
The txt file is like:
12
15
32
68
46
...
But the output of this code keeps giving "0000000000000000000..."
You forgot to "rewind" the file:
fseek(file, 0, SEEK_SET);
Your process of reading goes through the file twice - once to count lines, and once more to read the data. You need to go back to the beginning of the file before the second pass.
Note that you can do this in a single pass by using realloc as you go: read numbers in a loop into a temporary int, and for each successful read expand the num array by one by calling realloc. This will expand the buffer as needed, and you would not need to rewind.
Be careful to check the results of realloc before re-assigning to num to avoid memory leaks.
You could try to use the getline function from standard IO and add the parsed numbers into the array using only one loop. See the code below. Please check https://linux.die.net/man/3/getline
Also, you can use the atoi or strtoul functions to convert the read line to an integer. Feel free to check https://linux.die.net/man/3/atoi or https://linux.die.net/man/3/strtoul
The code below evaluate a file with a list of numbers and add those numbers to a C integer pointer
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char ** argv) {
FILE * file;
file = fopen("./file.txt", "r");
size_t read;
char * line = NULL;
size_t line_len = 0;
size_t buffer_size = 10;
int * buffer = (int *)malloc(sizeof(int) * buffer_size);
int seek = 0;
while((read = getline(&line, &line_len, file)) != -1) {
buffer[seek++] = atoi(line);
if (seek % 10 == 0) {
buffer_size += 10;
buffer = (int *)realloc(buffer, sizeof(int) * buffer_size);
}
}
for (int i = 0; i < seek; i++) {
printf("%d\n", buffer[i]);
}
free(buffer);
fclose(file);
}
If you aren't sure which conversion function should you use. You can check the difference between atoi and sscanf at What is the difference between sscanf or atoi to convert a string to an integer?

Program stop reading file using fgets

It would be nice if anyone could help me with my 'program'. I am trying to read csv file and move it to 2D array. It stops on 17th line(out of 200).
int main ()
{
FILE * pFile;
double **tab;
char bufor [100];
int i=0;
tab = (double**)malloc(sizeof(double*));
pFile = fopen ("sygnal1.csv" , "r");
if (pFile == NULL) printf("Error");
else
while (fgets (bufor , 100 , pFile))
{
tab[i] = (double *) malloc(2 * sizeof(double));
sscanf(bufor, "%lf, %lf,", &tab[i][0], &tab[i][1]);
printf("%lf.%lf.\n",tab[i][0],tab[i][1]); //It's here only for testing
i++;
}
printf("number of lines read %d\n",i);
fclose (pFile);
system("PAUSE");
return 0;
}
You haven't completely allocated memory for tab yet. You've just allocated one (uninitialised) pointer. When i > 0 you're into Undefined Behaviour. You need to allocate at least as many elements as there might be lines in your file, e.g.
tab = malloc(sizeof(*tab) * MAX_LINES);
or use realloc after each iteration to increase the number of elements.
tab = (double**)malloc(sizeof(double*));
You're only allocating 1 element in this array. All other accesses are writing over unallocated chunks of memory and probably causing damage.
Try reallocing periodically.
You created place for only one double * in tab, if you know the number of lines you want to store, then do :
tab = malloc(sizeof(*tab) * NB_LINES);
Also, don't cast the return of malloc.

c - get file into array of chars

hi i have the following code below, where i try to get all the lines of a file into an array... for example if in file data.txt i have the following:
first line
second line
then in below code i want to get in data array the following:
data[0] = "first line";
data[1] = "second line"
My first question: Currently I am getting "Segmentation fault"... Why?
Exactly i get the following output:
Number of lines is 7475613
Segmentation fault
My second question: Is there any better way to do what i am trying do?
Thanks!!!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
FILE *f = fopen("data.txt", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos);
fread(bytes, pos, 1, f);
int i =0;
int counter = 0;
for(; i<pos; i++)
{
if(*(bytes+i)=='\n') counter++;
}
printf("\nNumber of lines is %d\n", counter);
char* data[counter];
int start=0, end=0;
counter = 0;
int length;
for(i=0; i<pos; i++)
{
if(*(bytes+i)=='\n')
{
end = i;
length =end-start;
data[counter]=(char*)malloc(sizeof(char)*(length));
strncpy(data[counter],
bytes+start,
length);
counter = counter+1;
start = end+1;
}
}
free(bytes);
return 0;
}
First line of the data.txt in this case is not '\n' it is: "23454555 6346346 3463463".
Thanks!
You need to malloc 1 more char for data[counter] for the terminating NUL.
after strncpy, you need to terminate the destination string.
Edit after edit of original question
Number of lines is 7475613
Whooooooaaaaaa, that's a bit too much for your computer!
If the size of a char * is 4, you want to reserve 29902452 bytes (30M) of automatic memory in the allocation of data.
You can allocate that memory dynamically instead:
/* char *data[counter]; */
char **data = malloc(counter * sizeof *data);
/* don't forget to free the memory when you no longer need it */
Edit: second question
My second question: Is there any
better way to do what i am trying do?
Not really; you're doing it right. But maybe you can code without the need to have all that data in memory at the same time.
Read and deal with a single line at a time.
You also need to free(data[counter]); in a loop ... and free(data); before the "you're doing it right" above is correct :)
And you need to check if each of the several malloc() calls succeeded LOL
First of all you need to check if the file got opened correctly or not:
FILE *f = fopen("data.txt", "rb");
if(!f)
{
fprintf(stderr,"Error opening file");
exit (1);
}
If there is error opening the file and you don't check it, you'll get a seg fault when you try to fseek on an invalid file pointer.
Apart from that I see no errors. Tried running the program, by printing the value of the data array at the end, it ran as expected.
One thing to note is that you're opening your file as binary - line termination disciplines may not work as you expect on your platform (UNIX is lf, Windows is cr-lf, some versions of MacOS are cr).

Resources