CS61C P1摸鱼记

First Post:

Last Update:

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

A1

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

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

  2. 将IMAGE结构体打印

  3. 释放IMAGE结构体内存

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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

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

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

1
2
3
4
5
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,以前我以为加数字是前进地址,类似于汇编。

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

1
2
3
4
5
6
7
8
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吧。想写一篇关于指针的简单博客,之后会写的。