我正在做一个学校项目,我必须将PPM数据存储到结构中。结构中的字符串数组有问题。

typedef struct {
     char **comments;
} PPM;

我有3个函数使用这个结构。
PPM*getPPM()用于从文件中获取所有PPM数据并将其存储到结构中
void show PPM(PPM*img)在终端显示图像数据
PPM*编码(PPM*img),用于更改图像RGB值的LSB
问题是getPPM按预期工作,并将所有注释放入getPPM中的comments数组中。如果我这样做,它会很好地显示它们:
PPM *p = getPPM(fin);
showPPM(p);

但是如果我试图用如下的encode函数调用它:
PPM *p = getPPM(fin);
PPM *g = encode(p);
showPPM(g);

调试器显示,只要程序进入encode函数,comments值就会重置为NULL,即使此函数甚至不接触comments。我调用这些函数的方式是错误的还是我的代码有问题?如果问题不是函数被调用的方式,我将尝试提供最小的代码,因为代码很大,并且所有的东西都相互依赖。
我对C语言很陌生。我试着理解这个问题好几个小时了,但哪儿也找不到解决办法。任何帮助都将不胜感激。
编辑:这是我能做的最小的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Structures
typedef struct {
    int r, g, b;
} pixels;

typedef struct {
    char format[3];
    char **comments;
    int width, height, maxColors, commentCounter;
    pixels **pixelValues;
} PPM;
// Functions declarations
PPM *getPPM(FILE * f);
PPM *encode(PPM *im, char *message, unsigned int mSize, unsigned int secret);
void showPPM(PPM * im);
static int *decimalToBinary(const char *message, unsigned int length);
// Main method
int main(int argc, char **argv) {

    FILE * fin = fopen(argv[1], "r");

    if(fin == NULL) {
        perror("Cannot open file");
        exit(1);
    }

    PPM *p = getPPM(fin);
    PPM *g = encode(p, "test", 5, 1);
    showPPM(g);

    return 0;
}
/*
 * This function is used to get the image data from a file and populate
 * our strutures with it.
 */
PPM *getPPM(FILE * f) {

    // Allocate the memory for structure and check if it was successful
    PPM *pic = (PPM *) malloc(sizeof(PPM));

    if(!pic) {
        perror("Unable to allocate memory for structure");
        exit(1);
    }

    char line[100]; // Expecting no more than 100 characters per line.
    pic->commentCounter = 0; // This is the counter to keep size if there are more than one comments
    int pixelsCounter = 0; // Counter for pixels' array
    pic->comments = malloc(sizeof(char *));
    pic->pixelValues = malloc(sizeof(PPM));
    int lineCounter = 0;

    if((pic->comments) == NULL) {
        perror("Unable to allocate memory for pixels");
        exit(1);
    }

    while(fgets(line, sizeof(line), f)) {
        // Reference: https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input
        size_t length = strlen(line);

        if(length > 0 && line[length-1] == '\n') {
            line[--length] = '\0';
        }
        // Assigning the file format
        if(line[0] == 'P') {
            pic->format[0] = line[0];
            pic->format[1] = line[1];
            pic->format[2] = '\0';
        }
        //Populate comments into struct PPM
        if(line[0] == '#') {
            // Reallocate/allocate the array size each time a new line of comment is found
            if(pic->commentCounter != 0) {
                pic->comments = realloc(pic->comments, (pic->commentCounter+1) * sizeof(char *));
            }
            // Allocate the memory for the string
            pic->comments[pic->commentCounter] = malloc(100 * sizeof(char));
            // Write the at commentCounter position of the array; character by character
            int i = 0;
            while(line[i] != '\0') {
                pic->comments[pic->commentCounter][i] = line[i];
                i++;
            }
            pic->comments[pic->commentCounter][i] = '\0';
            pic->commentCounter++;
        }
        /*
         * Loading the max color property of the file which is gonna be 3 letters (Usually 255)
         * and checking if we previously got maxColors in our construct or not. If we didn't
         * then we load this value into the consturct and the condition will never validate
         * throughout the iterations
         */
        if(strlen(line) == 3 && pic->maxColors == 0 && line[0] != '#') {
            pic->maxColors = atoi(line);
            continue;

        }
        /*
         * Check if the length of string > 3, which means it is going to be a
         * number, potentially RGB value or a comment. But since width & height
         * comes before RGB values, our condition will fail once we have found
         * the width/height for the next iteration. That's why this condition
         * only checks if it is a comment or a numbered value of length > 3
         */
        if((strlen(line) > 3) && (pic->width == 0) && (line[0] != '#')) {
            char *width = strtok(line, " ");
            char *height = strtok(NULL, " ");

            pic->width = atoi(width);
            pic->height = atoi(height);
            continue;
        }
        /*
         * If the width/height and maxColors have been found, that means every
         * other line is either going to be the RGB values or a comment.
         */
        if((pic->width != 0) && (pic->maxColors != 0) && (line[0] != '#')) {
            // length(line) > 3 means all the RGB values are in same line
            if(strlen(line) > 3) {
                char *val1 = strtok(line, " ");
                char *val2 = strtok(NULL, " ");
                char *val3 = strtok(NULL, " ");
                // pixelsCounter = 0 means it's the first element.
                if(pixelsCounter != 0) {
                    // Reallocate memory each time a new R G B value line is found
                    pic->pixelValues = realloc(pic->pixelValues, (pixelsCounter + 1) * sizeof(PPM));
                }

                pic->pixelValues[pixelsCounter] = malloc(12 * sizeof(pixels));
                pic->pixelValues[pixelsCounter]->r = atoi(val1);
                pic->pixelValues[pixelsCounter]->g = atoi(val2);
                pic->pixelValues[pixelsCounter]->b = atoi(val3);
                pixelsCounter++;
            } else if(strlen(line) <= 3) {
                /*
                 * If each individual RGB values are in a separete lines, we will
                 * use a switch case and a line counter to keep track of where the
                 * values were inserted and when to know when we got RGB values for
                 * one pixel
                 */
                if(pixelsCounter != 0 && lineCounter == 0) {
                    // Reallocate memory each time a new R G B value line is found
                    pic->pixelValues = realloc(pic->pixelValues, (pixelsCounter + 1) * sizeof(PPM));
                }

                switch(lineCounter) {
                    case 0 :
                        pic->pixelValues[pixelsCounter] = malloc(12 * sizeof(pixels));
                        pic->pixelValues[pixelsCounter]->r = atoi(line);
                        lineCounter++;
                        continue;
                    case 1 :
                        pic->pixelValues[pixelsCounter]->g = atoi(line);
                        lineCounter++;
                        continue;
                    case 2 :
                        pic->pixelValues[pixelsCounter]->b = atoi(line);
                        lineCounter=0;
                        pixelsCounter++;
                        continue;
                    default:
                        continue;
                }
            }
        }
    }
    pic->pixelValues[pixelsCounter] = NULL;
    fclose(f);
    return pic;
}

void showPPM(PPM * im) {

    printf("%s\n",im->format);
    int k = 0;
    while(k < im->commentCounter) {
        printf("%s\n", im->comments[k]);
        k++;
    }

    printf("%d %d\n", im->width, im->height);
    printf("%d\n",im->maxColors);

    int j = 0;
    while(im->pixelValues[j] != NULL) {
        printf("%d %d %d\n", im->pixelValues[j]->r, im->pixelValues[j]->g, im->pixelValues[j]->b);
        j++;
    }
}


PPM *encode(PPM *im, char *message, unsigned int mSize, unsigned int secret) {

    int *binaryMessage = decimalToBinary(message, mSize);
    int i, j = 0, lineCounter = 0;

    for(i = 0; i < 40; i++) {
        switch(lineCounter) {
            case 0 :
                im->pixelValues[j]->r |= binaryMessage[i] << 0;
                lineCounter++;
                continue;
            case 1 :
                im->pixelValues[j]->g |= binaryMessage[i] << 0;
                lineCounter++;
                continue;
            case 2 :
                im->pixelValues[j]->b |= binaryMessage[i] << 0;
                lineCounter=0;
                j++;
                continue;
            default:
                continue;
        }

    }
    return im;
}
/*
 * Converts a string into binary to be used in encode function. It
 * first converts each letter of the string into ascii code. Then
 * finds and stores each of the 8 bits of that int (ascii code of
 * the letter) sequentially in an array.
 */
static int *decimalToBinary(const char *message, unsigned int length) {
    /*
     * malloc is used here instead of [] notation to allocate memory,
     * because defining the variable with [] will make its scope
     * limited to this function only. Since we want to access this
     * array later on, we use malloc to assign space in the memory
     * for it so we can access it using a pointer later on.
     */
    int k=0, i, j;
    unsigned int c;
    unsigned int *binary = malloc(8 * length);

    for(i = 0; i < length; i++) {
        c = message[i];
        for(j = 7; j >= 0; j--,k++) {
            /*
             * We check here if the jth bit of the number is 1 or 0
             * using the bit operator &. If it is 1, it will return
             * 1 because 1 & 1 will be true. Otherwise 0.
             */
            if((c >> j) & 1)
                binary[k] = 1;
            else
                binary[k] = 0;
        }
    }
    return binary;
}

PPM文件:
P3
# CREATOR: GIMP PNM Filter Version 1.1
# Amazing comment 2
# Yet another amazing comment
400 530
255
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0

最佳答案

在代米尔塔比纳

unsigned int *binary = malloc(8 * length);

必须是
unsigned int *binary = malloc(8 * length * sizeof(int));

新代码是:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Structures
typedef struct {
    int r, g, b;
} pixels;

typedef struct {
    char format[3];
    char **comments;
    int width, height, maxColors, commentCounter;
    pixels **pixelValues;
} PPM;
// Functions declarations
PPM *getPPM(FILE * f);
PPM *encode(PPM *im, char *message, unsigned int mSize, unsigned int secret);
void showPPM(PPM * im);
static int *decimalToBinary(const char *message, unsigned int length);
// Main method
int main(int argc, char **argv) {

    FILE * fin = fopen(argv[1], "r");

    if(fin == NULL) {
        perror("Cannot open file");
        exit(1);
    }

    PPM *p = getPPM(fin);
    PPM *g = encode(p, "test", 5, 1);
    showPPM(g);

    free(p->comments);
    free(p);

    return 0;
}
/*
 * This function is used to get the image data from a file and populate
 * our strutures with it.
 */
PPM *getPPM(FILE * f) {

    // Allocate the memory for structure and check if it was successful
    PPM *pic = (PPM *) malloc(sizeof(PPM));

    if(!pic) {
        perror("Unable to allocate memory for structure");
        exit(1);
    }

    char line[100]; // Expecting no more than 100 characters per line.
    pic->commentCounter = 0; // This is the counter to keep size if there are more than one comments
    int pixelsCounter = 0; // Counter for pixels' array
    pic->comments = malloc(sizeof(char *));
    pic->pixelValues = malloc(sizeof(PPM));
    int lineCounter = 0;

    if((pic->comments) == NULL) {
        perror("Unable to allocate memory for pixels");
        exit(1);
    }
    pic->width = 0;
    pic->height = 0;
    pic->maxColors = 0;
    while(fgets(line, sizeof(line), f)) {
        // Reference: https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input
        size_t length = strlen(line);

        if(length > 0 && line[length-1] == '\n') {
            line[--length] = '\0';
        }
        // Assigning the file format
        if(line[0] == 'P') {
            pic->format[0] = line[0];
            pic->format[1] = line[1];
            pic->format[2] = '\0';
        }
        //Populate comments into struct PPM
        if(line[0] == '#') {
            // Reallocate/allocate the array size each time a new line of comment is found
            if(pic->commentCounter != 0) {
                pic->comments = realloc(pic->comments, (pic->commentCounter+1) * sizeof(char *));
            }
            // Allocate the memory for the string
            pic->comments[pic->commentCounter] = malloc(100 * sizeof(char));
            // Write the at commentCounter position of the array; character by character
            int i = 0;
            while(line[i] != '\0') {
                pic->comments[pic->commentCounter][i] = line[i];
                i++;
            }
            pic->comments[pic->commentCounter][i] = '\0';
            pic->commentCounter++;
        }
        /*
         * Loading the max color property of the file which is gonna be 3 letters (Usually 255)
         * and checking if we previously got maxColors in our construct or not. If we didn't
         * then we load this value into the consturct and the condition will never validate
         * throughout the iterations
         */
        if(strlen(line) == 3 && pic->maxColors == 0 && line[0] != '#') {
            pic->maxColors = atoi(line);
            continue;

        }
        /*
         * Check if the length of string > 3, which means it is going to be a
         * number, potentially RGB value or a comment. But since width & height
         * comes before RGB values, our condition will fail once we have found
         * the width/height for the next iteration. That's why this condition
         * only checks if it is a comment or a numbered value of length > 3
         */
        if((strlen(line) > 3) && (pic->width == 0) && (line[0] != '#')) {
            char *width = strtok(line, " ");
            char *height = strtok(NULL, " ");

            pic->width = atoi(width);
            pic->height = atoi(height);
            continue;
        }
        /*
         * If the width/height and maxColors have been found, that means every
         * other line is either going to be the RGB values or a comment.
         */
        if((pic->width != 0) && (pic->maxColors != 0) && (line[0] != '#')) {
            // length(line) > 3 means all the RGB values are in same line
            if(strlen(line) > 3) {
                char *val1 = strtok(line, " ");
                char *val2 = strtok(NULL, " ");
                char *val3 = strtok(NULL, " ");
                // pixelsCounter = 0 means it's the first element.
                if(pixelsCounter != 0) {
                    // Reallocate memory each time a new R G B value line is found
                    pic->pixelValues = realloc(pic->pixelValues, (pixelsCounter + 1) * sizeof(PPM));
                }

                pic->pixelValues[pixelsCounter] = malloc(12 * sizeof(pixels));
                pic->pixelValues[pixelsCounter]->r = atoi(val1);
                pic->pixelValues[pixelsCounter]->g = atoi(val2);
                pic->pixelValues[pixelsCounter]->b = atoi(val3);
                pixelsCounter++;
            } else if(strlen(line) <= 3) {
                /*
                 * If each individual RGB values are in a separete lines, we will
                 * use a switch case and a line counter to keep track of where the
                 * values were inserted and when to know when we got RGB values for
                 * one pixel
                 */
                if(pixelsCounter != 0 && lineCounter == 0) {
                    // Reallocate memory each time a new R G B value line is found
                    pic->pixelValues = realloc(pic->pixelValues, (pixelsCounter + 1) * sizeof(PPM));
                }

                switch(lineCounter) {
                    case 0 :
                        pic->pixelValues[pixelsCounter] = malloc(12 * sizeof(pixels));
                        pic->pixelValues[pixelsCounter]->r = atoi(line);
                        lineCounter++;
                        continue;
                    case 1 :
                        pic->pixelValues[pixelsCounter]->g = atoi(line);
                        lineCounter++;
                        continue;
                    case 2 :
                        pic->pixelValues[pixelsCounter]->b = atoi(line);
                        lineCounter=0;
                        pixelsCounter++;
                        continue;
                    default:
                        continue;
                }
            }
        }
    }
    pic->pixelValues[pixelsCounter] = NULL;
    fclose(f);
    return pic;
}

void showPPM(PPM * im) {

    printf("%s\n",im->format);
    int k = 0;
    while(k < im->commentCounter) {
        printf("%s\n", im->comments[k]);
        k++;
    }

    printf("%d %d\n", im->width, im->height);
    printf("%d\n",im->maxColors);

    int j = 0;
    while(im->pixelValues[j] != NULL) {
        printf("%d %d %d\n", im->pixelValues[j]->r, im->pixelValues[j]->g, im->pixelValues[j]->b);
        j++;
    }
}


PPM *encode(PPM *im, char *message, unsigned int mSize, unsigned int secret) {

    int *binaryMessage = decimalToBinary(message, mSize);
    int i, j = 0, lineCounter = 0;

    for(i = 0; i < 40; i++) {
        switch(lineCounter) {
            case 0 :
                im->pixelValues[j]->r |= binaryMessage[i] << 0;
                lineCounter++;
                continue;
            case 1 :
                im->pixelValues[j]->g |= binaryMessage[i] << 0;
                lineCounter++;
                continue;
            case 2 :
                im->pixelValues[j]->b |= binaryMessage[i] << 0;
                lineCounter=0;
                j++;
                continue;
            default:
                continue;
        }

    }
    free(binaryMessage);
    return im;
}
/*
 * Converts a string into binary to be used in encode function. It
 * first converts each letter of the string into ascii code. Then
 * finds and stores each of the 8 bits of that int (ascii code of
 * the letter) sequentially in an array.
 */
static int *decimalToBinary(const char *message, unsigned int length) {
    /*
     * malloc is used here instead of [] notation to allocate memory,
     * because defining the variable with [] will make its scope
     * limited to this function only. Since we want to access this
     * array later on, we use malloc to assign space in the memory
     * for it so we can access it using a pointer later on.
     */
    int k = 0, i, j;
    unsigned int c;
    unsigned int *binary = malloc(8 * length * sizeof(int));

    for(i = 0; i < length; i++) {
        c = message[i];
        for(j = 7; j >= 0; j--,k++) {
            /*
             * We check here if the jth bit of the number is 1 or 0
             * using the bit operator &. If it is 1, it will return
             * 1 because 1 & 1 will be true. Otherwise 0.
             */
            if((c >> j) & 1)
                binary[k] = 1;
            else
                binary[k] = 0;
        }
    }
    return binary;
}

关于c - C struct值重置为NULL,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54854415/

10-11 23:03