addresses.c
#include <stdio.h>
// *指针 &取址
int main(void)
{
int x = 50;
int *p = &x;
printf("变量 x 的值:%d\n", x);
printf("变量 x 的值:%d\n", *p);
printf("变量 x 的内存地址:%p\n", p);
printf("变量 x 的内存地址:%p\n", &x);
printf("指针变量 p 存储的地址:%p\n", &p);
char *s = "HI!";
printf("字符串 s 的值: %s\n", s);
printf("字符串 s 的第一个字符的内存地址: %p\n", &s[0]);
printf("字符串 s 的第二个字符的内存地址: %p\n", &s[1]);
printf("字符串 s 的第三个字符的内存地址: %p\n", &s[2]);
printf("字符串 s 的内存地址: %p\n", s);
printf("字符串 s 的第一个字符的值: %c\n", *s);
printf("字符串 s 的第二个字符的值: %c\n", *(s + 1));
for (int i = 0; i < 3; i++)
{
printf("%c\n", *(s + i));
}
printf("%s\n", s);
printf("%s\n", s + 1);
printf("%s\n", s + 2);
return 0;
}
compare.c
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char *s = get_string("s: ");
char *t = get_string("t: ");
if (strcmp(s, t) == 0)
{
printf("Same\n");
}
else
{
printf("Different\n");
}
}
copy.c
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// malloc free
int main(void)
{
char *s = get_string("s: ");
if (s == NULL)
{
return 1;
}
char *t = malloc(strlen(s) + 1);
if (t == NULL)
{
return 1;
}
// for (int i = 0,n = strlen(s); i <=n ; i++)
// {
// t[i] = s[i];
// }
strcpy(t, s);
if (strlen(t) > 0)
{
t[0] = toupper(t[0]);
}
printf("s: %s\n", s);
printf("t: %s\n", t);
free(t);
return 0;
}
memory.c
#include <stdio.h>
#include <stdlib.h>
//使用valgrind 检测语法错误
int main(void)
{
int *x = malloc(3 * sizeof(int));
if (x == NULL)
{
return 1;
}
x[0] = 72;
x[1] = 73;
x[2] = 33;
free(x);
return 0;
}
garbage.c
#include <stdio.h>
#include <stdlib.h>
//未定义数组,垃圾值
int main(void)
{
int scores[1024];
for (int i = 0; i < 1024; i++)
{
printf("%i\n", scores[i]);
}
}
swap.c
#include <stdio.h>
void swap(int *a, int *b);
void swap1(int a, int b);
int main(void)
{
int x = 1;
int y = 2;
int i = 3;
int j = 4;
printf("x is %i, y is %i\n", x, y);
printf("i is %i, j is %i\n", i, j);
swap(&x, &y);
swap1(i, j);
printf("x is %i, y is %i\n", x, y);
printf("i is %i, j is %i\n", i, j);
}
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void swap1(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
get.c
#include <stdio.h>
int main(void)
{
int x;
printf("x: ");
scanf("%i", &x);
printf("x: %i\n", x);
// char *s;
char s[4];
printf("s: ");
scanf("%s", s);
printf("s: %s\n", s);
return 0;
}
phonebook.c
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *file = fopen("phonebook.csv", "a");
string name = get_string("Name: ");
string number = get_string("Number: ");
fprintf(file, "%s,%s\n", name, number);
fclose(file);
}
test.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *x;
int *y;
x = malloc(sizeof(int));
*x = 42;
y = x;
*y = 13;
printf("%p\n", &*x);
printf("%p\n", &*y);
printf("%p\n", &x);
printf("%p\n", &y);
printf("%d\n", *x);
printf("%d\n", *y);
return 0;
}
bottommup.c
- 练习使用图像
- 了解元数据
- 详细了解结构体的用途
一个24位的BMP文件本质上只是一系列的比特,其中的每24个几乎代表一个像素的颜色。但是,BMP文件还包含一些“元数据”,像是图像的高度和宽度这样的信息。这些元数据以两个数据结构的形式存储在文件的开头,通常被称为“头部”,不要与C语言的头文件混淆。这两个头部中的第一个称为BITMAPFILEHEADER,长度为14个字节(请注意,1字节等于8位)。第二个头部称为BITMAPINFOHEADER,长度为40个字节。在这些头部之后,紧接着的是实际的位图数据:一个字节数组,其中的三个字节表示一个像素的颜色
wget https://cdn.cs50.net/2022/fall/labs/4/bottomup.zip
./bottomup harvard_bottomup.bmp harvard_topdown.bmp
// Copies a BMP file
#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"
int main(int argc, char *argv[])
{
// 确保正确的用法
if (argc != 3)
{
printf("Usage: copy infile outfile\n");
return 1;
}
// Remember filenames
char *infile = argv[1];
char *outfile = argv[2];
// 打开输入文件
// 文件的打开模式,例如 "r"(只读),"w"(写入),"a"(追加),等等
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
printf("Could not open %s.\n", infile);
return 2;
}
// 打开输出文件
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
printf("Could not create %s.\n", outfile);
return 3;
}
// 读取输入文件的 BITMAPFILEHEADER
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// ptr:是一个指针,指向存储读取数据的缓冲区
// size:每个数据块的字节数
// nmemb:要读取的数据块的数量
// stream:是一个指向 FILE 结构的指针,表示输入流
// 读取输入文件的 BITMAPINFOHEADER
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
// 确保输入文件是一个24位无压缩的BMP 4.0文件
if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
bi.biBitCount != 24 || bi.biCompression != 0)
{
fclose(outptr);
fclose(inptr);
printf("Unsupported file format.\n");
return 4;
}
// 写入输出文件的 BITMAPFILEHEADER
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);
// 写入输出文件的 BITMAPINFOHEADER
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);
// 计算扫描线的填充值
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
// 迭代输入文件的扫描线
for (int i = abs(bi.biHeight) - 1; i >= 0; i--)
{
// 迭代扫描线中的像素
for (int j = 0; j < bi.biWidth; j++)
{
// 临时存储
RGBTRIPLE triple;
// 从输入文件读取 RGB 三元组
fseek(inptr, (sizeof(RGBTRIPLE) * bi.biWidth + padding) * i + sizeof(RGBTRIPLE) * j, SEEK_SET);
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
// 将 RGB 三元组写入输出文件
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
// 将填充添加到输出文件(如果有的话)
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
// 关闭输入文件
fclose(inptr);
// 关闭输出文件
fclose(outptr);
// Success
return 0;
}
license.c
wget https://cdn.cs50.net/2022/fall/labs/4/license.zip
./license plates.txt
11ZT00
1KAD21
78ZZ01
99ZZ11
72ZZ21
98ZZ31
44ZW41
34ZZ51
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// 检查命令行参数
if (argc != 2)
{
printf("Usage: ./read infile\n");
return 1;
}
// 创建缓冲区以读取数据
char buffer[7];
// 创建数组以存储车牌号
char *plates[8];
FILE *infile = fopen(argv[1], "r");
int idx = 0;
/*
plates 数组中的每个元素都是指针,plates[idx] 是一个 char* 类型的指针,这些指针指向字符数组buffer
当执行 plates[idx] = buffer; 时,它将 buffer 数组的地址(即第一个元素的地址)赋给了 plates[idx]
由于 plates[idx] 是 char* 类型,它指向的是一个以 \0 结尾的字符数组,即buffer
由于 buffer 在迭代中的内容变动,plates 中的每个指针都指向不同迭代中的相同内存地址
因此它们都反映了最后一次迭代中 buffer 的内容
所以需要为每个 plates[idx] 动态分配内存,以便每个指针都指向不同的内存空间
*/
while (fread(buffer, 1, 7, infile) == 7)
{
// 将 '\n' 替换为 '\0'
buffer[6] = '\0';
// plates[idx] = buffer;
// 为每个车牌号动态分配内存并将其保存到数组中
plates[idx] = malloc(strlen(buffer) + 1); // 为字符串分配内存
strcpy(plates[idx], buffer); // 复制字符串内容
idx++;
}
// 关闭文件
fclose(infile);
// 打印车牌号
for (int i = 0; i < 8; i++)
{
printf("%s\n", plates[i]);
free(plates[i]); // 释放每个车牌号的内存
}
return 0;
}
Smiley
- 了解如何处理图像
- 练习操作像素
wget https://cdn.cs50.net/2022/fall/labs/4/smiley.zip
// //可以了解.h文件的作用
$ ls
Makefile bmp.h colorize colorize.c helpers.c helpers.h smiley.bmp
//命令
clang colorize.c helpers.c -o colorize
./colorize smiley.bmp smiley_out.bmp
colorize.c
用于读取一个24位彩色的BMP图像文件,然后通过调用名为colorize
的函数修改图像中的黑色像素。最后,将修改后的图像保存为新的BMP文件
#include <stdio.h>
#include <stdlib.h>
#include "helpers.h"
int main(int argc, char *argv[])
{
// ensure proper usage
if (argc != 3)
{
printf("Usage: colorize infile outfile\n");
return 1;
}
// remember filenames
char *infile = argv[1];
char *outfile = argv[2];
// open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
printf("Could not open %s.\n", infile);
return 4;
}
// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
printf("Could not create %s.\n", outfile);
return 5;
}
// read infile's BITMAPFILEHEADER
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);
// read infile's BITMAPINFOHEADER
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);
// ensure infile is (likely) a 24-bit uncompressed BMP 4.0
if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
bi.biBitCount != 24 || bi.biCompression != 0)
{
fclose(outptr);
fclose(inptr);
printf("Unsupported file format.\n");
return 6;
}
int height = abs(bi.biHeight);
int width = bi.biWidth;
// allocate memory for image
RGBTRIPLE (*image)[width] = calloc(height, width * sizeof(RGBTRIPLE));
if (image == NULL)
{
printf("Not enough memory to store image.\n");
fclose(outptr);
fclose(inptr);
return 7;
}
// determine padding for scanlines
int padding = (4 - (width * sizeof(RGBTRIPLE)) % 4) % 4;
// iterate over infile's scanlines
for (int i = 0; i < height; i++)
{
// read row into pixel array
fread(image[i], sizeof(RGBTRIPLE), width, inptr);
// skip over padding
fseek(inptr, padding, SEEK_CUR);
}
colorize(height, width, image);
// write outfile's BITMAPFILEHEADER
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);
// write new pixels to outfile
for (int i = 0; i < height; i++)
{
// write row to outfile
fwrite(image[i], sizeof(RGBTRIPLE), width, outptr);
// write padding at end of row
for (int k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}
// free memory for image
free(image);
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
return 0;
}
helpers.c //实现替换黑色像素
#include "helpers.h"
void colorize(int height, int width, RGBTRIPLE image[height][width])
{
// Change all black pixels to a color of your choosing
// 定义橙色
RGBTRIPLE orange_pixel;
orange_pixel.rgbtRed = 255;
orange_pixel.rgbtGreen = 165;
orange_pixel.rgbtBlue = 0;
// 遍历每一个像素
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// 检查当前像素是否是黑色
if (image[i][j].rgbtBlue == 0 && image[i][j].rgbtGreen == 0 && image[i][j].rgbtRed == 0)
{
// 将黑色像素更改为红色
image[i][j] = orange_pixel;
}
}
}
}
volume.c
- 处理wav格式音频文件
wget https://cdn.cs50.net/2022/fall/labs/4/volume.zip
./volume input.wav output.wav 2.0
WAV文件将音频存储为一系列“样本”:表示某个特定时间点上音频信号值的数字。WAV文件以一个44字节的“头部”开头,其中包含有关文件本身的信息,包括文件大小、每秒的样本数和每个样本的大小。在头部之后,WAV文件包含一系列样本,每个样本都是一个单独的2字节(16位)整数,表示某个特定时间点上的音频信号
stdint.h的头文件中,允许我们非常精确地定义整数的大小(以位为单位)和符号(有符号或无符号)。在这个实验中,两种类型将对我们有用。
uint8_t是一种存储8位无符号(即非负)整数的类型。我们可以将WAV文件头的每个字节视为一个值。
int16_t是一种存储16位有符号(即正或负)整数的类型。我们可以将WAV文件中的每个音频样本视为一个值
// 修改音频文件的音量
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// .wav 头部的字节数
const int HEADER_SIZE = 44;
int main(int argc, char *argv[])
{
// 检查命令行参数
if (argc != 4)
{
printf("用法: ./volume input.wav output.wav factor\n");
return 1;
}
// 打开文件并确定缩放因子
FILE *input = fopen(argv[1], "r");
if (input == NULL)
{
printf("无法打开文件。\n");
return 1;
}
FILE *output = fopen(argv[2], "w");
if (output == NULL)
{
printf("无法打开文件。\n");
return 1;
}
float factor = atof(argv[3]);
// TODO: 从输入文件复制头部到输出文件
// 先从输入文件中读取头部数据,然后再将其写入输出文件
/*
ptr(指针):一个指向存储读取数据的内存位置的指针.在这里,它是指向 header 数组的指针
size(大小):每个数据项的字节数.在这里,数据项是 uint8_t 类型,即一个字节
nmemb(成员数量):要读取的数据项的数量.在这里,它是 HEADER_SIZE,即头部的大小,表示要读取的数据项的总数
stream(流):指向 FILE 结构的指针,表示输入流
*/
uint8_t header[HEADER_SIZE];
// TODO: 从输入文件读取头部数据
fread(header, sizeof(uint8_t), HEADER_SIZE, input);
// TODO: 将头部数据写入输出文件
fwrite(header, sizeof(uint8_t), HEADER_SIZE, output);
// TODO: 从输入文件读取样本并将更新后的数据写入输出文件
// 设置缓冲区大小,也可以const常量,这里设置为1024
int16_t buffer[1024];
//因为未知大小,这里一直循环,不符合条件跳出
while (1)
{
//计算分配内存后,读取的次数
//fwrite跟fread会根据写入/读取的字节数自动将文件指针移动到下一个位置
size_t items_read = fread(buffer, sizeof(int16_t), 1024, input);
// 处理读取到的数据
for (size_t i = 0; i < items_read; i++)
{
// 将样本乘以缩放因子
buffer[i] = buffer[i] * factor;
}
// 将更新后的数据写入输出文件
fwrite(buffer, sizeof(int16_t), items_read, output);
// 如果无法读取更多数据,说明已经读取完毕,退出循环
if (items_read < 1024)
{
break;
}
}
// 关闭文件
fclose(input);
fclose(output);
return 0;
}