Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion exercises/00_hello_world/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

int main(int argc, char **argv) {
// TODO: 在控制台输出 "Hello, InfiniTensor!" 并换行
std::cout : "Hello, InfiniTensor!" + std::endl;
std::cout << "Hello, InfiniTensor!" << std::endl;
return 0;
}
6 changes: 3 additions & 3 deletions exercises/01_variable&add/main.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#include "../exercise.h"

// READ: 运算符 <https://zh.cppreference.com/w/cpp/language/expressions#.E8.BF.90.E7.AE.97.E7.AC.A6>
// READ: 运算符 <https://zh.cppreference.com/w/cpp/language/expressions#.E8.BF.90.E7.AE.97.E7.AC.A6.A6>

int main(int argc, char **argv) {
// TODO: 补全变量定义并打印加法运算
// x ?
int x = 5;// 在这里定义变量 x,类型为 int(整数)
std::cout << x << " + " << x << " = " << x + x << std::endl;
return 0;
}
}
11 changes: 5 additions & 6 deletions exercises/02_function/main.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
#include "../exercise.h"

// READ: 声明 <https://zh.cppreference.com/w/cpp/language/declarations>
// NOTICE: cppreference 中的示例中指出了复杂声明的解读法,建议认真阅读。
// NOTICE: 补充由内而外读法的机翻解释 <https://learn.microsoft.com/zh-cn/cpp/c-language/interpreting-more-complex-declarators?view=msvc-170>

// TODO: 在这里声明函数
int add(int a, int b);// <--- 添加这一行(函数原型声明)

int main(int argc, char **argv) {
// 这里调用 add,编译器现在知道它是一个接收两个 int 并返回 int 的函数了
ASSERT(add(123, 456) == 123 + 456, "add(123, 456) should be 123 + 456");

auto x = 1, y = 2;
std::cout << x << " + " << y << " = " << add(x, y) << std::endl;
return 0;
}

// TODO: 补全函数定义
int add(int a, int b) {
// TODO: 补全函数定义,但不要移动代码行
}
return a + b;// <--- 在这里补全逻辑
}
20 changes: 13 additions & 7 deletions exercises/03_argument&parameter/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,28 @@

void func(int);

// TODO: 为下列 ASSERT 填写正确的值
int main(int argc, char **argv) {
auto arg = 99;
ASSERT(arg == ?, "arg should be ?");
// arg 初始值为 99
ASSERT(arg == 99, "arg should be 99");
std::cout << "befor func call: " << arg << std::endl;

func(arg);
ASSERT(arg == ?, "arg should be ?");

// 关键点:由于是值传递,func 内部的操作不改变 arg
ASSERT(arg == 99, "arg should be 99");
std::cout << "after func call: " << arg << std::endl;
return 0;
}

// TODO: 为下列 ASSERT 填写正确的值
void func(int param) {
ASSERT(param == ?, "param should be ?");
// 进入函数时,param 是 arg 的副本,值为 99
ASSERT(param == 99, "param should be 99");
std::cout << "befor add: " << param << std::endl;

param += 1;
ASSERT(param == ?, "param should be ?");

// 修改后,副本 param 变成了 100
ASSERT(param == 100, "param should be 100");
std::cout << "after add: " << param << std::endl;
}
}
25 changes: 14 additions & 11 deletions exercises/04_static/main.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
#include "../exercise.h"

// READ: `static` 关键字 <https://zh.cppreference.com/w/cpp/language/storage_duration>
// THINK: 这个函数的两个 `static` 各自的作用是什么?
static int func(int param) {
// 只有第一次调用时,static_ 会被赋值为 param (即 5)
// 之后的调用都会跳过这一行初始化
static int static_ = param;
// std::cout << "static_ = " << static_ << std::endl;
return static_++;
return static_++;// 返回当前值,然后 static_ 自增 1
}

int main(int argc, char **argv) {
// TODO: 将下列 `?` 替换为正确的数字
ASSERT(func(5) == ?, "static variable value incorrect");
ASSERT(func(4) == ?, "static variable value incorrect");
ASSERT(func(3) == ?, "static variable value incorrect");
ASSERT(func(2) == ?, "static variable value incorrect");
ASSERT(func(1) == ?, "static variable value incorrect");
// 第一次调用:static_ 初始化为 5,返回 5,随后 static_ 变为 6
ASSERT(func(5) == 5, "static variable value incorrect");
// 第二次调用:跳过初始化,返回 6,随后 static_ 变为 7
ASSERT(func(4) == 6, "static variable value incorrect");
// 第三次调用:返回 7,随后 static_ 变为 8
ASSERT(func(3) == 7, "static variable value incorrect");
// 第四次调用:返回 8,随后 static_ 变为 9
ASSERT(func(2) == 8, "static variable value incorrect");
// 第五次调用:返回 9,随后 static_ 变为 10
ASSERT(func(1) == 9, "static variable value incorrect");
return 0;
}
}
11 changes: 7 additions & 4 deletions exercises/05_constexpr/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ int main(int argc, char **argv) {
std::cout << "fibonacci(20) = " << FIB20 << std::endl;

// TODO: 观察错误信息,修改一处,使代码编译运行
// PS: 编译运行,但是不一定能算出结果……
constexpr auto ANS_N = 90;
constexpr auto ANS = fibonacci(ANS_N);
constexpr auto ANS_N = 30;

// 修改处:将 constexpr 改为 const 或 auto

const auto ANS = fibonacci(ANS_N);

std::cout << "fibonacci(" << ANS_N << ") = " << ANS << std::endl;

return 0;
}
}
11 changes: 6 additions & 5 deletions exercises/06_array/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ unsigned long long fibonacci(int i) {
case 1:
return 1;
default:
// TODO: 补全三目表达式缺失的部分
return <condition> ? <cache> : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2));
// 使用记忆化搜索:如果缓存中已有值,直接返回;否则进行递归并存入缓存
return arr[i] != 0 ? arr[i] : (arr[i] = fibonacci(i - 1) + fibonacci(i - 2));
}
}

int main(int argc, char **argv) {
// TODO: 为此 ASSERT 填写正确的值
ASSERT(sizeof(arr) == ?, "sizeof array is size of all its elements");
// 90 个元素 * 每个 unsigned long long 占用 8 字节 = 720
ASSERT(sizeof(arr) == 720, "sizeof array is size of all its elements");

// ---- 不要修改以下代码 ----
ASSERT(fibonacci(2) == 1, "fibonacci(2) should be 1");
ASSERT(fibonacci(20) == 6765, "fibonacci(20) should be 6765");
ASSERT(fibonacci(80) == 23416728348467685, "fibonacci(80) should be 23416728348467685");
return 0;
}
}
16 changes: 9 additions & 7 deletions exercises/07_loop/main.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
#include "../exercise.h"

// TODO: 改正函数实现,实现正确的缓存优化斐波那契计算
// THINk: 这个函数是一个纯函数(pure function)吗?
// READ: 纯函数 <https://zh.wikipedia.org/wiki/%E7%BA%AF%E5%87%BD%E6%95%B0>
static unsigned long long fibonacci(int i) {
// TODO: 为缓存设置正确的初始值
static unsigned long long cache[96], cached;
// TODO: 设置正确的循环条件
for (; false; ++cached) {
// 使用静态变量,cached 记录当前已经缓存到的最大索引
// 初始化前两项:cache[0]=0, cache[1]=1,已经缓存到索引 1
static unsigned long long cache[96]{0, 1}, cached = 2;

// 如果请求的 i 已经超出了当前缓存的范围,则继续计算
// 循环条件:只要 cached 还没达到或超过需要的 i + 1(即还没算出第 i 项),就继续
for (; cached <= i; ++cached) {
cache[cached] = cache[cached - 1] + cache[cached - 2];
}

return cache[i];
}

Expand All @@ -25,4 +27,4 @@ int main(int argc, char **argv) {
std::cout << "fibonacci(90) = " << fib90 << std::endl;
ASSERT(fib90 == 2880067194370816120, "fibonacci(90) should be 2880067194370816120");
return 0;
}
}
23 changes: 20 additions & 3 deletions exercises/08_pointer/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,20 @@
// READ: 数组向指针退化 <https://zh.cppreference.com/w/cpp/language/array#%E6%95%B0%E7%BB%84%E5%88%B0%E6%8C%87%E9%92%88%E7%9A%84%E9%80%80%E5%8C%96>
bool is_fibonacci(int *ptr, int len, int stride) {
ASSERT(len >= 3, "`len` should be at least 3");
// TODO: 编写代码判断从 ptr 开始,每 stride 个元素取 1 个元素,组成长度为 n 的数列是否满足
// arr[i + 2] = arr[i] + arr[i + 1]

// 我们需要检查 len 个逻辑元素,索引范围从 0 到 len-1
// 逻辑上的第 i 个元素在内存中的位置是 ptr + i * stride
for (int i = 0; i < len - 2; ++i) {
// 获取逻辑上连续的三个元素
int a = *(ptr + i * stride);
int b = *(ptr + (i + 1) * stride);
int c = *(ptr + (i + 2) * stride);

// 如果不满足 a + b = c,则不是斐波那契数列
if (a + b != c) {
return false;
}
}
return true;
}

Expand All @@ -13,6 +25,11 @@ int main(int argc, char **argv) {
int arr0[]{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55},
arr1[]{0, 1, 2, 3, 4, 5, 6},
arr2[]{99, 98, 4, 1, 7, 2, 11, 3, 18, 5, 29, 8, 47, 13, 76, 21, 123, 34, 199, 55, 322, 0, 0};

// 测试点分析:
// arr2 + 2 从 4 开始,步长 2 取出:4, 7, 11, 18, 29, 47, 76, 123, 199, 322 (满足)
// arr2 + 3 从 1 开始,步长 2 取出:1, 2, 3, 5, 8, 13, 21, 34, 55 (满足)

// clang-format off
ASSERT( is_fibonacci(arr0 , sizeof(arr0) / sizeof(*arr0) , 1), "arr0 is Fibonacci" );
ASSERT( is_fibonacci(arr0 + 2, sizeof(arr0) / sizeof(*arr0) - 4, 1), "part of arr0 is Fibonacci" );
Expand All @@ -25,4 +42,4 @@ int main(int argc, char **argv) {
ASSERT(!is_fibonacci(arr2 + 1, 10 , 2), "guard check" );
// clang-format on
return 0;
}
}
23 changes: 7 additions & 16 deletions exercises/09_enum&union/main.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
#include "../exercise.h"

// READ: 枚举类型 <https://zh.cppreference.com/w/cpp/language/enum>

// `enum` 是 C 的兼容类型,本质上其对应类型的常量。
// 在 `enum` 中定义标识符等价于定义 constexpr 常量,
// 这些标识符不需要前缀,可以直接引用。
// 因此 `enum` 定义会污染命名空间。
// 非作用域枚举
enum ColorEnum : unsigned char {
COLOR_RED = 31,
COLOR_GREEN,
COLOR_YELLOW,
COLOR_BLUE,
};

// 有作用域枚举型是 C++ 引入的类型安全枚举。
// 其内部标识符需要带前缀引用,如 `Color::Red`。
// 作用域枚举型可以避免命名空间污染,并提供类型安全保证。
// 有作用域枚举型
enum class Color : int {
Red = COLOR_RED,
Green,
Expand All @@ -24,20 +17,18 @@ enum class Color : int {
};

ColorEnum convert_by_pun(Color c) {
// READ: <https://zh.cppreference.com/w/cpp/language/union>
// `union` 表示在同一内存位置存储的不同类型的值。
// 其常见用法是实现类型双关转换,即将一种类型的值转换为另一种无关类型的值。
// 但这种写法实际上仅在 C 语言良定义,在 C++ 中是未定义行为。
// 这是比较少见的 C++ 不与 C 保持兼容的特性。
// READ: 类型双关 <https://tttapa.github.io/Pages/Programming/Cpp/Practices/type-punning.html>
union TypePun {
ColorEnum e;
Color c;
};

TypePun pun;
// TODO: 补全类型双关转换
// 将 Color 类型的值写入联合体成员 c
pun.c = c;

// 从同一内存位置读取为 ColorEnum 类型
// 虽然在 C++ 中是未定义行为,但内存中它们共享起始地址
return pun.e;
}

Expand All @@ -47,4 +38,4 @@ int main(int argc, char **argv) {
ASSERT(convert_by_pun(Color::Yellow) == COLOR_YELLOW, "Type punning conversion");
ASSERT(convert_by_pun(Color::Blue) == COLOR_BLUE, "Type punning conversion");
return 0;
}
}
15 changes: 9 additions & 6 deletions exercises/10_trivial/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@ struct FibonacciCache {

// TODO: 实现正确的缓存优化斐波那契计算
static unsigned long long fibonacci(FibonacciCache &cache, int i) {
for (; false; ++cached) {
cache[cached] = cache[cached - 1] + cache[cached - 2];
// 逻辑:从当前已经缓存的位置开始计算,直到达到索引 i
// 注意:访问结构体成员需要使用 . 运算符
for (; cache.cached <= i; ++cache.cached) {
cache.cache[cache.cached] = cache.cache[cache.cached - 1] + cache.cache[cache.cached - 2];
}
return cache.cache[i];
}

int main(int argc, char **argv) {
// TODO: 初始化缓存结构体,使计算正确
// NOTICE: C/C++ 中,读取未初始化的变量(包括结构体变量)是未定义行为
// READ: 初始化的各种写法 <https://zh.cppreference.com/w/cpp/language/initialization>
FibonacciCache fib;
// 初始化 cache 数组的前两项为 {0, 1},并将 cached 设为 2(表示已缓存两项)
// 其余数组元素会自动初始化为 0
FibonacciCache fib{{0, 1}, 2};

ASSERT(fibonacci(fib, 10) == 55, "fibonacci(10) should be 55");
std::cout << "fibonacci(10) = " << fibonacci(fib, 10) << std::endl;
return 0;
}
}
10 changes: 7 additions & 3 deletions exercises/11_method/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ struct Fibonacci {

// TODO: 实现正确的缓存优化斐波那契计算
unsigned long long get(int i) {
for (; false; ++cached) {
// 直接访问成员变量 cached 和 cache
// 循环条件:计算到索引 i 为止
for (; cached <= i; ++cached) {
cache[cached] = cache[cached - 1] + cache[cached - 2];
}
return cache[i];
Expand All @@ -15,8 +17,10 @@ struct Fibonacci {

int main(int argc, char **argv) {
// TODO: 初始化缓存结构体,使计算正确
Fibonacci fib;
// 初始化前两项为 {0, 1},并记录已缓存到索引 2
Fibonacci fib{{0, 1}, 2};

ASSERT(fib.get(10) == 55, "fibonacci(10) should be 55");
std::cout << "fibonacci(10) = " << fib.get(10) << std::endl;
return 0;
}
}
12 changes: 10 additions & 2 deletions exercises/12_method_const/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@

struct Fibonacci {
int numbers[11];

// TODO: 修改方法签名和实现,使测试通过
int get(int i) {
// 添加 const 限定符,表示该函数不会修改结构体内部的数据
// 只有这样,constexpr 对象 FIB 才能调用它
int get(int i) const {
return numbers[i];
}
};

int main(int argc, char **argv) {
// FIB 是一个编译期常量对象
Fibonacci constexpr FIB{{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}};

// 如果 get 不是 const 的,这里会报编译错误:
// “不能将“this”指针从“const Fibonacci”转换为“Fibonacci &””
ASSERT(FIB.get(10) == 55, "fibonacci(10) should be 55");
std::cout << "fibonacci(10) = " << FIB.get(10) << std::endl;
return 0;
}
}
Loading