modern

modern,第1张

modern

文章目录

A. First C++ program: Guessing Game (5 points)

A.1 Requirements to pass the testsA.2 TipsA.3 Answer B. Inut Parameters and streams (5points)

B.1 Requirements to pass the testsB.2 Answer
作业地址: homework_2.pdf (uni-bonn.de)

练习内容:C++ Functions

提交的注意事项:

    你需要提供the build system,这意味着你需要编写CMakeLists.txt这次不需要创建results文件夹,所有的目标文件都应该放在cpp_homeworks.homework_2/task_/bin/中你需要检查是否正确使用了clang-format,否则的话,测试可能不通过你所提交的代码应该是被clang-tidy静态分析过没有错误的,否则的话,测试可能不通过你被提供了自动检查器会运行的样例,如果你想要运行这些样例在你的个人电脑上,你需要安装bats一个cmake文件夹也被提供,其中有一些帮助模块,你可能会在创建你的作业时被用到,这些模块包括:

    clang-formatclang-tidycpp_check

A. First C++ program: Guessing Game (5 points)

在这部分,你需要写一个”Guessing“的游戏,程序会在0到99之间随机选择一个整数,你通过stdin输入你所猜测的数字,程序根据你的猜测给出三种反应:

    你已经猜中了数字,程序告诉你已经赢了你猜测的数字比实际数字小,程序告诉你猜小了你猜测的数字比实际数字大,程序告诉你猜大了
A.1 Requirements to pass the tests
    该程序应该被命名为task_1如果遇到错误,该程序应该返回"Error encountered, exiting…"当用户输入不合法的时候,程序应该使用stderr输出一个警告信息"[WARNING] : Number must be between 0 and 99"最后程序通知给用户的成功信息中,必须有产生的随机数字该程序应当一直运行,知道用户输入正确为止所要猜测的数字必须是随机产生的
A.2 Tips
    如果要产生随机数,可以参考random_device: https://en.cppreference.com/w/cpp/numeric/random/random_device你应该使用std::cin.fail()去检查stdin的错误头文件定义了两个宏EXIT_SUCCESS和EXIT_FAILURE去检查程序是运行成功还是运行失败
A.3 Answer

首先,下载解压作业:

$ wget https://www.ipb.uni-bonn.de/html/teaching/modern-cpp-2021/homeworks/homework_2.zip
$ mkdir hm_2
$ unzip homework_2.zip -d ./hm_2/
$ rm -f homework_2.zip

查看作业文件树目录

这里我们新建文件夹task_1,并仿照作业1中的结构,创建task_1/bin/、task_1/include/、task_1/src,分别存放二进制目标文件、头文件和源文件。

mkdir task_1 && cd task_1
mkdir src include src

首先是随机数生成部分,我们参考官网上的使用案例:

#include 
#include 
#include 
#include 
 
int main()
{
    std::random_device rd;
    std::map hist;
    std::uniform_int_distribution dist(0, 9);
    for (int n = 0; n < 20000; ++n) {
        ++hist[dist(rd)]; 
        // note: demo only: the performance of many 
        // implementations of random_device degrades sharply
        // once the entropy pool is exhausted. For practical use
        // random_device is generally only used to seed 
        // a PRNG such as mt19937
    }
    for (auto p : hist) {
        std::cout << p.first << " : " << std::string(p.second/100, '*') << 'n';
    }
}

设计main.cpp的产生随机数部分的代码如下

#include
#include

int main(){
        std::random_device rd;
        std::uniform_int_distribution dist(0,99);
        const int pred_value = dist(rd);
        int user_value = 0;
        while (std::cin>>user_value){
                if ((user_value>=0)&&(user_value<=99)){
                        if(user_value==pred_value){
                                std::cout<<"Your have guessed the number. The Targeted Number equals "<pred_value){
                                std::cout<<"Your input is bigger than the target one"< 

这里我们再处理异常情况,这里要用到std::cin.fail(),可以参考StackOverFlow

std::cin.fail() is used to test whether the preceding input succeeded. It is, however, more idiomatic(惯用的) to just use the stream as if it were a boolean:

if ( std::cin ) {
    //  last input succeeded, i.e. !std::cin.fail()
}

if ( !std::cin ) {
    //  last input failed, i.e. std::cin.fail()
}

In contexts where the syntax of the input permit either a number of a character, the usual solution is to read it by lines (or in some other string form), and parse it; when you detect that there is a number, you can use an std::istringstream to convert it, or any number of other alternatives (strtol, or std::stoi if you have C++11).

It is, however, possible to extract the data directly from the stream:

bool isNumeric;
std::string stringValue;
double numericValue;
if ( std::cin >> numericValue ) {
    isNumeric = true;
} else {
    isNumeric = false;
    std::cin.clear();
    if ( !(std::cin >> stringValue) ) {
        //  Shouldn't get here.
    }
}

于是我们完成main.cpp的最后版本

#include
#include
#include

int main(){
        std::random_device rd;
        std::uniform_int_distribution dist(0,99);
        const int pred_value = dist(rd);
        int user_value = 0;
        while (std::cin>>user_value){
                if ((user_value>=0)&&(user_value<=99)){
                        if(user_value==pred_value){
                                std::cout<<"Your have guessed the number. The Targeted Number equals "<pred_value){
                                std::cout<<"Your input is bigger than the target one"< 

运行结果如下所示:

然后我们需要编写CMakeLists.txt取代手动g++编译,与此同时,还要将生成后的文件放在本地的bin/下

cmake_minimum_required(VERSION 3.1)

project(task_1)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

add_executable(task_1 src/main.cpp)

set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR})

install(TARGETS task_1 
                RUNTIME DESTINATION bin  # 可执行文件安装路径
        )

我们测试代码,结果如下

题目中还要求我们使用clang-format和clang-tidy进行检查,参考我写的这篇博客,安装成功后,运行

clang-format -style=google -i ./main.cpp

可以看到格式都已经调整好了

接下来运行clang-tidy,进行静态代码分析

clang-tidy --checks='Checks' main.cpp --  -x c++

其中–checks=可以写‘*’,表示对所有clang-tidy检查项进行检查,上面‘Checks’是指定检查clang-tidy文件里的检查项;main.cpp是要检查的文件;-I是你要包含的头文件路径(可去除);后面-x c++是指定使用c++编译器(很多时候默认是gcc)(可去除)。

下面我们在检查test_task1.sh是否有问题

检查没有问题后,整理下文件夹(删去build文件夹),我们的Task1就完成了,代码结构如下所示(在运行cmake之前)

B. Inut Parameters and streams (5points)

该部分的程序接收两个参数,都是如下格式”.“,你编写的程序要能够处理这些信息,并提供一些关于这些文件的信息,所有的输出都应该是stdout。

B.1 Requirements to pass the tests
    程序要命名为task_2你应该检查输入是否是刚好两个参数,否则的话,应该输出警告如果文件都是.txt后缀,你需要输出第一个数字和最后一个数字之间的平均值如果文件都是.png后缀,你需要输出第一个数字和最后一个数字之间的和如果第一个文件后缀的.txt,第二个文件后缀是.png,你应该输出第一个数字和第二个数字的模除(the modulo devision)如果没有后缀能和上面匹配的话,那么你必须终止程序执行,通过stderr输出错误

特别注意:除了最后一种情况,其他情况都必须使用stdout输出结果

测试样例

$ cd cpp-homework/homework_2
$ ./bin/task_2 100.txt 200.txt # 返回150
$ ./bin/task_2 100.doc 100.doc # Invalid should give an error
B.2 Answer

这里我们同Exercise A一样,新建bin, include,src文件夹,分别存放二进制目标文件、头文件和源程序

Exercise B的重点是main函数传参和条件分支语句的使用,前者的使方法如下所示

int main(int argc,char *argv[])

第一个参数是传参的个数,在本例中可用来判断当前传递参数是否大于2,第二参数是char**类型,可通过索引遍历,例如下面的例子

#include 
using namespace std;
int main(int argc, char* argv[])
{
    for(int i=0;i 

在控制台调用语句如下所示

./main.o hello xm 

这里依次将三个参数传递给argv[0], argv[1],argv[2],分别是./main.o, hello, xm。

传入的参数类型是char *类型的字符串,需要通过atoi和atof函数进行类型转换:

    atoi, 即 ascii to integer,将字符串转换为intatof,即ascii to float,将字符串转换为doubleatol,即ascii to long int,把字符串转换成long intatoll,即ascii to long long int,把字符串转换成long long int

例如上面的例子中,

 int year = atoi(argv[2]);  // year = 1996

这里还要设计解析读到的两个参数,按照之前的"C-style strings are evil",我们应该使用string类型保存,这里解析出文件名前面的数字,有两种方案:

    通过argv[1].find('.')找到文件后缀的初始位置,然后通过strcpy将之前的部分拷贝到另一变量,然后通过atoi转化为整数类型;使用sscanf(str,%d,&n)搭配正则表达式,可以很快将问题解决

这里采用第二种方案,main.cpp代码如下所示

#include 
#include 
#include 

int main(int argc, char *argv[]) {
  if (argc != 3) {
    std::cerr << "Error occured, Exiting..." << std::endl;
    return EXIT_FAILURE;
  }
  std::string file1 = argv[1];
  std::string file2 = argv[2];

  int a = 0;
  int b = 0;
  sscanf(&file1[0], "%d", &a);
  sscanf(&file2[0], "%d", &b);

  std::string type1 = file1.substr(file1.find('.'));
  std::string type2 = file2.substr(file2.find('.'));

  std::string type_txt = ".txt";
  std::string type_png = ".png";

  if (type1 == type_txt && type2 == type_txt) {
    std::cout << (a + b) / 2.0 << std::endl;
  } else if (type1 == type_png && type2 == type_png) {
    std::cout << (a + b) << std::endl;
  } else if (type1 == type_txt && type2 == type_png) {
    std::cout << a % b << std::endl;
  } else {
    std::cerr << "Error occured, Exiting..." << std::endl;
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}

然后我们编写本部分的CMakeLists.txt

cmake_minimum_required(VERSION 3.1)

project(task_2)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

add_executable(task_2 src/main.cpp)

set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR})

install(TARGETS task_2 
                RUNTIME DESTINATION bin  # 可执行文件安装路径
        )

然后进行测试

结果一切OK

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5711600.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存