CS61C在20fa的P1是康威的生命游戏,涉及到了不少关于指针和内存的操作,发现自己学得太烂完全不会写,最后还是抄了PKU飞猪大佬的答案,因此特地开一篇文章记录一下相关内容和学习笔记。
A1
A1要求实现的是3个函数,readData ,writeData,和 freeImage。分别实现了
将PPM文件转为IMAGE结构体
将IMAGE结构体打印
释放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的解释。
A2
A2需要你设计一个隐写术(Steganography)程序。用的是LSB,比较简单,这里不多赘述。比较要注意的是(*variable)->B不要写成*variable->B了。
B
首先我们知道,我们需要两个(至少输入的时候是)参数,filename和rule。
y有一个方法解析rule,将每一位都走一遍与运算,将结果放到数组上。有点像桶排序?在编写逻辑时就可以用了,好像不太优雅,先试着写吧
结束:2024.7.15 1:10
最后总算是写完了,完全没有诚信可言,最后疯狂地参考PKU大佬的答案修改。
也算是练习了一下指针和valgrind吧。想写一篇关于指针的简单博客,之后会写的。