Domanda Problema con puntatori in C

aspi

Utente Iron
8 Febbraio 2024
7
2
2
4
Ultima modifica:
Buon pomeriggio sono un nuovo utente del forum. Probabilmente sto sbagliando anche la trafila per porre una domanda. Avrei un problema in C. Dovrei assegnare NULL al puntatore di un singolo elemento di una matrice bidimensionale allocata dinamicamente (non ci riesco), e poi fare in modo di selezionare gli elementi con puntatore non NULL. Potreste aiutarmi. Vi ringrazio anticipatamente.
 
Il programma allochi una matrice A di puntatori a double di dimensioni n x m.
Elimini dalla matrice i valori di A che sono inferiori ai valori medi delle righe corrispondenti (i corrispondenti puntatori dovranno puntare a NULL con conseguente liberazione della memoria occupata dai corrispondenti double);
Ho inserito in grassetto la parte che non so come fare.
Grazie a chi mi volesse aiutare
 
Ciao, inizia a postare il codice che hai scritto finora, così possiamo capire il contesto e darti un aiuto più mirato.

Quoto. A parte che non si capisce cosa vuoi fare posta il codice almeno della parte che hai scritto così capiamo di che cosa si parla. La struttura dati richiesta non è chiara, una matrice di double in C la dichiareresti double matrice[n][m]; o double (*matrice)[m]; ma se vuoi allocarla dinamicamente con i puntatori, liberare singole righe e impostarle a NULL è un'altra cosa esempio double** matrice;
 
Ultima modifica da un moderatore:
Ecco l'intero esercizio. Posto anche il codice fino al punto B. Non riesco a svolgere il punto C. Grazie per l'interessamento

Scrivere un programma in C che:
● A. Prenda come argomenti da riga di comando il nome IN di un file di testo di input (es. “input.txt”) e il nome OUT di un file di testo di output (es. “output.txt”). Si assuma che i nomi dei file non superino i 255 caratteri.
● B. Si assuma che il file di input contenga un numero n di righe di testo e che ogni riga contenga un numero m di valori in virgola mobile separati da spazi; il programma allochi una matrice di puntatori a double di dimensioni n x m, che punteranno ai numeri contenuti nel file di input;
● C. Elimini dalla matrice i valori di A che sono inferiori ai valori medi delle righe corrispondenti (i corrispondenti puntatori dovranno puntare a NULL con conseguente liberazione della memoria occupata dai corrispondenti double);
● D Inserisca i valori puntati dai puntatori non nulli in A in un array di double;
● E scriva i valori dell’array sul file di output il cui nome e’ stato specificato a linea di comando.
Specifiche
Il programma potrà essere articolato in un unico file sorgente, ma dovrà contenere almeno le seguenti funzioni con opportuni parametri formali:
● readParameters: funzione che permetta di leggere i parametri da riga di comando, verificando che rispettino i criteri specificati nel punto A, gestendo eventuali messaggi di errore e terminando il programma ove opportuno;
● createMatrix: funzione che permetta di creare la matrice A di puntatori a double a partire dal nome del file di input;
● sparsify: funzione che permetta di mettere a NULL gli opportuni puntatori di A (come da punto C);
● collect: funzione che permetta di creare e riempire l’array di double come descritto nel punto D;
● writeToFile: funzione che permetta di scrivere gli elementi dell’array su file.

C:
#include<stdio.h>
#include<limits.h>
#include<string.h>
#include<stdlib.h>
#define N 6
#define M 5

struct nomi_file{
    char IN[256];
    char OUT[256];
} nomi;

int readParameters(){
    printf("\nInserire nome file di input: ");
    scanf("%s", nomi.IN);
    unsigned x = strlen(nomi.IN);
    if(x > 255 || x < 5)
        return -1;
    else if(nomi.IN[x-4] != '.' || nomi.IN[x-3] != 't' || nomi.IN[x-2] != 'x' || nomi.IN[x-1] != 't'){
        printf("\n%d - %c%c%c%c", x, nomi.IN[x-4], nomi.IN[x-3], nomi.IN[x-2], nomi.IN[x-1]);
        printf("\nChiudo");
        return -1;
    }   
    printf("\nInserire nome file di output: ");
    scanf("%s", nomi.OUT);
    x = strlen(nomi.OUT);
    if(x > 255 || x < 5)
        return -1;
    else if(nomi.OUT[x-4] != '.' || nomi.OUT[x-3] != 't' || nomi.OUT[x-2] != 'x' || nomi.OUT[x-1] != 't'){
        printf("\n%d - %c%c%c%c", x, nomi.IN[x-4], nomi.IN[x-3], nomi.IN[x-2], nomi.IN[x-1]);
        printf("\nChiudo");
        return -1;
    }   

    return 0;
}

double *createMatrix(char *input, int n){
    char *array[50];
    FILE *fp = fopen(input, "r");
    if(!fp){
    fprintf(stderr, "\nErrore nell'apertura del file %s", input);
    perror("");
    }
        for(unsigned short i = 0; i<n; i++){
            fscanf(fp, "%s", array[1]);
            printf("\n%s", array);
        }
return array;
}

int main(){
    readParameters();
    createMatrix(nomi.IN, N);
    
}
 
Non sono sicuro di aver capito del punto C alla media tra cosa si riferisce. Comunque sia la funzione createMatrix dovrà ritornare un double** allocato tramite malloc perché in base alla consegna dovrai liberare dalla memoria certe righe in base a dei calcoli e assegnarle a NULL mentre le matrici classiche sono continue in memoria e non puoi "rimuovere" una riga dal mezzo senza costruire una nuova matrice.

Nel tuo codice hai usato char *array[50];, che è un array di 50 stringhe non inizializzato, per leggere il file ma non va bene: devi convertire ogni stringa a virgola mobile separata da spazio in un double e inserirlo al posto giusto nella matrice (puoi farlo con fscanf e formato %lf, oppure con strtod come ho fatto io). Ti faccio un esempio funzionante per questo step, vedrai che sarà più chiaro anche per gli step successivi.

C:
// Questa macro serve a semplificare la gestione degli errori, stampando il messaggio, chiudendo il file e liberando la memoria
#define ERRORE(str, ...) do { fprintf(stderr, str "\n", __VA_ARGS__); failure = 1; goto cleanup; } while (0)

double** createMatrix(const char* inputFile)
{
    char line[256];
    int len, ncol, nline = 0, failure = 0;
    double d;
    FILE* fp = NULL;
    char *end, *p;
    double** matrix = (double**)malloc(N * sizeof(double*));
    if (!matrix)
        ERRORE("Memoria insufficiente");
    
    fp = fopen(inputFile, "r");
    if (!fp)
        ERRORE("Errore nell'apertura del file %s", inputFile);

    while (!feof(fp))
    {
        fgets(line, sizeof(line), fp);
        len = strlen(line);
        if (len < 2)
            continue;

        ncol = 0;
        matrix[nline] = (double*)malloc(M * sizeof(double));
        if (!matrix[nline])
            ERRORE("Memoria insufficiente");
        
        p = line;
        while (ncol < M)
        {
            d = strtod(p, &end);
            if (p == end)
                break;

            if (errno == ERANGE)
                ERRORE("Errore lettura valore a linea %d", nline + 1);

            matrix[nline][ncol++] = d;
            p = end;
        }
        if (ncol != M)
            ERRORE("Valori mancanti a linea %d", nline + 1);
        
        nline++;
    }

#undef ERRORE
cleanup:

    if (fp)
        fclose(fp);

    if (!failure && nline != N)
    {
        fprintf(stderr, "Le linee sono %d invece che %d\n", nline, N);
        failure = 1;
    }

    if (failure && matrix)
    {
        for (int i = 0; i < nline; i++)
            free(matrix[i]);

        free(matrix);
        matrix = NULL;
    }

    return matrix;
}

A questo punto liberare la riga X è uno scherzo: free(matrix[X]); matrix[X] = NULL;
 
Non sono sicuro di aver capito del punto C alla media tra cosa si riferisce. Comunque sia la funzione createMatrix dovrà ritornare un double** allocato tramite malloc perché in base alla consegna dovrai liberare dalla memoria certe righe in base a dei calcoli e assegnarle a NULL mentre le matrici classiche sono continue in memoria e non puoi "rimuovere" una riga dal mezzo senza costruire una nuova matrice.

Nel tuo codice hai usato char *array[50];, che è un array di 50 stringhe non inizializzato, per leggere il file ma non va bene: devi convertire ogni stringa a virgola mobile separata da spazio in un double e inserirlo al posto giusto nella matrice (puoi farlo con fscanf e formato %lf, oppure con strtod come ho fatto io). Ti faccio un esempio funzionante per questo step, vedrai che sarà più chiaro anche per gli step successivi.

C:
// Questa macro serve a semplificare la gestione degli errori, stampando il messaggio, chiudendo il file e liberando la memoria
#define ERRORE(str, ...) do { fprintf(stderr, str "\n", __VA_ARGS__); failure = 1; goto cleanup; } while (0)

double** createMatrix(const char* inputFile)
{
    char line[256];
    int len, ncol, nline = 0, failure = 0;
    double d;
    FILE* fp = NULL;
    char *end, *p;
    double** matrix = (double**)malloc(N * sizeof(double*));
    if (!matrix)
        ERRORE("Memoria insufficiente");
   
    fp = fopen(inputFile, "r");
    if (!fp)
        ERRORE("Errore nell'apertura del file %s", inputFile);

    while (!feof(fp))
    {
        fgets(line, sizeof(line), fp);
        len = strlen(line);
        if (len < 2)
            continue;

        ncol = 0;
        matrix[nline] = (double*)malloc(M * sizeof(double));
        if (!matrix[nline])
            ERRORE("Memoria insufficiente");
       
        p = line;
        while (ncol < M)
        {
            d = strtod(p, &end);
            if (p == end)
                break;

            if (errno == ERANGE)
                ERRORE("Errore lettura valore a linea %d", nline + 1);

            matrix[nline][ncol++] = d;
            p = end;
        }
        if (ncol != M)
            ERRORE("Valori mancanti a linea %d", nline + 1);
       
        nline++;
    }

#undef ERRORE
cleanup:

    if (fp)
        fclose(fp);

    if (!failure && nline != N)
    {
        fprintf(stderr, "Le linee sono %d invece che %d\n", nline, N);
        failure = 1;
    }

    if (failure && matrix)
    {
        for (int i = 0; i < nline; i++)
            free(matrix[i]);

        free(matrix);
        matrix = NULL;
    }

    return matrix;
}

A questo punto liberare la riga X è uno scherzo: free(matrix[X]); matrix[X] = NULL;
Grazie gentilissimo. Adesso leggo con attenzione il tuo codice
 
Non sono sicuro di aver capito del punto C alla media tra cosa si riferisce. Comunque sia la funzione createMatrix dovrà ritornare un double** allocato tramite malloc perché in base alla consegna dovrai liberare dalla memoria certe righe in base a dei calcoli e assegnarle a NULL mentre le matrici classiche sono continue in memoria e non puoi "rimuovere" una riga dal mezzo senza costruire una nuova matrice.

Nel tuo codice hai usato char *array[50];, che è un array di 50 stringhe non inizializzato, per leggere il file ma non va bene: devi convertire ogni stringa a virgola mobile separata da spazio in un double e inserirlo al posto giusto nella matrice (puoi farlo con fscanf e formato %lf, oppure con strtod come ho fatto io). Ti faccio un esempio funzionante per questo step, vedrai che sarà più chiaro anche per gli step successivi.

C:
// Questa macro serve a semplificare la gestione degli errori, stampando il messaggio, chiudendo il file e liberando la memoria
#define ERRORE(str, ...) do { fprintf(stderr, str "\n", __VA_ARGS__); failure = 1; goto cleanup; } while (0)

double** createMatrix(const char* inputFile)
{
    char line[256];
    int len, ncol, nline = 0, failure = 0;
    double d;
    FILE* fp = NULL;
    char *end, *p;
    double** matrix = (double**)malloc(N * sizeof(double*));
    if (!matrix)
        ERRORE("Memoria insufficiente");
  
    fp = fopen(inputFile, "r");
    if (!fp)
        ERRORE("Errore nell'apertura del file %s", inputFile);

    while (!feof(fp))
    {
        fgets(line, sizeof(line), fp);
        len = strlen(line);
        if (len < 2)
            continue;

        ncol = 0;
        matrix[nline] = (double*)malloc(M * sizeof(double));
        if (!matrix[nline])
            ERRORE("Memoria insufficiente");
      
        p = line;
        while (ncol < M)
        {
            d = strtod(p, &end);
            if (p == end)
                break;

            if (errno == ERANGE)
                ERRORE("Errore lettura valore a linea %d", nline + 1);

            matrix[nline][ncol++] = d;
            p = end;
        }
        if (ncol != M)
            ERRORE("Valori mancanti a linea %d", nline + 1);
      
        nline++;
    }

#undef ERRORE
cleanup:

    if (fp)
        fclose(fp);

    if (!failure && nline != N)
    {
        fprintf(stderr, "Le linee sono %d invece che %d\n", nline, N);
        failure = 1;
    }

    if (failure && matrix)
    {
        for (int i = 0; i < nline; i++)
            free(matrix[i]);

        free(matrix);
        matrix = NULL;
    }

    return matrix;
}

A questo punto liberare la riga X è uno scherzo: free(matrix[X]); matrix[X] = NULL;
Ti ringrazio nuovamente, ma da quel che capisco dal codice viene liberata tutta la riga della matrice, ma il testo chiede che venga impostato a NULL il singolo puntatore all'elemento, gli altri elementi della riga debbono rimanere disponibili
 
Ti ringrazio nuovamente, ma da quel che capisco dal codice viene liberata tutta la riga della matrice, ma il testo chiede che venga impostato a NULL il singolo puntatore all'elemento, gli altri elementi della riga debbono rimanere disponibili

Hai ragione, ho letto il testo velocemente e avevo capito fosse necessario liberare righe e non singoli valori anche perché ha ancora meno senso questa struttura dati (visto che poi copi comunque tutto in un array) ma capisco che è solo un esercizio didattico. Per fare questa matrice "con i buchi" che puoi liberarne singoli elementi puoi trasformare la matrice in double*** e accedere ai valori con *matrix[X][Y] e liberarli con free(matrix[X][Y]); matrix[X][Y] = NULL;
 
Hai ragione, ho letto il testo velocemente e avevo capito fosse necessario liberare righe e non singoli valori anche perché ha ancora meno senso questa struttura dati (visto che poi copi comunque tutto in un array) ma capisco che è solo un esercizio didattico. Per fare questa matrice "con i buchi" che puoi liberarne singoli elementi puoi trasformare la matrice in double*** e accedere ai valori con *matrix[X][Y] e liberarli con free(matrix[X][Y]); matrix[X][Y] = NULL;
Grazie, mi sei stato molto utile. Grazie per il tempo che mi hai dedicato