C 结构值重置为 NULL

标签 c pointers null

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

typedef struct {
     char **comments;
} PPM;

我有 3 个使用此结构的函数。

  1. PPM *getPPM() 用于从文件中获取所有 PPM 数据并将其存储到结构中

  2. void showPPM(PPM * img)在终端显示图像数据

  3. PPM *encode(PPM *img) 用于改变图像RGB值的LSB

问题是 getPPM 按预期工作并将所有评论获取到 getPPM 的评论数组中。如果我这样做,它会很好地显示它们:

PPM *p = getPPM(fin);
showPPM(p);

但是如果我尝试使用这样的编码函数调用它:

PPM *p = getPPM(fin);
PPM *g = encode(p);
showPPM(g);

调试器显示,一旦程序进入encode 函数,注释值就会重置为 NULL,即使此函数甚至没有触及注释。是我调用这些函数的方式有误还是我的代码有问题?如果问题不是调用函数的方式,我将尝试提供最少的代码,因为代码很大而且一切都相互依赖。

我是 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

最佳答案

decimalToBinar

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 结构值重置为 NULL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54854415/

相关文章:

pointers - 为什么带有指针接收器的方法在接收到值时仍然有效?

c - 在 C 中传递二维数组

c - 指针数组的不同声明

c - 在c程序中添加0作为最后一个值

c - 跨平台检测临时ipv6地址

c - 将数组传递给具有奇数格式的函数 - "v+last+1"

c - Atoi(char *p) 函数

mysql - 结合 CONCAT() 和 COALESCE() 在 MySQL 中生成 JSON

java - 使用 'if' 语句检查后出现空指针异常

c++ - 在 C 或 C++ 中,我应该根据 NULL/nullptr 检查指针参数吗?