C++学习日记 | LAB 4 Makefile

资料来源:南科大 于仕琪 C/C++ Program Design
LINK:CPP/week04 at main · ShiqiYu/CPP · GitHub


一、本节内容

1. Makefile的使用

1.1 Makefile简介

  • Makefile是一个简化和组织编译的工具。
  • Makefile是一组带有变量名和目标的命令,可以使用Makefile编译您的项目(程序)或仅编译项目中的更新文件。
  • 当存在非常多的文件需要一起编译时,使用Makefile是一个很好的选择
Makefile 的组成

1.2 Makefile中的宏/变量定义

如果只修改一个源文件,则不需要编译所有文件,接着可以将makefile修改成这种互相引用的结构

将所有的cpp自动编译和链接
重新编译命令的撰写

 1.3 Makefile中的函数

  • wildcard: 查找文件

  • patsubst:替换文件

对于.h文件的调用方式

1.4 总结:通用的makefile指令模板

# Using functions

SOURCE 	= $(wildcard ./*.cpp)
OBJS	= $(patsubst %.cpp, %.o, $(SOURCE))
TARGET	= #Enter target name here

CC		= g++
CFLAGS 	= -c -Wall

$(TARGET):$(OBJS)
	$(CC) -o $@ $(OBJS)
%.o: %.cpp
	$(CC) $(CFLAGS) $< -o $@

.PHONY:clean
clean:
	rm -f *.o $(TARGET)

2. 输入与输出指令

C:

scanf & printf

scanf使用空格、tab、和回车分割字符(是否结束)

C:

gets & puts

gets()可以读入含有空格的句子,同时以回车判读是否结束

C++:

cin & cout

cin使用空格、tab、和回车分割字符(是否结束)

C++:

cin.getline( )

& cin.get( )

getline()和get()都读取整个输入行,直到换行符终止读取。但是,getline()遇到换行符后会在输入队列中丢弃换行符,而get()会将其保留在输入队列中。

二、习题笔记

习题1

#include <iostream>
#include <string.h>
using namespace std;

int main()
{
    int cards[4]{};
    int hands[4];
    double price[] = {2.8,3.7,5,9};
    char direction[4] {'L',82,'U',68};
    char title[] = "ChartGPT is an awesome tool.";

    cout    << "sizeof(cards) = "       << sizeof(cards) 
            << ",sizeof of cards[0] = " << sizeof(cards[0]) << endl;
    cout    << "sizeof(price) = "       << sizeof(price) 
            << ",sizeof of price[0] = " << sizeof(price[1]) << endl;
    cout    << "sizeof(direction) = "   << sizeof(direction) 
            << ",length of direction = " << strlen(direction) << endl;
    cout    << "sizeof(title) = "       << sizeof(title) 
            << ",length of title = "    << strlen(title) << endl;

    //Print the value and address of each element in cards and hands respectively.
    
    int i;
    for (i = 0; i < 4; i++)
    {
        printf("value of cards[%d] is %d,\t address is %p\n", i, cards[i], &cards[i]);
    }
    
    for (i = 0; i < 4; i++)
    {
        printf("value of hands[%d] is %d,\t address is %p\n", i, hands[i], &hands[i]);
    }
    
    return 0;
}

 Tips:

习题2 


知识点:字节序

字节序(Endianness)是指在计算机内存中多字节数据(例如整数、浮点数)的存储顺序。具体来说,它涉及到字节在内存中的排列方式。

有两种常见的字节序类型:

  1. 小端序(Little-endian):在小端序中,最低有效字节(Least Significant Byte,LSB)存储在最低内存地址处,而最高有效字节(Most Significant Byte,MSB)存储在最高内存地址处。这意味着数据的低位字节排在前面。
  2. 大端序(Big-endian):在大端序中,最高有效字节(MSB)存储在最低内存地址处,而最低有效字节(LSB)存储在最高内存地址处。这意味着数据的高位字节排在前面。

假设我们有一个16位整数,其十六进制表示为0x1234。在小端序中,它在内存中的存储方式如下:

低地址 -> 0x34
高地址 -> 0x12

而在大端序中,它在内存中的存储方式如下:

低地址 -> 0x12
高地址 -> 0x34

字节序在计算机体系结构、网络通信和文件格式中都非常重要。例如,网络协议通常需要指定数据的字节序,以确保不同计算机之间的数据传输正确解释。

需要注意的是,不同的体系结构和操作系统可能使用不同的字节序。因此,在处理二进制数据时,我们需要了解所使用的字节序,以避免出现错误。


运行结果:

原理分析:

这段 C++ 代码使用了联合体(union),工作原理如下:

  1. 首先,定义了一个名为 data 的联合体。联合体允许在相同的内存位置存储不同类型的数据,但同一时刻只能使用其中一个成员。
  2. data 联合体有三个成员:
  3. 在 main 函数中,我们创建了一个名为 a 的 data 联合体变量。
  4. 我们使用 sizeof 运算符来获取 a 的大小(字节数),并打印出来。注意,联合体的大小等于其最大成员的大小,因为它们共享相同的内存空间。关于联合体、结构体的大小计算参考:关于结构体、联合体大小的计算-优快云博客
  5. 接下来,我们对 a 的成员进行赋值和打印:
    • a.n = 0x40;:将整数成员 n 设置为十六进制值 0x40
    • a.ch = '9';:将字符成员 ch 设置为字符 '9'
    • a.m = 0x2059;:将短整数成员 m 设置为十六进制值 0x2059
    • a.n = 0x3E25AD54;:将整数成员 n 设置为十六进制值 0x3E25AD54
  6. 每次设置成员后,我们使用 printf 打印出 a.na.ch 和 a.m 的值。

现在,让我们逐行分析输出结果:

  1. sizeof(a) 和 sizeof(union data) 都是 4 字节,因为 int 是联合体中最大的成员且是最小成员char的倍数,因此首先输出两个4
  2. 第二次打印:0x40, @, 40a.n 的十六进制值是 0x40,对应的 ASCII 字符是 '@'a.m 的十六进制值是 0x40
  3. 第三次打印:0x39, 9, 39a.ch 的值是字符 '9',对应的 ASCII 值是 0x39a.m 的十六进制值是 0x39
  4. 第四次打印:0x2059, Y, 2059a.m 的十六进制值是 0x2059,a.n 的十六进制值也是 0x2059,a.ch 的十六进制值是 0x59(因为只有1个byte,20超出范围被舍弃了),对应的 ASCII 字符是 'Y'。
  5. 第五次打印:0x3E25AD54, T, AD54a.n 的十六进制值是 0x3E25AD54a.ch 的十六进制值是 0x54(因为只有1个byte,;剩余的超出范围被舍弃了),对应的 ASCII 字符是 'T'a.m 的十六进制值是 0xAD54(剩余的超限被舍弃)
  6. 由此可以看出是小端序(Little-endian)

总结:这段代码展示了联合体的特性,以及在不同成员之间共享内存的能力。需要注意的是,联合体的使用需要谨慎,因为它涉及到不同类型之间的内存共享,可能导致未定义行为。

习题3

#include <iostream>
using namespace std;

enum class Day {
    Sunday,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
};

enum class Weather {
    Sunny,
    Rainy,
    Cloudy,
    Snowy
};

struct DayInfo {
    Day day;
    Weather weather;
};

bool canTravel(DayInfo dayInfo) {
    return (dayInfo.day == Day::Saturday || dayInfo.day == Day::Sunday) &&
           dayInfo.weather == Weather::Sunny;
    // 以一种简单的方式实现单一 return
}

// 将字符串映射到 Day 枚举
Day stringToDay(const string& dayStr) {
    if (dayStr == "Sunday")     return Day::Sunday;
    if (dayStr == "Monday")     return Day::Monday;
    if (dayStr == "Tuesday")    return Day::Tuesday;
    if (dayStr == "Wednesday")  return Day::Wednesday;
    if (dayStr == "Thursday")   return Day::Thursday;
    if (dayStr == "Friday")     return Day::Friday;
    if (dayStr == "Saturday")   return Day::Saturday;
    // 如果输入无效,返回默认值
    return Day::Sunday;
}

// 将字符串映射到 Weather 枚举
Weather stringToWeather(const string& weatherStr) {
    if (weatherStr == "Sunny")  return Weather::Sunny;
    if (weatherStr == "Rainy")  return Weather::Rainy;
    if (weatherStr == "Cloudy") return Weather::Cloudy;
    if (weatherStr == "Snowy")  return Weather::Snowy;
    // 如果输入无效,返回默认值
    return Weather::Sunny;
}

int main() {
    DayInfo today;

    cout << "请输入今天的星期(英文):" << endl;
    string dayInput;
    cin >> dayInput;
    today.day = stringToDay(dayInput);

    cout << "请输入今天的天气(英文):" << endl;
    string weatherInput;
    cin >> weatherInput;
    today.weather = stringToWeather(weatherInput);

    if (canTravel(today)) {
        cout << "今天可以出行!" << endl;
    } 
    else {
        cout << "今天不适合出行。" << endl;
    }

    return 0;
}

Tips:

1. enum枚举类型不能采用cin直接输入,需要编写映射函数或采用强制类型转换。参考:C语言枚举类型(enum)的各种用法_枚举类型enum用法-优快云博客

2. 映射函数中,由于只需要读取dayStr或weatherStr的值,因此采用const string& 的方式进行定义更好。


const string& dayStr

  • const string& 表示一个对字符串的引用
  • 引用是一个别名,它指向实际存储的字符串。
  • 使用引用可以避免复制大型字符串,提高性能。
  • 如果函数内部不需要修改字符串,使用引用是一种好的选择。

习题4

 main.cpp

#include <iostream>
#include "fib.hpp"
using namespace std;

int main() {
    int n;
    do
    {
        cout << "请输入一个正整数 n: ";
        cin >> n;
    }while (n <= 0 );

    cout << "斐波那契数列前 " << n << " 个数:\n";
    for (int i = 1; i <= n; i++) 
    {
        cout << fibonacci(i) << " ";
        if (i % 10 == 0)
        {
            cout << "\n";            
        }
        else if (i == n)
        {
            cout << "\n";
        }
    }
    return 0;
}

fib.cpp

int fibonacci(int n) {
    if (n <= 1)
        return n;
    int a = 0, b = 1, c;
    for (int i = 2; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return b;
}

fib.hpp

#pragma once

int fibonacci(int n);

makefile

# Using functions

SOURCE 	= $(wildcard ./*.cpp)
OBJS	= $(patsubst %.cpp, %.o, $(SOURCE))
TARGET	= main		#change here

CC		= g++
CFLAGS 	= -c -Wall

$(TARGET):$(OBJS)
	$(CC) -o $@ $(OBJS)
%.o: %.cpp
	$(CC) $(CFLAGS) $< -o $@

.PHONY:clean
clean:
	rm -f *.o $(TARGET)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

电子异术家

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

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

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

打赏作者

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

抵扣说明:

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

余额充值