2017-11-29 4 views
1

テキストファイルを読み取るプログラムを作成しようとしています。テキストファイルには、rgb番号を表す数字が含まれています。 test.txtの例:予期しない動作をしている構造体の配列に入力を格納しようとしています

P3 
3 4  
255 
12 32 222 12 32 222 12 32 222 12 32 222 12 32 222 12 32 222 12 32 222 
12 32 222 12 32 222 12 32 222 12 32 222 12 32 222 

PPMファイルをエミュレートする。私は最初の行を無視し、後で計算に使う次の3つの整数を読みます。私が正しく動作しないことをテストしてきたのは、unsigned char型の3つの要素、r、g、bを含むstruct型RGBの配列に次の数値を格納しようとしていることです。私が理解していないことは、最初のいくつかの要素を印刷しようとすると、最初のピクセル(最初の構造体、最初のr、g、b)が正しいとし、次の3つの数値はランダムであり、乱数で設定します。残りはゼロです。私は私の理解が正しいが、誰でも私を助けてくれるだろうか?コード:filter.hで

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

RGB *readPPM(const char *file, int *width, int *height, int *max) 

{ 
width = malloc(sizeof(int)*10);   
height = malloc(sizeof(int)*10); 
max = malloc(sizeof(int)*3);   

FILE *fp;        
int numOfPix, i=0; 
char garbage[3];               

if ((fp = fopen(file,"r")) == NULL)           
{ 
    printf("No such file exists!"); 
} 

fgets(garbage,sizeof(garbage),fp);          
fscanf(fp,"%d %d %d",width,height,max);        
int numofPix = (*width) * (*height);    
RGB pixels[numOfPix];          
RGB *returnptr = pixels; 
printf("w: %d h: %d m: %d pix: %d\n",*width,*height,*max,numofPix); 
while (!feof(fp)) 
{ 
    fscanf(fp,"%hhd %hhd %hhd", &pixels[i].r, &pixels[i].g, &pixels[i].b); 
    i++;  
} 
fclose(fp); 
return returnptr; 
} 

int main() 
{ 
RGB *RGBValues; 
int *width, *height, *max; 
int j=0; 
char *testfile = "test.txt"; 
RGBValues = readPPM(testfile,width,height,max); 
for(j=0;j<5;j++) 
{ 
printf("r: %d g: %d b: %d\n",RGBValues[j].r,RGBValues[j].g,RGBValues[j].b); 
} 
} 

構造体の定義:

typedef struct { unsigned char r, g, b; } RGB; 

出力例:

w: 3 h: 4 m: 255 pix: 12  //Correct 
r: 12 g: 32 b: 222    //Correct 
r: 232 g: 253 b: 85   //Changes every execution 
r: 0 g: 0 b: 19    //Static 
r: 0 g: 0 b: 0    //Static 
r: 0 g: 0 b: 0     //Static 
+1

リターンでreturn returnptr;が指すメモリはどうなりますか? (ヒント:poof!)、さらにそれにアクセスしようとすると、* Undefined Behavior *が呼び出されます。 (構造体の配列に割り当てる必要があります、関数にローカル宣言しません) –

答えて

3

あなたが宣言しているポインタint *width, *height, *max;readPPMに渡します。その後readPPMにあなたが

width = malloc(sizeof(int)*10);   
height = malloc(sizeof(int)*10); 
max = malloc(sizeof(int)*3); 

を置くこれらのmalloc割り当てはwidthheightmaxために整数の配列を作成します。明らかに、幅と高さの配列を作成する意図はありません。何もしないでください。整数を宣言してアドレスを渡すだけです。

while(feof(fp))も使用しないでください。fscanfの戻り値を確認し、fscanfが失敗した場合はループを中断してください。

コメントに指摘されているように、関数が終了するとRGB pixels[numOfPix]が破棄されます。この場合、RGB *pixelsにはmallocを使用してください。これにより、ヒープ上にメモリが作成され、関数が終了すると破棄されません。後でそのメモリを解放する必要があります。

RGB *readPPM(const char *file, int *ptr_width, int *ptr_height, int *ptr_max) 
{ 
    int width, height, max; 

    FILE *fp; 
    int numOfPix, i = 0; 
    char garbage[3]; 

    if((fp = fopen(file, "r")) == NULL) 
    { 
     printf("No such file exists!"); 
    } 

    fgets(garbage, sizeof(garbage), fp); 
    fscanf(fp, "%d %d %d", &width, &height, &max); 
    int numofPix = width * height; 
    RGB *pixels = malloc(numofPix * sizeof(RGB)); 
    printf("w: %d h: %d m: %d pix: %d\n", width, height, max, numofPix); 
    while(fscanf(fp, "%d %d %d", &pixels[i].r, &pixels[i].g, &pixels[i].b) == 3) 
    { 
     i++; 
    } 
    fclose(fp); 

    *ptr_width = width; 
    *ptr_height = height; 
    *ptr_max = max; 

    return pixels; 
} 

int main() 
{ 
    RGB *RGBValues; 
    int width, height, max; 
    int j = 0; 
    char *testfile = "test.txt"; 

    //pass the address of width, height, and max 
    RGBValues = readPPM(testfile, &width, &height, &max); 
    for(j = 0; j<5; j++) 
    { 
     printf("r: %d g: %d b: %d\n", RGBValues[j].r, RGBValues[j].g, RGBValues[j].b); 
    } 

    //width, height, max should be modified: 
    printf("%d %d %d\n", width, height, max); 

    //free the memory which was allocated in `readPPM` 
    free(RGBValues); 
} 
+0

何が未定義の動作を引き起こしているか分からないが、改善していただきありがとうございます。それは不要なポインタからでしたか? – TheShield

+1

Visual Studioを使用しているため、おそらくgccを使用している可能性があります。 UBは通常、コンパイラ間で一貫性がありません。ポインタは1つの問題で、もう1つは 'RGB pixels [numOfPix]'でした。これは、関数が終了するとすぐに破壊される「スタック」上にメモリを作成するためです。 –

+1

ところで、 'main'に' width'、 'height'の値を出力したい場合はポインタが必要ですが、' malloc'は必要ありません。 '* ptr_width = width;'を設定する部分を参照してください。これは 'main'で宣言された' width'の値を変更します –

関連する問題