CS50 第四周 Memory

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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anxonz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值