[C] read 'n write PNM-Files

B

Bgag

Hallo zusammen!

Ich versuche momentan PNM-Files (*.pgm, *.pbm, *.ppm) auszulesen und neu zu erstellen. NähereInformationen findet ihr unter hier. Nun habe ich das Programm in seinem momentanen Zustand mittels
Code:
gcc -ansi -o pxm pxm.c
kompiliert. Leider bekomme ich einen Segmentation fault Fehler. Ich konnte bereits herausfinden, dass dieser Fehler wohl durch folgende zuweisung verursacht wird.
C:
pic.matrix[i][j].red = tmp;
Ich habe mir die Zeit genommen meinen Code ausführlich zu dokumentieren und wäre euch sehr dankbar, wenn ihr mal einen Blick auf meinen Quellcode werfen könntet und mir die folgenden Fragen beantworten könntet.

Wieso ist die obige Zuweisung fehlerhaft?

Ist mein Code im allgemeinen in Ordnung? Syntax, Dokumentation?

Programmiere leider erst seit etwa 3 Wochen in C. Brauche noch etwas mehr Sicherheit. Meine pxm.c findet ihr am Ende meines Posts. Vielen Dank für euer Verständnis und eure Hilfe.

MfG, Andy

C:
/* include needed packages */
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>

/* define boolean type */
enum boolean { FALSE, TRUE };

/* define pixel type */
typedef struct
{
	int red;
	int green;
	int blue;
} pixel;

/* define image type */
typedef struct
{
	int height;
	int width;
	int maxval;
	int type;  /* 1/4 = bitmap; 2/5 = graymap; 3/6 = pixel map */
	pixel **matrix;
} pnm;

/* define implemented methods */
pnm *read();
int write(char *file, pnm *pic);
void gettype();
void skipcomment();
void getsize();
void getmaxval();
void getmatrix();
int getint(void);
int *bittorgb(int bit);
int rgbtobit(int *rgb);

main(int argc, char *argv[])
{
	/* init some helpers */
	int i, j, tmp;
	time_t t;

	/* init random generator */
	time(&t);
	srand((unsigned int) t);

	/* init picture */
	pnm pic =
	{
		50,
		100,
		255,
		3,
		(pixel **) malloc(pic.height * pic.width * sizeof(pixel))
	};

	/* check number of arguments */
	if( argc < 1 )
	{
		/* print out error message */
		fprintf(stderr, "main: to view arguments\n");

		return FALSE;
	}

	/* loop over image rows */
	for(i = 0; i < pic.height; i++)
	{
		/* loop over image columns */
		for(j = 0; j < pic.width; j++)
		{
			tmp = rand() % 255 + 1;
			pic.matrix[i][j].red = tmp;
			tmp = rand() % 255 + 1;
			pic.matrix[i][j].green = tmp;
			tmp = rand() % 255 + 1;
			pic.matrix[i][j].blue = tmp;
		}
	}

	/* generate image */
	if( !write(argv[0], &pic) )
	{
		/* print out error message */
		fprintf(stderr, "main: cannot create image %s\n", argv[0]);

		return FALSE;
	}

	return TRUE;
}

pnm *read()
{

}

int write(char *file, pnm *pic)
{
	/* init file pointer */
	FILE *fp;

	/* init helper vars */
	int i, j;

	/* open file for reading */
	if( (pic->type > 0) && (pic->type < 7) )
	{
		/* print out error message */
		fprintf(stderr, "write: invalid format P%d\n", pic->type);

		return FALSE;
	}

	/* open the file for output */
	if( (fp = fopen(file, "w")) == NULL )
	{
		/* print out error message */
		fprintf(stderr, "write: failed to open file %s\n", file);

		return FALSE;
	}

	/* write filetype and dimensions into the file */
	fprintf(fp, "P%d\n%d %d\n", pic->type, pic->height, pic->width);

	/* add maxval if needed */
	if( (pic->type > 1) && (pic->type < 7) && (pic->type != 4) )
	{
		/* check for valid max value */
		if( pic->maxval > 0 )
			fprintf(fp, "%d\n", pic->maxval);
		else
			fprintf(fp, "255\n");
	}

	/* check if image is a colored one */
	if( (pic->type == 6) || (pic->type == 3) )
	{
		/* check if image is ascii decimal encoded */
		if( pic->type < 4 )
		{
			/* loop over image rows */
			for(i = 0; i < pic->height; i++)
			{
				/* loop over image columns */
				for(j = 0; j < pic->width; j++)
				{
					fprintf(fp, "%d %d %d\n", pic->matrix[i][j].red, pic->matrix[i][j].green, pic->matrix[i][j].blue);
				}
			}
		}

		/* plain bytes */
		else
		{
			/* loop over image rows */
			for(i = 0; i < pic->height; i++)
			{
				/* loop over image columns */
				for(j = 0; j < pic->width; j++)
				{
					fprintf(fp, "%c%c%c\n", pic->matrix[i][j].red, pic->matrix[i][j].green, pic->matrix[i][j].blue);
				}
			}
		}
	}

	/* check if image is grayscale */
	else if( (pic->type == 2) || (pic->type == 5) )
	{
		/* check if image is ascii decimal encoded */
		if( pic->type < 4 )
		{
			return;
		}

		/* plain bytes */
		else
		{
			return;
		}
	}

	/* image is bitmap */
	else
	{
		/* check if image is ascii decimal encoded */
		if( pic->type < 4 )
		{
			return;
		}

		/* plain bytes */
		else
		{
			return;
		}
	}

	/* close file handle */
	fclose(fp);

	return TRUE;
}

void gettype()
{

}

void skipcomment()
{

}

void getsize()
{

}

void getmaxval()
{

}

void getmatrix()
{

}

int getint(void)
{
	/* init input buffer */
	char buf[80];

	/* int some helpers */
	int i, c;

	/* loop while there is an input */
	for(i = 0; (i < 80) && !isspace(c = getchar()); i++)
	{
		/* check if input is a digit */
		if (isdigit(c))
		{
			/* write digit to buffer */
			buf[i] = (char) c;
		}
	}

	/* end string */
	buf[i] = '\0';

	/* cast string to int */
	i = atoi(buf);

	return i;
}

int *bittorgb(int bit)
{
	/* declare rgb arrays */
	static int one[3] = {255, 255, 255};
	static int zero[3] = {0, 0, 0};

	/* check if bit is up */
	return (bit == 1) ? one : zero;
}

int rgbtobit(int *rgb)
{
	/* declare rgb array */
	static int one[3] = {255, 255, 255};

	return (*rgb == *one) ? 1 : 0;
}
 
Hi.

Das Problem ist, dass du versuchst auf pnm.pixel wie auf ein 2-dimensionales Feld (ein Feld von Zeigern) zuzugreifen, was es aber nicht ist. An Position
Code:
pnm.pixel[i]
befindet sich eben kein weiterer Zeiger der dereferenziert werden kann, sondern pnm.pixel ist ganz einfach ein Zeiger auf einen Block von Speicher (obwohl du es anders typisiert hast, was allerdings falsch ist).

Du kannst auf das (i, j)-te Element so zugreifen:
Code:
pnm.pixel[i * width + j]


Gruß
 
Danke erstmal für deine schnelle und erleuchtende Antwort. Nun würde mich allerdings noch interessieren, wie ich die Typisierung vornehmen muss, sodass sie nicht mehr falsch ist.

MfG, Andy

EDIT: Könnte ich, wie ich vorher getan habe, auf das Array zugreifen, wenn ich folgende Änderung vornehme?
C:
(pixel[][]) malloc(pic.height * pic.width * sizeof(pixel))
 
Zuletzt bearbeitet von einem Moderator:
Danke erstmal für deine schnelle und erleuchtende Antwort. Nun würde mich allerdings noch interessieren, wie ich die Typisierung vornehmen muss, sodass sie nicht mehr falsch ist.

MfG, Andy

EDIT: Könnte ich, wie ich vorher getan habe, auf das Array zugreifen, wenn ich folgende Änderung vornehme?
C:
(pixel[][]) malloc(pic.height * pic.width * sizeof(pixel))
Nein, soetwas ist nicht erlaubt und würde auch gar nichts ändern.

C:
typdef struct {
  ...
  pixel* matrix;
} pnm;

typedef pixel (*pixel_matrix)[pic->width];

pixel_matrix matrix = (pixel_matrix)pic->matrix;
Gruß
 
C:
typdef struct {
  ...
  pixel* matrix;
} pnm;

typedef pixel (*pixel_matrix)[pic->width];

pixel_matrix matrix = (pixel_matrix)pic->matrix;
Sicher, dass das mit dem typedef so klappt? Der konkrete Wert von pic->width ist ja im Allgemeinen erst zur Laufzeit bekannt. Ansonsten wär das echt eine nette Möglichkeit :)

Mein Vorschlag wäre der hier:
C:
typedef struct
{
    ...
    pixel *pixels;
    pixel **matrix;
} pnm;

pnm pic =
{
    ...
    (pixel *) malloc(pic.height * pic.width * sizeof(pixel)),
    (pixel **) malloc(pic.height * sizeof(pixel *))
};

for (int row = 0; row < pic.height; ++row)
  pic.matrix[row] = pic.pixels + row * pic.width;
Grüße, Matthias
 
Hallo!

Danke für eure Antworten. Habe sie leider gerade erst gesehen. Werde mir das auch gleich mal anschauen. Habe allerdings gerade nochmals versucht das Problem zu beheben und bin auf das selbe Problem gestoßen, wobei ich nicht verstehe, was daran falsch ist. ich habe mir eine Funktion zum allocieren von Speicher auf dem Heap für Pixel-Matrizen und eine Funktion zur Freigabe dieses speichers geschrieben, die es mir ermöglichen sollten mit matrix[i][j] auf die Einträge in der Matrix zugreifen zu können. Das ganze sieht so aus:

C:
pixel **pnmalloc(unsigned int n, unsigned int m)
{
	/* init matrix pointer */
	pixel **p;

	/* allocate space for rows */
	p = (pixel **) malloc(n * sizeof(pixel *));

	/* validate pointer */
	if( p != NULL )
	{
		/* allocate first column */
		p[0] = (pixel *) malloc(n * m * sizeof(pixel));

		/* validate pointer */
		if( p[0] == NULL )
		{
			/* set pointer to NULL */
			free(p);
			p = NULL;
		}

		else
		{
			/* init helper var */
			unsigned int i;

			/* loop over rest of columns */
			for(i = 1; i < m; i++)
			{
				p[i] = p[i-1] + m;
			}
		}
	}

	return p;
}

void pnmfree(pixel **p)
{
	/* check for NULL pointer */
	if( p != NULL )
	{
		free(p[0]);
	}

	/* free space */
	free(p);
}
Die Speicherallocierung habe ich daraufhin wie folgt geändert.
C:
	/* init picture */
	pnm pic =
	{
		50,
		100,
		255,
		3,
		(pixel **) pnmalloc(pic.width, pic.height)
	};
Ist das so nicht möglich? Wo liegt mein Denkfehler? Ist es nicht Möglich einen Vektor aus Pointer so zu erschaffen, dass jeder dieser pointer auf ein Array von Pixel-typen zeigt, sodass ich die einzelnen Einträge via matrix[row][col] ansprechen kann? Danke nochmals für eure Hilfe.

MfG, Andy
 
Hallo Andy,

sollte das in Zeile 7 nicht
C:
    p = (pixel **) malloc(m * sizeof(pixel *));
und in Zeile 31
C:
                p[i] = p[i-1] + n;
heißen? Vielleicht solltest du die Variablen etwas aussagekräftiger benennen (z.B. rows und cols). Der Rest sieht auf den ersten Blick in Ordnung aus.

Grüße, Matthias
 
Hi.
Sicher, dass das mit dem typedef so klappt? Der konkrete Wert von pic->width ist ja im Allgemeinen erst zur Laufzeit bekannt. Ansonsten wär das echt eine nette Möglichkeit :)
In C99 sollte es funktionieren, denn da gibt es Arrays mit variabler Größe.

In vorigen C Versionen sollte es eigentlich nicht gehen, aber der GCC beschwert sich nicht selbst mit -std=c89 -ansi Optionen...

Ich hab es spaßeshalber mal mit dem Digital Mars C/C++ und dem LCC ausprobiert, was auch problemlos funktioniert. Der MS Visual C/C++ Compiler spuckt allerdings nur Fehler aus (was nicht wirklich überrascht... :rolleyes:).

Gruß
 

Neue Beiträge

Zurück