CS61C在20fa的P1是康威的生命游戏,涉及到了不少关于指针和内存的操作,发现自己学得太烂完全不会写,最后还是抄了PKU飞猪大佬的答案,因此特地开一篇文章记录一下相关内容和学习笔记。
A1
A1要求实现的是3个函数,==readData== ,==writeData==,和 ==freeImage==。分别实现了
将PPM文件转为IMAGE结构体
将IMAGE结构体打印
释放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吧。想写一篇关于指针的简单博客,之后会写的。