Output non registering data file in C

65 views Asked by At

I'm not understanding why this code in C doesn't give me an output other than the first printf, and I would like some help. I've already tried swapping the names to input and read the file where I'm getting the matrix but it hasn't worked, Thanks in advance! Let me know if you think I can optimize something inside this code, I will need it very soon for an exam.

#include <stdio.h>
#include <stdlib.h>

#define MIN_DIMENSION 5
#define MAX_DIMENSION 10
#define FILE_NAME "inputquattro.txt"

#include <stdio.h>
#include <stdlib.h>

#define MIN_DIMENSION 5
#define MAX_DIMENSION 10

typedef struct {
    char *inputquattro;
    int N;
    int M;
} InputParams;

InputParams readInput(int argc, char *argv[]) {
    InputParams params;

    if (argc != 4) {
        fprintf(stderr, "Usage: %s inputquattro.txt N M\n", argv[0]);
        exit(1);
    }

    params.inputquattro = argv[1];
    params.N = atoi(argv[2]);
    params.M = atoi(argv[3]);

    if (params.N < MIN_DIMENSION || params.N > MAX_DIMENSION || params.M < MIN_DIMENSION || params.M > MAX_DIMENSION) {
        fprintf(stderr, "Invalid dimensions. Be sure N and M are between %d and %d\n", MIN_DIMENSION, MAX_DIMENSION);
        exit(1);
    }

    return params;
}

double **allocateMatrix(int N, int M) {
    double **matrix = malloc(N * sizeof(double *));
    if (matrix == NULL) {
        fprintf(stderr, "Error during matrix memory allocation.\n");
        exit(1);
    }

    for (int i = 0; i < N; i++) {
        matrix[i] = malloc(M * sizeof(double));
        if (matrix[i] == NULL) {
            fprintf(stderr, "Errore during matrix memory allocation.\n");
            exit(1);
        }
    }

    return matrix;
}

void fillMatrix(InputParams params, double **matrix) {
    FILE *file = fopen(params.inputquattro, "r");
    if (file == NULL) {
        fprintf(stderr, "Impossible file opening. %s\n", params.inputquattro);
        exit(1);
    }

    for (int i = 0; i < params.N; i++) {
        for (int j = 0; j < params.M; j++) {
            if (fscanf(file, "%lf", &matrix[i][j]) != 1) {
                fprintf(stderr, "Reading file error.\n");
                fclose(file);
                exit(1);
            }
        }
    }

    fclose(file);
}

void printMatrix(int N, int M, double **matrix) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            printf("%lf ", matrix[i][j]);
        }
        printf("\n");
    }
}

void normalize(int N, int M, double **A, double **B) {
    double max_col[M];

    for (int j = 0; j < M; j++) {
        max_col[j] = A[0][j];
        for (int i = 1; i < N; i++) {
            if (A[i][j] > max_col[j]) {
                max_col[j] = A[i][j];
            }
        }
    }

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            B[i][j] = A[i][j] / max_col[j];
        }
    }
}

void insertionSort(double *arr, int size) {
    for (int i = 1; i < size; i++) {
        double key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j = j - 1;
        }
        arr[j + 1] = key;
    }
}

void sortMatrixCols(int N, int M, double **matrix) {
    for (int j = 0; j < M; j++) {
        double column[N];
        for (int i = 0; i < N; i++) {
            column[i] = matrix[i][j];
        }
        insertionSort(column, N);
        for (int i = 0; i < N; i++) {
            matrix[i][j] = column[i];
        }
    }
}

int main(int argc, char *argv[]) {
    InputParams params = readInput(argc, argv);

    double **A = allocateMatrix(params.N, params.M);
    double **B = allocateMatrix(params.N, params.M);

    fillMatrix(params, A);

    printf("Matrix A:\n");
    printMatrix(params.N, params.M, A);

    normalize(params.N, params.M, A, B);

    printf("\nMatrix B:\n");
    printMatrix(params.N, params.M, B);

    sortMatrixCols(params.N, params.M, B);

    printf("\nMatrice B sorted by columns:\n");
    printMatrix(params.N, params.M, B);

    // Free parameters allocated memory
    for (int i = 0; i < params.N; i++) {
        free(A[i]);
        free(B[i]);
    }
    free(A);
    free(B);

    return 0;
}
1

There are 1 answers

2
arfneto On

Considering the file now provided by the author in anoher answer

  12.3 1.2 6.5 7.8 9.8
  3.4 8.54 0.34 9.2 8.6
  1.2 45.3 1.2 3.4 5.6
  7.8 9.1234 6.123 21.345 7.234
  8.231 9.321 5.123 9.123  3.54
  1.34 3.2 5.6 0.2 12.3

and the provided code --- with some minimum changes in order to not use VLA

Stack Overflow: > ./v0-0209 inputquattro.txt 6 5
Matrice A:
12.300000 1.200000 6.500000 7.800000 9.800000
3.400000 8.540000 0.340000 9.200000 8.600000
1.200000 45.300000 1.200000 3.400000 5.600000
7.800000 9.123400 6.123000 21.345000 7.234000
8.231000 9.321000 5.123000 9.123000 3.540000
1.340000 3.200000 5.600000 0.200000 12.300000

Matrice B:
1.000000 0.026490 1.000000 0.365425 0.796748
0.276423 0.188521 0.052308 0.431014 0.699187
0.097561 1.000000 0.184615 0.159288 0.455285
0.634146 0.201400 0.942000 1.000000 0.588130
0.669187 0.205762 0.788154 0.427407 0.287805
0.108943 0.070640 0.861538 0.009370 1.000000

Matrice B ordinata per colonne:
0.097561 0.026490 0.052308 0.009370 0.287805
0.108943 0.070640 0.184615 0.159288 0.455285
0.276423 0.188521 0.788154 0.365425 0.588130
0.634146 0.201400 0.861538 0.427407 0.699187
0.669187 0.205762 0.942000 0.431014 0.796748
1.000000 1.000000 1.000000 1.000000 1.000000

It may or may not be ok.

the code considered

// https://stackoverflow.com/questions/77967209/output-non-registering-data-file-in-c
#define MIN_DIMENSION 5
#define MAX_DIMENSION 10
#define FILE_NAME "inputquattro.txt"

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    unsigned R;  // rows
    unsigned C;  // columns
    double** M;  // the matrix itself
} Matrix;

Matrix* so_allocate(unsigned R, unsigned C);
Matrix* so_fill(const char* input_file);
Matrix* so_normalize(Matrix* A);
int     so_print(Matrix* matrix, const char* msg);
int     so_sort_cols(Matrix* m);

typedef struct
{
    char* inputquattro;
    int   N;
    int   M;
} InputParams;

double**    allocateMatrix(int N, int M);
void        fillMatrix(InputParams params, double** matrix);
void        insertionSort(double* arr, int size);
void        normalize(int N, int M, double** A, double** B);
void        printMatrix(int N, int M, double** matrix);
InputParams readInput(int argc, char* argv[]);
void        sortMatrixCols(int N, int M, double** matrix);

int main(int argc, char* argv[])
{
    InputParams params = readInput(argc, argv);

    double** A = allocateMatrix(params.N, params.M);
    double** B = allocateMatrix(params.N, params.M);

    fillMatrix(params, A);

    printf("Matrice A:\n");
    printMatrix(params.N, params.M, A);

    normalize(params.N, params.M, A, B);

    printf("\nMatrice B:\n");
    printMatrix(params.N, params.M, B);

    sortMatrixCols(params.N, params.M, B);

    printf("\nMatrice B ordinata per colonne:\n");
    printMatrix(params.N, params.M, B);

    // Free parameters allocated memory
    for (int i = 0; i < params.N; i++)
    {
        free(A[i]);
        free(B[i]);
    }
    free(A);
    free(B);

    return 0;
}

double** allocateMatrix(int N, int M)
{
    double** matrix = malloc(N * sizeof(double*));
    if (matrix == NULL)
    {
        fprintf(
            stderr,
            "Errore durante l'allocazione della memoria "
            "per la matrice.\n");
        exit(1);
    }

    for (int i = 0; i < N; i++)
    {
        matrix[i] = malloc(M * sizeof(double));
        if (matrix[i] == NULL)
        {
            fprintf(
                stderr,
                "Errore durante l'allocazione della "
                "memoria per la matrice.\n");
            exit(1);
        }
    }

    return matrix;
}
void fillMatrix(InputParams params, double** matrix)
{
    FILE* file = fopen(params.inputquattro, "r");
    if (file == NULL)
    {
        fprintf(
            stderr, "Impossibile aprire il file %s\n",
            params.inputquattro);
        exit(1);
    }

    for (int i = 0; i < params.N; i++)
    {
        for (int j = 0; j < params.M; j++)
        {
            int res = fscanf(file, "%lf", &matrix[i][j]);
            if (res != 1)
            {
                fprintf(
                    stderr,
                    "Errore durante la lettura del "
                    "file. (%d,%d)\n",
                    i, j);
                fclose(file);
                exit(1);
            }
        }
    }

    fclose(file);
}

void insertionSort(double* arr, int size)
{
    for (int i = 1; i < size; i++)
    {
        double key = arr[i];
        int    j   = i - 1;
        while (j >= 0 && arr[j] > key)
        {
            arr[j + 1] = arr[j];
            j          = j - 1;
        }
        arr[j + 1] = key;
    }
}

void normalize(int N, int M, double** A, double** B)
{
    double max_col[MAX_DIMENSION];

    for (int j = 0; j < M; j++)
    {
        max_col[j] = A[0][j];
        for (int i = 1; i < N; i++)
        {
            if (A[i][j] > max_col[j])
            {
                max_col[j] = A[i][j];
            }
        }
    }

    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < M; j++)
        {
            B[i][j] = A[i][j] / max_col[j];
        }
    }
}

void printMatrix(int N, int M, double** matrix)
{
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < M; j++)
        {
            printf("%lf ", matrix[i][j]);
        }
        printf("\n");
    }
}

InputParams readInput(int argc, char* argv[])
{
    InputParams params;

    if (argc != 4)
    {
        fprintf(
            stderr, "Usage: %s inputquattro.txt N M\n",
            argv[0]);
        exit(1);
    }

    params.inputquattro = argv[1];
    params.N            = atoi(argv[2]);
    params.M            = atoi(argv[3]);

    if (params.N < MIN_DIMENSION ||
        params.N > MAX_DIMENSION ||
        params.M < MIN_DIMENSION ||
        params.M > MAX_DIMENSION)
    {
        fprintf(
            stderr,
            "Dimensioni non valide. Assicurati che N ed M "
            "siano compresi tra %d e %d.\n",
            MIN_DIMENSION, MAX_DIMENSION);
        exit(1);
    }

    return params;
}

void sortMatrixCols(int N, int M, double** matrix)
{
    for (int j = 0; j < M; j++)
    {
        double column[MAX_DIMENSION];
        for (int i = 0; i < N; i++)
        {
            column[i] = matrix[i][j];
        }
        insertionSort(column, N);
        for (int i = 0; i < N; i++)
        {
            matrix[i][j] = column[i];
        }
    }
}

Matrix* so_allocate(unsigned M, unsigned N) { return NULL; }

Matrix* so_fill(const char* input_file) { return NULL; }

Matrix* so_normalize(Matrix* A) { return NULL; }

int so_print(Matrix* matrix, const char* msg) { return 0; }

int so_sort_cols(Matrix* m) { return 0; }

why this code in C doesn't give me an output other than the first printf, and I would like some help. I've already tried swapping the names to input and read the file where I'm getting the matrix but it hasn't worked, Thanks in advance! Let me know if you think I can optimize something inside this code, I will need it very soon for an exam

I am not sure I understand what you say.

about the original code

why this?

#include <stdio.h>
#include <stdlib.h>

#define MIN_DIMENSION 5
#define MAX_DIMENSION 10
#define FILE_NAME "inputquattro.txt"

#include <stdio.h>
#include <stdlib.h>

#define MIN_DIMENSION 5
#define MAX_DIMENSION 10

typedef struct
{
    char* inputquattro;
    int   N;
    int   M;
} InputParams;

Considering the simple

#include <stdio.h>
#include <stdlib.h>

#define MIN_DIMENSION 5
#define MAX_DIMENSION 10
#define FILE_NAME "inputquattro.txt"

typedef struct
{
    char* inputquattro;
    int   N;
    int   M;
} InputParams;

and also this

InputParams readInput(int argc, char* argv[])
{
    InputParams params;

    if (argc != 4)
    {
        fprintf(
            stderr, "Usage: %s inputquattro.txt N M\n",
            argv[0]);
        exit(1);
    }

This InputParams thing is not useful. Why use this delegation of the input parameters parsing to another function? Why use this at all? It is used only in fillMatrix just to send the file name in.

use of VLA is problematic

This is not standard:


void normalize(int N, int M, double** A, double** B)
{
    double max_col[M];

Why not use the standard and simple

    double max_col[MAX_DIMENSION];

since the difference is 40 bytes at most?

this is your data

#define MIN_DIMENSION 5
#define MAX_DIMENSION 10
#define FILE_NAME "inputquattro.txt"

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    char* inputquattro;
    int   N;
    int   M;
} InputParams;

double**    allocateMatrix(int N, int M);
void        fillMatrix(InputParams params, double** matrix);
void        insertionSort(double* arr, int size);
void        normalize(int N, int M, double** A, double** B);
void        printMatrix(int N, int M, double** matrix);
InputParams readInput(int argc, char* argv[]);
void        sortMatrixCols(int N, int M, double** matrix);

int main(int argc, char* argv[])
{
//...

So do as usual and write this way. Use prototypes as above, let the code for the functions after main or in a separate file. It will make your life easier. And the life of others that read your code also easier.

where is the matrix?

It is clear that your code process a MxN array of doubles. But there is no such thing in your code. This is called abstraction and the one you use (almost none) is not good. You should have a Matrix thing.

where are the parameters?

You need the dimensions and the name of the input file. But in the code the dimensions come from the command line, but the data is in the file. And the file name is hardcoded after all. Makes little sense.

Why not pass just the file name in the command line, and inside the file put the dimensions in the first line? It is called encapsulation and SRP for single responsibility principle and it is a valuable thing.

avoid double** allocateMatrix(int N, int M);

On the same line, what you do need is to create a matrix based on data in a file. Build a model on that. As close as the model is from the problem the easier your code becomes to write, adapt and test. Return type** is problematic: you always need more information and there is no place for it. It is a pointer to a pointer. But can be a pointer to an array. Or an array of pointers to pointers. Use encapsulation and return a container.

why copy the data and then sort and copy back?

Insertion sort is an in_place algorithm. It is very strange to extract the data and then copy it back as in

void sortMatrixCols(int N, int M, double** matrix)
{
    for (int j = 0; j < M; j++)
    {
        double column[MAX_DIMENSION];
        for (int i = 0; i < N; i++)
        {
            column[i] = matrix[i][j];
        }
        insertionSort(column, N);
        for (int i = 0; i < N; i++)
        {
            matrix[i][j] = column[i];
        }
    }
}

Also note that you do not need all those braces.

But just sort the data where it is.

a possibly better approach

Here follows an example of what I am saying, and some arguments. I just copied your code into this model. It is a long post just because I am coding along with the text.

a Matrix thing

typedef struct
{
    unsigned R;  // rows
    unsigned C;  // columns
    double** mat;  // the matrix itself
} Matrix;

As you see, the dimensions are now part of the model, so we can redefine the operations using

    Matrix* so_allocate(unsigned R, unsigned C);
    Matrix* so_destroy(Matrix* m);
    Matrix* so_fill(const char* input_file);
    Matrix* so_normalize(Matrix* A);
    int     so_print(Matrix* matrix, const char* msg);
    int     so_sort_cols(Matrix* m);

a prototype v1.h header

#define MIN_DIMENSION 5
#define MAX_DIMENSION 10

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    unsigned R;  // rows
    unsigned C;  // columns
    double** mat;  // the matrix itself
} Matrix;

Matrix* so_allocate(unsigned R, unsigned C);
Matrix* so_destroy(Matrix* m);
Matrix* so_fill(const char* input_file);
Matrix* so_normalize(Matrix* A);
int     so_print(Matrix* matrix, const char* msg);
int     so_sort_cols(Matrix* m);

so_fill does just that and returns a pointer to the matrix. Encapsulation. Sure, as soon as so_fill gets the dimensions of the matrix it calls so_allocate to get a pointer to a new one. This is the single responsibility principle.

so_print does the expected, but accepts now an optional message, very handy in testing.

so_normalize returns a normalize version of the input Matrix.

so_destroy gets a pointer to a Matrix and erase it, freeing all memory and returning NULL. This is written now, as in RAII in C++: as we write the constructor we write the destructor and test both.

some code for these, in v1.c

#include "v1.h"

#include <stdio.h>
#include <stdlib.h>

Matrix* so_allocate(unsigned n_rows, unsigned n_cols)
{
    if ((n_rows < MIN_DIMENSION) ||
        (n_rows > MAX_DIMENSION))
        return NULL;
    if ((n_cols < MIN_DIMENSION) ||
        (n_cols > MAX_DIMENSION))
        return NULL;
    fprintf(
        stderr, "    so_allocate: %ux%u matrix\n", n_rows,
        n_cols);
    Matrix* M = malloc(sizeof(Matrix));
    if (M == NULL) return NULL;  // could not alloc
    M->R   = n_rows;
    M->C   = n_cols;
    M->mat = malloc(n_rows * sizeof(double*));
    if (M->mat == NULL)
    {
        free(M);
        return NULL;
    }
    unsigned i = 0;
    for (; i < n_rows; i += 1)
    {
        M->mat[i] = malloc(n_cols * sizeof(double));
        if (M->mat[i] == NULL)
        {  // error on i
            for (unsigned j = 0; j < i; j += 1)
            {
                free(M->mat[j]);
                free(M->mat);
                free(M);
                return NULL;
            }
        }
    }
    return M;
};  // so_allocate()

Matrix* so_destroy(Matrix* m)
{
    if (m == NULL) return NULL;
    fprintf(
        stderr, "    so_destroy: %ux%u matrix\n", m->R,
        m->C);
    for (unsigned i = 0; i < m->R; i += 1) free(m->mat[i]);
    free(m->mat);
    free(m);
    return NULL;
}

Matrix* so_fill(const char* input_file)
{
    FILE* file = fopen(input_file, "r");
    if (file == NULL)
    {
        fprintf(stderr, "Could not open %s\n", input_file);
        return NULL;
    }
    unsigned n_rows = 0;
    unsigned n_cols = 0;
    if (fscanf(file, "%d %d", &n_rows, &n_cols) != 2)
    {
        fprintf(
            stderr, "Could not read dimensions from %s\n",
            input_file);
        fclose(file);
        return NULL;
    }
    Matrix* my = so_allocate(n_rows, n_cols);
    for (unsigned i = 0; i < my->R; i++)
    {
        for (unsigned j = 0; j < my->C; j++)
        {
            if (fscanf(file, "%lf", &my->mat[i][j]) != 1)
            {
                fprintf(
                    stderr,
                    "Error reading element [%d,%d]"
                    " from %s\n",
                    i, j, input_file);
                fclose(file);
                so_destroy(my);
                return NULL;
            }
        }
    }
    fclose(file);
    return my;
}

Matrix* so_normalize(Matrix* A)
{
    double max_col[MAX_DIMENSION] = {0};
    if (A == NULL) return NULL;
    Matrix* B = so_allocate(A->R, A->C);
    if (B == NULL) return NULL;

    for (unsigned j = 0; j < A->C; j++)
    {
        max_col[j] = A->mat[0][j];
        for (unsigned i = 1; i < A->R; i++)
            if (A->mat[i][j] > max_col[j])
                max_col[j] = A->mat[i][j];
    }
    // here max_col[] holds the max value for each column
    for (unsigned i = 0; i < A->R; i++)
        for (unsigned j = 0; j < A->C; j++)
            B->mat[i][j] = A->mat[i][j] / max_col[j];
    return B;
}

int so_print(Matrix* matrix, const char* msg)
{
    if (matrix == NULL) return -1;
    if (msg == NULL)
        printf("Matrix [%ux%u]:\n\n", matrix->R, matrix->C);
    else
        printf(
            "%sMatrix [%ux%u]:\n\n", msg, matrix->R,
            matrix->C);

    for (unsigned i = 0; i < matrix->R; i++)
    {
        for (unsigned j = 0; j < matrix->C; j++)
        {
            printf("%12.4lf ", matrix->mat[i][j]);
        }
        printf("\n");
    }
    printf("\n\n");
    return 0;
}

int so_sort_cols(Matrix* m)
{
    double curr = 0.;  // current value starts with 1st
    int    j    = 0;
    if (m == NULL) return -1;
    for (unsigned col = 0; col < m->C; col++)
    {   // sort column 'col' by insertion
        for (int i = 1; i < (int) m->R; i++)
        {
            double curr = m->mat[i][col];
            j = i - 1;
            while (j >= 0)
            {
                if (m->mat[j][col] > curr) break;
                m->mat[j + 1][col] = m->mat[j][col];
                j                  = j - 1;
            }; // while
            m->mat[j + 1][col] = curr;
        }; // for
    }; // for
    return 0;
}

a main to test this

#include <stdio.h>
#include <stdlib.h>
#include "v1.h"

int main(int argc, char* argv[])
{
    Matrix* one =
        so_allocate(MAX_DIMENSION, MAX_DIMENSION - 1);
    one = so_destroy(one);  // del matrix and reset pointer
    return 0;
}

It does almost nothing, but we can run it already.

A matrix is created and then deleted.

output

    so_allocate: 10x9 matrix
    so_destroy: 10x9 matrix

It is good for moral support, at least ;).

a second step: building allocate

Matrix* so_fill(const char* input_file)

This is simple: gets a file name, returns a Matrix or NULL. And it is exactly what is in this example implementation:

Matrix* so_fill(const char* input_file)
{
    FILE* file = fopen(input_file, "r");
    if (file == NULL)
    {
        fprintf(stderr, "Could not open %s\n", input_file);
        return NULL;
    }
    unsigned n_rows = 0;
    unsigned n_cols = 0;
    if (fscanf(file, "%d %d", &n_rows, &n_cols) != 2)
    {
        fprintf(
            stderr, "Could not read dimensions from %s\n",
            input_file);
        fclose(file);
        return NULL;
    }
    Matrix* my = so_allocate(n_rows, n_cols);
    for (unsigned i = 0; i < my->R; i++)
    {
        for (unsigned j = 0; j < my->C; j++)
        {
            if (fscanf(file, "%lf", &my->mat[i][j]) != 1)
            {
                fprintf(
                    stderr,
                    "Error reading element [%d,%d]"
                    " from %s\n",
                    i, j, input_file);
                fclose(file);
                so_destroy(my);
                return NULL;
            }
        }
    }
    fclose(file);
    return my;
}

an example i4.txt

6 5
  12.3 1.2 6.5 7.8 9.8
  3.4 8.54 0.34 9.2 8.6
  1.2 45.3 1.2 3.4 5.6
  7.8 9.1234 6.123 21.345 7.234
  8.231 9.321 5.123 9.123  3.54
  1.34 3.2 5.6 0.2 12.3

And we have already print.

a main file for this test

#include <stdio.h>
#include <stdlib.h>
#include "v1.h"

int main(int argc, char* argv[])
{
    Matrix* one = so_fill("i4.txt");
    if ( one != NULL )
        fprintf(
            stderr, "    matrix dimensions: %ux%u\n",
            one->R, one->C);
    so_print(one, "\ntesting...\n");
    one = so_destroy(one);  // del matrix and reset pointer
    return 0;
}

So you see that is simpler to have main in a separate file. And using components makes it easier: so_fill calls so_create to get an empty matrix with the exact dimensions. so_destroy was already written and tested. so_print even accepts a mesage, so the code is easier to read.

output

    so_allocate: 6x5 matrix
    matrix dimensions: 6x5

testing...
Matrix [6x5]:

     12.3000       1.2000       6.5000       7.8000       9.8000
      3.4000       8.5400       0.3400       9.2000       8.6000
      1.2000      45.3000       1.2000       3.4000       5.6000
      7.8000       9.1234       6.1230      21.3450       7.2340
      8.2310       9.3210       5.1230       9.1230       3.5400
      1.3400       3.2000       5.6000       0.2000      12.3000


    so_destroy: 6x5 matrix

testing normalize and sort

It is convenient that normalize returns a new Matrix. This way we can normalize the original matrix and sort and print the result by writing

    Matrix* norm = so_normalize(one);
    so_print(norm, "\n(normalized)...\n");
    so_sort_cols(norm);
    so_print(norm, "\n(then sorted by columnn)...\n");

about sort

Note that the code below is almost the same you wrote. but sorts the data inside the matrix

int so_sort_cols(Matrix* m)
{
    double curr = 0.;  // current value starts with 1st
    int    j    = 0;
    if (m == NULL) return -1;
    for (unsigned col = 0; col < m->C; col++)
    {   // sort column 'col' by insertion
        for (int i = 1; i < (int) m->R; i++)
        {
            double curr = m->mat[i][col];
            j = i - 1;
            while (j >= 0)
            {
                if (m->mat[j][col] <= curr) break;
                m->mat[j + 1][col] = m->mat[j][col];
                j                  = j - 1;
            }; // while
            m->mat[j + 1][col] = curr;
        }; // for
    }; // for
    return 0;
}

testing all functions

#include <stdio.h>
#include <stdlib.h>

#include "v1.h"

int main(int argc, char* argv[])
{
    Matrix* one = so_fill("i4.txt");
    if (one != NULL)
        fprintf(
            stderr, "    matrix dimensions: %ux%u\n",
            one->R, one->C);
    so_print(one, "\ntesting...\n");
    // now normalize
    Matrix* norm = so_normalize(one);
    if (norm == NULL)
    {
        fprintf(stderr, "    Error on normalize\n");
        so_destroy(one);
        return -1;
    }
    so_print(norm, "\n(normalized)...\n");
    // now sort the new matrix
    so_sort_cols(norm);
    so_print(norm, "\n(then sorted by columnn)...\n");
    so_destroy(norm);
    so_destroy(one);
    return 0;
}

3rd test output

    so_allocate: 6x5 matrix
    matrix dimensions: 6x5

testing...
Matrix [6x5]:

     12.3000       1.2000       6.5000       7.8000       9.8000
      3.4000       8.5400       0.3400       9.2000       8.6000
      1.2000      45.3000       1.2000       3.4000       5.6000
      7.8000       9.1234       6.1230      21.3450       7.2340
      8.2310       9.3210       5.1230       9.1230       3.5400
      1.3400       3.2000       5.6000       0.2000      12.3000


    so_allocate: 6x5 matrix

(normalized)...
Matrix [6x5]:

      1.0000       0.0265       1.0000       0.3654       0.7967
      0.2764       0.1885       0.0523       0.4310       0.6992
      0.0976       1.0000       0.1846       0.1593       0.4553
      0.6341       0.2014       0.9420       1.0000       0.5881
      0.6692       0.2058       0.7882       0.4274       0.2878
      0.1089       0.0706       0.8615       0.0094       1.0000



(then sorted by columnn)...
Matrix [6x5]:

      0.0976       0.0265       0.0523       0.0094       0.2878
      0.1089       0.0706       0.1846       0.1593       0.4553
      0.2764       0.1885       0.7882       0.3654       0.5881
      0.6341       0.2014       0.8615       0.4274       0.6992
      0.6692       0.2058       0.9420       0.4310       0.7967
      1.0000       1.0000       1.0000       1.0000       1.0000


    so_destroy: 6x5 matrix
    so_destroy: 6x5 matrix

This is your code, just adapted to another structure. I am not focusing on the results and did not checked the algorithms. I Just wanted to show another way to write it, one that you can find more safe, readable and extensible. And it is a very common one.