CS61C在20fa的P1是康威的生命游戏,涉及到了不少关于指针和内存的操作,发现自己学得太烂完全不会写,最后还是抄了PKU飞猪大佬的答案,因此特地开一篇文章记录一下相关内容和学习笔记。

A1

A1要求实现的是3个函数,readDatawriteData,和 freeImage。分别实现了

  1. 将PPM文件转为IMAGE结构体

  2. 将IMAGE结构体打印

  3. 释放IMAGE结构体内存

下面是代码答案,来自PKU的飞猪大佬的github。我偷了个懒,没有写NULL检查。

typedef struct Color 
{
	uint8_t R;
	uint8_t G;
	uint8_t B;
} Color;
typedef struct Image
{
	Color **image;
	uint32_t rows;
	uint32_t cols;
} Image;
Image *readData(char *filename) 
{
	Image *image = (Image*)malloc(sizeof(Image));
	char format[3];
	FILE *fp = fopen(filename, "r");
	int maxcolor;
	fscanf(fp, "%s", format);
	fscanf(fp, "%d %d", &image->cols, &image->rows);
	fscanf(fp, "%d", &maxcolor);
	int count = image->cols*image->rows;
	image->image = (Color**)malloc(sizeof(Color*) * count);
	for(int i = 0; i < count; i++){
	*(image->image + i) = (Color*)malloc(sizeof(Color));
	Color *pixel = *(image->image + i);
	fscanf(fp, "%d %d %d", &pixel->R, &pixel->G, &pixel->B);
	}
	
	return image;

}
void writeData(Image *image)
{
	printf("P3\n%d %d\n255\n", image->cols, image->rows);
	Color** p = image->image;
	for (int i = 0; i < image->rows; i++) {
		for (int j = 0; j < image->cols - 1; j++) {
			printf("%3hhu %3hhu %3hhu   ", (*p)->R, (*p)->G, (*p)->B);
			p++;
		}
		printf("%3hhu %3hhu %3hhu\n", (*p)->R, (*p)->G, (*p)->B);
		p++;
	}
}
void freeImage(Image *image)
{	
	int count = image->cols*image->rows;
	for (int i = 0; i < count; i++)
	{
		free(*(image->image+i));
	}
	
	free(image->image);
	free(image);
}

接下来我将记录一下里面关于数组和指针的一些内容。

首先我们看看PPM文件的格式(其中一种)

P3 //格式

4 5 //列和行的数量

255//最大值

0 0 0 0 0 0 0 0 0 0 0 0

255 255 255 255 255 255 255 255 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

image->image = (Color**)malloc(sizeof(Color*) * count);

这行代码是对IMAGE对象的image指针进行分配内存,image指针是一个双重指针,一般指向的是一个二维数组——即数组的数组。

for(int i = 0; i < count; i++){
	*(image->image + i) = (Color*)malloc(sizeof(Color));
	Color *pixel = *(image->image + i);
	fscanf(fp, "%d %d %d", &pixel->R, &pixel->G, &pixel->B);
	}

这段代码则是将像素点全部写入数组中,(image+i)代表将指针前推一个image中color的大小的值。我们可以想象指针在跳格子,每个格子都是一个color的大小。

在C语言中,指针加上一个整数值可以表示地址偏移,这是因为指针在底层是以字节为单位进行偏移的。当你对指针加上一个整数值时,实际上是将指针指向的内存地址增加(或减少)相应的字节数,从而实现地址的偏移。

例如,假设有一个指向整型数组的指针 int *ptr,如果你执行 ptr + 1,那么 ptr 将指向数组中下一个整型元素的地址,这是因为在C语言中,整型通常占据4个字节(取决于系统架构)。

类似地,如果你有一个指向字符数组的指针 char *ptr,执行 ptr + 2 将使指针指向数组中第三个字符的地址,因为字符通常占据1个字节。

以上内容取自gpt,以前我以为加数字是前进地址,类似于汇编。

以及一个很重要的事情:开了指针变量,一定要记得分配内存!

	for (int i = 0; i < image->rows; i++) {
		for (int j = 0; j < image->cols - 1; j++) {
			printf("%3hhu %3hhu %3hhu   ", (*p)->R, (*p)->G, (*p)->B);
			p++;
		}
		printf("%3hhu %3hhu %3hhu\n", (*p)->R, (*p)->G, (*p)->B);
		p++;
	}

这段没有什么好说的,要注意的是%3hhu代表的是宽度为3位(不够的话会自动补全)的无符号字符。

以下是我google出来的对%c和%hhu的解释。

Data Type 数据类型

Format Specifier 格式说明符

char 字符

%c %C

signed char 有符号字符

%c (or %hhi for numerical output)
%c(或 %hhi 用于数字输出)

unsigned char 无符号字符

%c (or %hhu for numerical output)
%c(或 %hhu 用于数字输出)

A2

A2需要你设计一个隐写术(Steganography)程序。用的是LSB,比较简单,这里不多赘述。比较要注意的是(*variable)->B不要写成*variable->B了。

B

首先我们知道,我们需要两个(至少输入的时候是)参数,filename和rule。

y有一个方法解析rule,将每一位都走一遍与运算,将结果放到数组上。有点像桶排序?在编写逻辑时就可以用了,好像不太优雅,先试着写吧

结束:2024.7.15 1:10

最后总算是写完了,完全没有诚信可言,最后疯狂地参考PKU大佬的答案修改。

也算是练习了一下指针和valgrind吧。想写一篇关于指针的简单博客,之后会写的。

用记录对抗遗忘