Array elements are "lost" outside the function - c

I have a matrix in a file like:
3
1 2 3
4 5 6
7 8 -9
where the first line indicates the square matrix order. I'm using the following code to read the file and store it into a vector (I have removed all if checks for sake of simplicity):
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int read_matrix_file(const char *fname, double *vector)
{
/* Try to open file */
FILE *fd = fopen(fname, "r");
char line[BUFSIZ];
fgets(line, sizeof line, fd);
int n;
sscanf(line, "%d", &n)
vector = realloc(vector, n * n * sizeof *vector);
memset(vector, 0, n * n * sizeof *vector);
/* Reads the elements */
int b;
for(int i=0; i < n; i++) {
// Read the i-th line into line
if (fgets(line, sizeof line, fd) == NULL) {
perror("fgets");
return(-1);
}
/* Reads th j-th element of i-th line into the vector */
char *elem_ptr = line;
for (int j=0; j < n; j++) {
if(sscanf(elem_ptr, "%lf%n", &vector[n*i+j] , &b) != 1) {
perror("sscanf");
return(1);
}
elem_ptr += b;
}
}
fclose(fd);
/* HERE PRINTS OK */
for(int i=0; i<n*n; i++)
printf("%i %f\n",i, vector[i]);
return n;
}
The read_matrix_file receives a filename and an array of doubles and fill the array, returning the matrix order. The expected usage can be seen in this code block.
int main(void)
{
const char *fname = "matrix.txt";
double *vector = malloc(sizeof * vector);
int n = read_matrix_file(fname, vector);
/* Here prints junk */
for(int i=0; i<n*n; i++)
printf("%i %f\n",i, vector[i]);
free(vector);
}
The issue is, the printf works fine inside the read_matrix_file but seems invalid in main.
I'm allocating the array outside the function and passing it by "reference", but I'm very suspecious of realloc, unfortunatelly I don't know how to fix or a better approach.

You are reallocating memory inside read_matrix_file() and storing the elements of the matrix in that memory region. But when you come out of the function, since the pointer vectoris a local variable, its new value is lost when you leave the function.
When you come back inside main() vector still points to the (now likely invalid) memory region that you had previously allocated with malloc().
You should either allocate large enough memory before calling read_matrix_file or pass a double pointer (**) if you want to modify the pointer and see the change reflected back in main()
What I meant is something like this:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int read_matrix_file(const char *fname, double **p_vector)
{
/* Try to open file */
FILE *fd = fopen(fname, "r");
char line[BUFSIZ];
fgets(line, sizeof line, fd);
int n;
sscanf(line, "%d", &n);
*p_vector = realloc(*p_vector, n * n * sizeof **p_vector);
double *vector = *p_vector;
memset(vector, 0, n * n * sizeof *vector);
/* Reads the elements */
int b;
for(int i=0; i < n; i++) {
// Read the i-th line into line
if (fgets(line, sizeof line, fd) == NULL) {
perror("fgets");
return(-1);
}
/* Reads th j-th element of i-th line into the vector */
char *elem_ptr = line;
for (int j=0; j < n; j++) {
if(sscanf(elem_ptr, "%lf%n", &vector[n*i+j] , &b) != 1) {
perror("sscanf");
return(1);
}
elem_ptr += b;
}
}
fclose(fd);
/* HERE PRINTS OK */
for(int i=0; i<n*n; i++)
printf("%i %f\n",i, vector[i]);
return n;
}
In main, call it with:
int n = read_matrix_file(fname, &vector);
EDIT: Please note that this code does not handle the failure of realloc() properly.

Related

Reading file content line by line into a matrix

I was trying to write a function that could copy a text file content line by line to a matrix, so this is what I came up with:
#include <stdio.h>
#include <stdlib.h>
#define rows 5
#define columns 12
void file_to_matrix(char *matrix[]){
FILE *file = fopen("swamp.txt", "r");
if(file==NULL){
printf("File error");
exit(1);
}
int y=0;
for(int x=0; (matrix[y][x]=fgetc(file))!=EOF; ++x){
if(matrix[y][x]=='\n'){
++y;
x=0;
}
}
fclose(file);
}
int main(){
char *matrix[rows];
file_to_matrix(matrix);
for(int i=0; i<5; ++i){
for(int j = 0; j < 11; j++){
printf("%c", matrix[i][j]);
}
printf("\n");
}
}
I tried to compile and run it on Windows and Linux but the only thing that it manages to do is giving me errors that are practically useless like:
"Stopping due to fatal error: NullReferenceException: Object reference not set to an instance of an object"
or
"Segmentation fault (core dumped)"
You have to allocate memory for each element of matrix. This is the same exact mistake as writing:
char *s = "string";
As the first act of the program, loop through matrix, and call malloc for each cell:
int i;
for (i = 0; i < 5; ++i)
if (!(matrix[i] = malloc(MAXLEN))) [
perror("malloc");
exit(EXIT_FAILURE);
}
Note: we check the return value of malloc to ensure no errors have occurred.
with your code, i think the matrix in the file will restricted (you define rows=5 and columns=12). so i think again to write a code that can be flexible, no need to set max row or column again. here we go
#include <stdio.h>
#include <stdlib.h> //malloc realloc
//file read function. returning string with splitted new line
//ex: (note: newline is '\n' (ENTER))
//test.txt:
//10 20 30(space)(newline)
//40 50 60(space)(newline)
void file_to_matrix(char *filename)
{
FILE * file = fopen(filename, "r");
char *buff = (char*)malloc(sizeof(char)); //temp string
int *matrix_row = (int*)malloc(sizeof(int)); //temp matrix row
int **MATRIX = (int**)malloc(sizeof(int*)); //this will become the final matrix
int index_buff = 0, index_MATRIX = 0, index_matrix_row = 0;
while (1)
{
char c = getc(file);
if (c==EOF)
{
break;
}
if (c=='\n') //if c meet newline, then add the matrix row to final matrix
{
(*(MATRIX+index_MATRIX)) = matrix_row;
index_MATRIX++;
MATRIX = realloc(MATRIX, sizeof(MATRIX)*(index_MATRIX+1));
matrix_row = (int*)malloc(sizeof(int));
index_matrix_row = 0;
printf("\n");
continue;
}
if (c==' ') //if c meet space, then buff wil converted to int
{
int num = atoi(buff);
printf("%d ", num);
(*(matrix_row+index_matrix_row)) = num;
index_matrix_row++;
matrix_row = realloc(matrix_row, sizeof(int)*(index_matrix_row+1));
free(buff);
buff = (char*)malloc(sizeof(char));
index_buff=0;
continue;
}
//add buff with c
(*(buff+index_buff))=c;
index_buff++;
buff = realloc(buff, sizeof(char)*(index_buff+1));
}
fclose(file);
//clearing the dynamic memory
free(buff);
for (int i = 0; i < index_MATRIX; i++)
{
free(MATRIX[i]);
}
}
int main()
{
file_to_matrix("test.txt");
return 0;
}
test.txt:
10 20 30
40 50 60
70 80 90
if you confused with the test.txt format, i will explain again:
10 20 30(space)(newline)
30 40 60(space)(newline)
70 80 90(space)(newline)
Thank you for your replies, I think your solutions are very clever but I was looking to an easier one.
After a little bit of trial and error I think I found a solution that it isn't very generalized but it's simple and it seems to work fine:
#include <stdlib.h>
#include <stdio.h>
#define ROWS <n of rows>
#define COLS <n of columns>
void file_to_matrix(int rows, int cols, char matrix[rows][cols], char file_path[]){
FILE *file = fopen(file_path, "r");
if(file==NULL){
printf("File error");
exit(1);
}
int y=0;
for(int x=0; (matrix[y][x]=getc(file))!=EOF; ++x){
if(matrix[y][x]=='\n'){
matrix[y][x]='\0';
++y;
x=-1;
}
}
fclose(file);
}
int main(){
char matrix[ROWS][COLS+2];
file_to_matrix(ROWS, COLS+2, matrix, "file path");
for(int i=0; i<=ROWS; ++i){
printf("%s\n", matrix[i]);
}
}
Thank you again for your answers

allocate and fill 2d-array in a function using mpi in C

Im tryin to read a ascii-file consisting of 2 columns of and a variable nr of rows. Reading is done by processor 0 and then the data is distributed using MPI_Bcast(...). I had to create a 1-dim buffer array which contains the data and is sent to all other procs because I haven't found a way to directly broadcast the 2-dim. data.
There's something wrong with the allocation of the array arr. When I try to print the data, the first 3 rows are printed and then I get a segmentation fault. Most likely there are several mistakes.
I really tried to make the example as simple as possible.
my code:
//global variables
...
double **testarray;
int dim;
...
int main(int argc, char **argv){
...
...
read_file("test.txt",&testarray,&dim);
}
int read_file(char* infilename,double ***arr,int *rowsout)
{
FILE* infile;
int i,j,ch,number_of_lines=0;
int rows=0;
//first count lines
if(myid==0) //myid is processor id
{
infile = fopen(infilename, "r");
do
{
ch = fgetc(infile);
if(ch == '\n')
number_of_lines++;
} while (ch != EOF);
if(ch != '\n' && number_of_lines != 0)
number_of_lines++;
//close file
fclose(infile);
rows=number_of_lines-1;
*rowsout=rows;
}
// every proc should know about length of file in order
//to be able to allocate memory
MPI_Bcast(rowsout,1,MPI_INT,0,MPI_COMM_WORLD);
//allocate memory
double *buf; //1D-buffer for 2D-array
MPI_Alloc_mem((*rowsout)*2*sizeof(double), MPI_INFO_NULL, &buf);
MPI_Alloc_mem((*rowsout)*sizeof(double*), MPI_INFO_NULL,arr);
for (i = 0; i < (*rowsout); i++) {
MPI_Alloc_mem(2*sizeof(double),MPI_INFO_NULL,&arr[i]);
}
// Now read file on proc 0
if(myid==0)
{
infile=fopen(infilename,"r");
for(i=0;i<rows;i++)
{
for(j=0;j<2;j++)
{
fscanf(infile,"%lf",arr[i][j]);
printf("arr[%d][%d]:%e\n",i,j,(*arr)[i][j]);
}
}
fclose(infile);
}
return 0;
//dont go further, error occurs before loop finishs
MPI_Bcast(buf,(rows)*2,MPI_DOUBLE,0,MPI_COMM_WORLD);
//now reconstruct array from buffer
for(i=0;i<(*rowsout);i++)
{
for(j=0;j<2;j++)
{
*arr[i][j]=buf[i*2+j];
}
}
MPI_Free_mem(buf);
return 0;
}
You have some issues with your arr as you guessed. Here is a fixed version of your code (minus the MPI stuff):
#include <stdio.h>
#include <stdlib.h>
void read_file(char *filename, double ***arr, int *rowsout);
int main(void)
{
double **testarray;
int dim;
read_file("test.txt", &testarray, &dim);
return 0;
}
void read_file(char *filename, double ***arr, int *rowsout)
{
FILE *infile;
int i, j, ch;
infile = fopen(filename, "r");
do
{
ch = fgetc(infile);
if (ch == '\n')
++*rowsout;
} while (ch != EOF);
rewind(infile);
*arr = malloc(sizeof **arr * *rowsout);
for (i = 0; i < *rowsout; ++i)
(*arr)[i] = malloc(sizeof ***arr * 2);
for (i = 0; i < *rowsout; ++i)
{
for (j = 0; j < 2; ++j)
{
fscanf(infile, "%lf", &(*arr)[i][j]);
printf("(*arr)[%d][%d]: %e\n", i, j, (*arr)[i][j]);
}
}
fclose(infile);
for (i = 0; i < *rowsout; ++i)
free((*arr)[i]);
free(*arr);
}
Example input: test.txt
0.01 0.02
0.3 0.4
5.0 6.0
Example output:
(*arr)[0][0]: 1.000000e-02
(*arr)[0][1]: 2.000000e-02
(*arr)[1][0]: 3.000000e-01
(*arr)[1][1]: 4.000000e-01
(*arr)[2][0]: 5.000000e+00
(*arr)[2][1]: 6.000000e+00
I believe the mistake comes with your fscanf() line. fscanf() wants a double * when you are using "%lf", but you are instead passing arr[i][j] and there's two things wrong with that: since arr is actually a pointer to your 2-D array, you will need to deference it first ((*arr)), and second, since it needs the address of the double, you will need to use &: &(*arr)[i][j].

how to search an element from a file in c

My code needs to do three things:
Read numbers from a file FILE1 into an array (dynamic)
Sort those numbers
Search for numbers input from a FILE2 in the sorted array.
.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main (int argc, char *argv[]) {
FILE *fp1 = fopen ("myFile1.txt", "r");
if (fp1 == NULL) {
printf ("cannot open this file");
exit (0);
}
FILE *fp2 = fopen ("test1.txt", "w");
if (fp2 == NULL) {
puts ("Not able to open this file");
exit (1);
}
int i = 0, num, j, k;
int *B = NULL;
int *C;
int a;
int size = 32;
B = malloc (sizeof (int) * size);
while (fscanf (fp1, "%d", &num) == 1) {
if (i < size) {
B[i] = num;
fprintf (fp2, "%d\r\n", num);
i++;
}
else {
C = malloc (sizeof (int) * 2 * size);
memcpy (C, B, size * sizeof (int));
free (B);
B = &C[0];
B[i] = num;
i++;
size = size * 2;
i++;
for (j = 0; j < size; ++j) {
for (k = j + 1; k < size; ++k) {
if (B[j] < B[k]) {
a = &B[j];
B[j] = B[k];
B[k] = a;
}
}
}
printf ("after sorting");
for (j = 0; j < size; ++j)
printf ("%d\n", B[j]);
}
}
return 0;
fclose (fp1); /* note this code is never reached */
fclose (fp2);
}
I successfully complete the first part of reading in the numbers from a file. But I am not able to understand how to sort these numbers.
I am trying to apply bubble sort, but it puts 0s in my array. How is my implementation incorrect?
& is the address-of operator. You pass it as a pointer. You need a = B[i], since a is an int.
Now you sort the numbers descending, if you want them to be ascending change the < to > in if (B[j] < B[k]).
Also you must always check whether malloc succeeded or not with e.g.:
if (!B) {
fprintf(stderr,"B alloc error");
exit(-1);
}
Also you might want to consider realloc.
In addition there is a built-in qsort in stdlib.h, which gives much better time than O(n^2).
Note: I haven't tested your file operations, since you said they work properly.

Utils.cpp function and getting prefilled array from text file

This file needs to read a text file with numbers on each line, and preload the values from the text file into myArray. It also needs to read the integer for arraySize, so that it can be used to calculate the mean and median. The code is in c, but the teacher uses c++ file names and doesn't want us t change it. I also want to total the numbers, but when executed it doesn't print anything.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "Utils.cpp"
int main(void)
{
//declare variables
int myArray[]={};
// declare a string to hold the name of the file
// that will be read into your statsArray
char statsFile[] = "2005.txt"; /* file to be read in */
int arraySize = getFileSize(statsFile); /* determine size of file */
//dynamically declare statsArray based on size of file
int *statsArray = (int*)malloc(sizeof(int)*arraySize);
// initialize arrays with zero values
initializeArray(myArray, arraySize);
//load array from file
loadArray(myArray, arraySize, statsFile);
// sum stats array
int total=0;
for(int x=0;x<myArray[arraySize-1];x++){
total+=myArray[x]; //adds each element of array to total
}
printf("%i",total);
Here is the utils.cpp that was provided.
#include <stdio.h>
int getFileSize(char fileName[])
{
FILE *fr; /* declare the file pointer */
int c; /*Nb int (not char) for the EOF */
int newlineCount = 0;
// open the file to count the lines
fr = fopen(fileName, "rt");
// count the newline characters
while ( (c=fgetc(fr)) != EOF)
{
if (c =='\n')
newlineCount++;
}
return newlineCount;
}
void loadArray(int list[], int size, char fileName[])
{
FILE *fr; // declare the file pointer
int count = 0; // running line count
char line[80];
fr = fopen(fileName, "rt");
while(fgets(line, 80, fr) != NULL)
{
// get a line, p to 80 chars fr. done if NULL
sscanf(line, "%i", &list[count]);
count++;
}
}
void initializeArray(int list[], int size)
{
for (int i = 0; i < size; i++)
{
list[i] = 0;
}
}
void sortArray(int list[], int size)
{
int tempNumber;
for (int i = 0; i < size; i++)
{
for (int j = i +1; j < size; j++)
{
if(list[i] > list[j])
{
tempNumber = list[i];
list[i] = list[j];
list[j] = tempNumber;
}
}
}
}

Segmentation fault (core dumped) c

My code is posted below, i'm pretty sure that the segmentation fault is happening in the while loop at the bottom of main().
My program has to read a file, the data is in a grid formation, the fscanf gets teh dimensions of the grid, then creates a 2D array with those dimensions, then i use a while loop to go through line by line and then a for loop to go through character by character in that line to store it in the inner part of the array, then i++ goes to store it in the next level of the 2D array.
can anyone tell me why its doing it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char direction;
int steps;
int cur[2];
char** gridArray;
int* move(int cur[2]);
int* getCommand(int next[2], char** gridArray);
char** make2DFileArray(int sizeX, int sizeY);
int* makeArray(int size);
int main(int argc, char *argv[] )
{
FILE *fp;
int rows;
int columns;
int i=0,j;
char line[1000];
if ((argc < 2)^(argc > 3)) {
fprintf(stderr, "Usage: thebox mapfile [maxsteps]\n");
exit(1);
}
if( argc < 3 ) { steps = 10; }
else { steps = argv[2]; }
fp = fopen(argv[1], "r");
if( fp == NULL) {
fprintf(stderr, "Missing map file.\n");
exit(1);
}
fscanf(fp, "%d %d", &rows, &columns);
gridArray = make2DFileArray(rows+1, columns);
while ( fgets(line, 1000, fp) != NULL) {
for(j=0; j < columns - 1; j++) {
gridArray[i][j] = line [j];
}
i++;
printf("%s", line);
}
fclose(fp);
printf("(sidepos)>\n");
return 0;
}
and my helper method, is this
char** make2DFileArray(int sizeX, int sizeY)
{
int i;
char** array;
array = (char**) malloc(sizeX*sizeof(char*));
for (i = 0; i < sizeX; i++) {
array[i] = (char*) malloc(sizeY*sizeof(char));
return array;
}
return array;
}
int* makeArray(int size)
{
int* array;
array = (int*) malloc(size*sizeof(int));
return array;
}
The error is in the make2DFileArray function. You just return after the first allocation in the loop, meaning only gridArray[0] will be allocated.
Since none of the other entries will be allocated, accessing e.g. gridArray[1] is undefined behavior and the cause of your crash.
This:
for (i = 0; i < sizeX; i++) {
array[i] = (char*) malloc(sizeY*sizeof(char));
return array;
}
return array;
... should be:
for (i = 0; i < sizeX; i++) {
array[i] = (char*) malloc(sizeY*sizeof(char));
}
return array;
... in your function make2DFileArray.
Remark: The element is allocated memory however the rest of the elements are not because of your return inside the for-loop. Each element in the array from [1 ... length - 1] are random memory addresses (because when you malloc you did not populate them as proper pointers) and hence dereferencing any element other than the one at position 0 will result in undefined behaviour.
steps = argv[2];
The variable 'steps' type is int, and variable 'argv[2]' type is char *, can't be directly assigned.
You can do it by
steps = atoi(argv[2]);

Resources