关于CLion的基本的安装和配置建议

可能有人要问我了:为什么要用这个IDE?这么复杂的安装过程我为什么不用Dev-C++或者CodeBlocks?VC6.0多好用啊!

这个…我的回答是:CLion确实是一个优秀的、强大的跨平台C/C++ IDE;安装过程本身不复杂,官网提供了详尽的文档;此外,理解(划重点)并按照向导一步步做下来也是相当简单的,写这篇主要是因为…很多同学真的看到英文就害怕,对此我只能引用一下这段话:

“英文在科学技术领域是世界语”这个事实在未来几十年都不会改变。我们在授课过程中应用的非技术词汇都很简单。因此停止抱怨,以开放的心态来迎接挑战吧。你会发现,其实挑战也并不大。

学堂在线 《电路原理》 于歆杰教授 朱桂萍教授 陆文娟教授 清华大学电机系

至于它的特性和优点,你可以看这里:Intelligent Coding Assistance & Code Analysis – Features | CLion。如果新手的你看不懂的话,我大概可以告诉你这几点:

  • 静态代码审查:在构建前即可知道代码中存在的问题,一般情况下一些比较傻的问题都能很快发现,可以节约大量的时间。这些提示对于提高你的代码质量也有帮助,我觉得这点非常重要。
  • 智能代码补全:你可以利用多种技术来进行补全,包括但不限于补全关键字、变量名或者使用模板,可以大幅提高编写代码的速度。
  • 你可以比较方便地在考试机房的电脑上安装并使用,而不需要花费很长时间,然而你可能需要等待较长时间才能把Visual Studio 2019(或者未来的更高版本)部署好。

Anyway,现在已经是2020年了,我再次修订这篇文章,还是那句话:建议你在学习和未来的开发过程中使用先进的工具。包括但不限于:

如果你期望使用Visual Studio,那么你只要到官网上下载对应的Installer,选中你需要的开发用途并安装就可以了。由于会自动配置工具链,整个安装过程非常友好且不劝退,安装好之后马上就可以开始使用。我建议你再装上ReSharper Ultimate,这可能对你学习或是开发有很大的帮助。

下文主要探讨CLion的一些配置建议。但不管怎样,这些都只是工具而已,水平怎么样还是要看个人,没有必要非要争个谁更厉害,甚至是用出优越感来。更重要的是你平时有没有努力学习和探索,给你个宇宙第一IDE,用了几年还是只会按几个常用的功能,其它的都不碰的话,那跟你一开始就用文本编辑器似乎没什么区别,甚至编辑器还要更快一点。

废话了这么多,开始说正事,希望我写的这些能让你看到,为什么它值得我们去用。此外,这不是教程向,也不会手把手地教你并给你截图,我始终反对无脑跟随一个教程的做法,希望你会理解后再去动手。

安装CLion

作为计算机专业学生,如果需要安装某某应用软件,正确的操作应当是前往官网,阅读说明后下载并安装。而非点击某某下载站的“快速下载”按钮。

你会发现,你可以选择直接下载CLion的安装程序进行安装;也可以选择下载JetBrains Toolbox,然后用Toolbox来安装并管理包括CLion在内的JetBrains IDE。我更推荐后者,虽然多了个应用,但是方便你管理IDE和最近的项目,并且可以静默为你安装更新。

那我就假设你已经安装好了Toolbox并且在当中安装了CLion。我的Toolbox大概长这样:

JetBrains Toolbox

申请教育授权

JetBrains全家桶对于教育用途来说都是免费的,具体可以自行前往这里查看。

不用我说,你应该要能找到这里,点击进入申请即可。通常情况下,这里会使用教育邮箱来验证你的身份,也就是学校为你提供的那个。

(深大特供)我想想估计又有好多同学会问:我忘记了密码怎么办?教育邮箱是什么?首先,忘记了密码,目前请持校园卡前往沧海校区(原南校区)致理楼C(原理工楼L3)1007进行重置,重置后记得在设置中绑定你的微信,这样的话,以后忘记密码也可以自己重置了。(最好还是弄个密码管理器)其次,教育邮箱通常是你所在的教育机构提供的、网域归属于该教育机构的邮箱。像我这样的人脑收藏夹应该比较少,那我就推荐你到内部网里点击这个:

内部网->学生邮箱

其它方式我就不扯了,自己研究吧。不管怎样,你申请到的教育授权有效期应该是1年的。

不过不用慌,每年到期前,你都会收到邮件提醒你续期,只要你没毕业(教育授权政策似乎在慢慢收紧来应对白嫖党)并且持续持有教育邮箱,你就能一直免费使用。

更多关于授权的信息就自己看吧。

启动CLion

不同于往常你可能用过的IDE,这里需要你自行选择并配置Toolchain才能开始构建项目。如果你一路Next来到主界面后就会发现你写的程序没法构建并运行。以前你使用的IDE可能已经为你包办好了一切,希望这不会让你感到特别不习惯。

在不同平台上,所需的工具链通常是不同的,简单说来大概是这样的:

  • Linux。我本来打算写的,但是想想就算是某厂预装了Linux系统笔记本的用户,应该第一时间就安装了Windows。除此之外的Linux用户应该都是能人,不用我来教,只要用包管理把cmakemakegccgdb这些东西装上就好了。
  • MacOS。由于本菜买不起,所以只好给你放个文档了:Quick Tutorial: Configure CLion on macOS – Help | CLion。据文档所述,开发所需的工具应该系统已经事先预装好了,你只需要检测它们是否能正常工作就可以了。
  • Windows,有很多选择。很多选择倒不是说它很好,我个人觉得都是因为不太完美所以才会搞出这么多方案来。包括但不限于WSL(子系统)、Cygwin、MinGW这些。

下面主要简单来说说Windows下的那些方案,具体的还是自己看文档比较好。

Windows Subsystem for Linux (WSL) on Windows 10 适用于Linux的Windows子系统

非常推荐你日常学习和开发用这个方案(和GUI无关的话)。(虽然现在已经有了WSL 2,但是由于它是基于Hyper-V的,开了之后会和VMware、VirtualBox这些应用冲突,有点蛋疼,所以我还没打算迁移过去。如果你想折腾的话,那么请:Install WSL 2 | Microsoft Docs。)

不管怎样,我都觉得看官方文档比较好:WSL – Help | CLion。其实就是在WSL中安装所需的工具链,然后CLion会通过SSH来调用它们。

首先还是要强调一下你需要使用Windows 10(1607之后)或是Windows Server 2019。更多信息请参阅这里:适用于 Linux 的 Windows 子系统 – 维基百科,自由的百科全书

安装WSL其实很简单,你首先需要打开这个特性。你可以在控制面板中的启用或关闭Windows功能中打开它:

控制面板->程序->启用或关闭Windows功能->适用于Linux的Windows子系统

当然,这和直接在PowerShell中执行如下语句是等效的:

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

打开该特性后请务必重启你的电脑,否则并不会生效,后续打开WSL会直接提示The Windows Subsystem for Linux optional component is not enabled. Please enable it and try again.

随后在Windows Store中搜索WSL并安装你需要的Linux发行版:

不得不接受的现实是,目前还是Ubuntu的支持最好。

下面不打算假设你对Linux有多少了解,我只希望你会自己去了解…

首次打开Ubuntu时会让你设定用户名和密码,随后就可以进入系统。我们需要来安装软件包,在这之前,你需要先刷新软件包列表:

sudo apt update

如果成功,那么随后你就可以开始安装软件包了:

sudo apt install cmake make clang gcc g++ build-essential gdb

如果你发现下载软件包的速度慢到难以接受,那么更换软件源可能是最简单粗暴的解决方法。例如你可以到这里查看阿里提供的镜像的文档,照抄不是明智的做法,直接改域名可能更好一点。如果更换软件源,你需要重新执行apt update才会生效,然后再进行安装。

至此工具链应该已经安装好了。前面提到,CLion会通过SSH去连接WSL来调用工具链,默认情况下刚装好的Ubuntu WSL里的SSH Server没有密钥对,直接启动后应该会连接不上。当然,按照报错信息去找到对应地方生成密钥对再重启SSH Server就可以了。不过有个更简单的方法:

sudo apt remove openssh-server
sudo apt install openssh-server

直接删除并重新安装的时候,在安装过程中会自动生成,也就不用自己去折腾了。然后再启动就可以了。

题外话:注意安全

考虑到我们是学安全的社团,所以特意拿出来强调一下这个点:不要让别人连接你的WSL的SSH Server!除非你知道你在做什么。不过我敢说大部分时候大部分人都是不知道自己在做什么的……大部分教程是没有强调这个问题的。简单说来,SSH是用来远程通过命令行控制你的主机的,具体请出门左转wiki,不正确的配置可能会导致被恶意连接,从而造成系统被破坏的后果。SSH Server的配置文件默认应该会在/etc/ssh/sshd_config,默认情况下应该会监听来自所有地址的请求,也就是那个ListenAddress 0.0.0.0。如果你只有在本机连接的需求,那么改为只监听本机可能会是个好主意。别忘了还有IPv6,虽然地址量的庞大使得扫描变得困难,但是只监听本机仍然有助于保护你自己。

只监听本机有助于保护你,除非你有远程连接的需求

修改配置文件后,请记得重启你的SSH Server以使得更改生效。

此外,如果你只是希望依赖防火墙来阻断端口22的入站连接,那么你可能要花费更多的精力来保证防火墙策略和区域设置正确。既然如此,那为啥不一开始就直接设置成仅本机访问呢。

回归正题,如果配置好了之后就可以来启动SSH Server了:

sudo service ssh start

我建议你马上测试一下是否能连接成功:

ssh 你希望登录的用户名@localhost -p22

如果不是很有必要,我不建议你直接root用户登录。

如果出现类似Connection refused的错误信息,那么说明服务可能没有开启,你可以通过类似sudo service ssh status的命令来检查;如果出现类似密钥交换失败的错误,请尝试检查密钥对;如果出现认证失败的错误,你可能需要检查一下配置文件中的认证策略,例如:

不管怎样,这些都应该能在Google上找到答案,我就不多说了。

如果能成功建立连接,那么说明大部分工作已经完成了。你可以回到CLion中,简单设置一下连接信息就可以使用了。你可以从Settings->Build, Execution, Deployment->Toolchains进入,也可以直接两下Shift来Search Everywhere,然后直接搜索Toolchains并进入:

如果检测都正确,那么你应该已经可以开始正常使用CLion了。

如果重启电脑后SSH Server没有启动,那么你可能会看到类似的错误信息:

很简单,启动SSH Server后,再点按Reload CMake Project就可以了:

MinGW,通过MSYS2

在这之前,我建议你先去了解一下MinGW、MinGW-w64还有MSYS2。

这也是个不错的方案(特别是和GUI相关的时候)。首先你要下载并安装MSYS2,根据文档快速开始,这应该没有什么问题。

然后安装工具链:

选好路径就可以开始工作了:

程序员也应当非常重视警告(Warning)。CLion可能会对超出版本范围的工具提示警告,不管是更新还是更旧。这都说明没有经过足够的测试来保证该版本的兼容性。如果你后续在调试阶段出现问题,那么可能要安装合适版本的调试器。

MinGW,通过直接在线/离线安装mingw-w64

你可以前往Mingw-w64 – GCC for Windows 64 & 32 bits [mingw-w64]了解更多信息。然后根据实际情况选择安装方式。如果你希望在没有网络的考试环境部署,那么直接离线安装可能是比较好的方案。

这个比较简单,其实没什么好说的,只要在CLion的工具链选项中指定你的安装/解压后的mingw-w64的路径就可以了。默认在线安装的话,CLion应该可以直接自动检测到。

Cygwin

也是个不错的方案,前往Cygwin查看更多信息。也是类似的下载安装,不过由于Cygwin并没有自带包管理,所以你需要在安装过程中就安装工具链,而没法像MSYS2那样。

记得在这里安装CMake、make、gcc-core、gcc-g++和gdb

其实CLion文档中只要求安装gcc-g++、make和gdb。

同样的,安装成功后在CLion中的工具链选项中配置好就要可以使用了。

小结

对了,不管你使用什么工具链,只要某些路径中涉及了非ASCII字符,例如中文用户名,那么可能会出现各种难以预料的错误。即使你在设置中更改了用户名,个人文件夹也不会改变,就算试图更改注册表也不一定稳妥。你可以使用各种链接的操作来试图避开,不过我觉得还是重新安装系统来得好一点。

调试?

毫无疑问,调试(Debug)在编写程序的过程中几乎是不可缺少的。如果你在上面配置工具链时Debugger已经可以工作,那么就不再需要额外的工作了。而如果你调用了Visual Studio的工具链,那么可能不能启动调试。(至少我上次看文档的时候还是不支持的,不过印象中可以调用其它Debugger的样子)

在CLion中进行调试也非常简单,基本的调试只需要点按编辑器左侧的行数就可以打下断点,然后点按右上角Debug图标启动调试即可。同样,你也可以使用快捷键Ctrl+F8来打断点、Shift+F9来启动调试。

启动调试之后,像Step Over、Step Into、Step Out之类的我觉得你应该自己去了解了。此外,调试不应该只停留在查看变量的阶段。例如,如果你在调试一个递归或者哪怕仅仅只是若干函数调用的程序,那么查看栈帧(Frames)可能会有所帮助。如果有些UI中没有实现的功能,你可以直接在GDB窗口中自行输入命令。

当然,我不希望你错过这个强大的功能:Evaluate Expression…(Alt+F8)

你可以利用这个功能,在当前现场像脚本语言一样执行你所需要的语句,这在很多时候都非常有用

智慧树时间:你可能不知道的IntelliJ/CLion

想到一个写一个吧……

Do not use mouse in IntelliJ, mouse is the evil.

Hadi Hariri

你知道吗?复制(Ctrl+C)、剪切(Ctrl+X)和粘贴(Ctrl+V)整行代码并不需要手动选中,不选中就是选中整行。想要重复一行?直接Ctrl+D吧。

这看起来很简单,对吗?你是否厌倦了还要移动右手去调整方向键才能补全例如ifwhile等语句的花括号?我想你还需要Complete Current Statement(Ctrl+Shift+Enter)。

Before
After

想为一个语句加上if吗?别再手动加括号了,直接对选中的语句使用Surround With…(Ctrl+Alt+T)吧!

什么?你说很多变量名起得不好,想改很麻烦,还怕有副作用?别慌,使用Refactor->Rename(Shift+F6),把这些让人头疼的问题交给IDE去分析就好了。相信我,它会做得又快又好的。

嗯……IntelliJ/CLion已经是一个成熟的IDE了,按下Generate(Alt+Ins)就可以帮你生成代码了。

代码经过几手修改,格式乱七八糟?试试Reformat Code(Ctrl+Alt+L)或许会有所帮助的。

糟糕,我的代码上出现了一个黄色/红色的灯泡。别慌,这说明IDE可能想要帮助你!触发Show Intention Actions(Alt+Enter)来看看吧。说不定,这就是以后你最经常使用的快捷键之一了。

找不到想要的东西吗?Search Everywhere(连续的两下Shift)和Run Anything(连续的两下Ctrl)或许能帮到你。

随便说了几个,更多精彩请自行翻阅每天都会弹出的Tip of the Day。对了,安装Presentation Assistant插件试试吧,我敢打赌你不会后悔的。

别人家的小朋友都有缩略代码的滚动条

很简单,安装CodeGlance插件就可以了。

这个主题天天看,看腻了,有没有猛男专用的颜色

喵?安装Material Theme UI插件看看有没有你需要的?在设置里你想怎么搞颜色都可以。

图标包自5.0.0+后的版本被分离出来了,下载Atom Material Icons就可以了。

Compare with Clipboard

什么?你说你又过不了OJ?它说Wrong Answer的时候,别再自己人工对比了!复制样例数据,回到你的程序的输出窗口,让IDE帮你对比它们吧。

Search with Google

遇到了问题?不要睡大觉,直接问Google吧!直接将选中的文字送到搜索引擎上!

以前在右键菜单里可以直接触发的,刚发现2020.1似乎移除了这个选项。不过我们仍然可以在Settings->Keymap中设置快捷键,或者在Settings->Appearance & Behavior->Menus and Toolbars里重新加回来。

不同Project设置不同的工具链?

你或许曾经有过这样的困扰,为了不同的用途,你安装了多个工具链。但是你发现每次切换项目似乎都要切换工具链?不,你只需要在Settings->Build, Execution, Deployment->CMake中设置工具链就可以了。

拷贝到Word后,行间距有违和的白色?

试着在这段代码前面加上一个回车吧,你会回来感谢我的。

鸡蛋队列(数组,函数)

时间限制: 1 Sec 内存限制: 128 MB

题目描述

将两根筷子平行的放在一起,就构成了一个队列。将带有编号的鸡蛋放到两根筷子之间叫做入队(push),将筷子之间的鸡蛋拿出来叫做出队(pop)。但这两种方式有特殊的定义,对于入队,只能将鸡蛋从队列的尾部向里放入;对于出队,只能将鸡蛋从队列的头部向外将鸡蛋拿出来。

输入

第一行输入一个数T,表示有T组数据 第二行输入一个数N,表示有N(N<=10)种操作 接下来N行,每行一种操作,push表示将编号为x的鸡蛋放入队列中,pop表示拿走队列头部的一个鸡蛋。 数据输入保证合法,队列中没有鸡蛋时不会有出队操作!

输出

输出N种操作完之后,队列中蛋蛋的编号,如果没蛋了,就输出”no eggs!”(不包括引号)每组输出占一行。

样例输入

2
3
push 1 
push 2
push 3
2
push 1
pop

样例输出

1 2 3
no eggs!

提示

数组模拟队列,用下标记录对头、队尾位置。

解决方案

#include <stdio.h>

struct Queue {
    int data[32];
    int head, tail;
};

void push(struct Queue *queue, int data);
void pop(struct Queue *queue);
void show(struct Queue *queue);

int main() {
    int ctrl;
    scanf("%d", &ctrl);

    while (ctrl--) {
        struct Queue queue = {};
        int time;
        scanf("%d", &time);
        while (time--) {
            char operation[8];
            scanf("%s", operation);
            if (operation[1] == 'u') {
                int data;
                scanf("%d", &data);
                push(&queue, data);
            } else {
                pop(&queue);
            }
        }
        show(&queue);
    }

    return 0;
}

void push(struct Queue *queue, int data) {
    queue->data[queue->tail] = data;
    queue->tail += 1;
}

void pop(struct Queue *queue) {
    queue->head += 1;
}

void show(struct Queue *queue) {
    if (queue->head == queue->tail) {
        printf("no eggs!\n");
    } else {
        printf("%d", queue->data[queue->head]);
        for (int i = queue->head + 1; i != queue->tail; ++i) {
            printf(" %d", queue->data[i]);
        }
        printf("\n");
    }
}

分类排序(函数)

时间限制: 1 Sec 内存限制: 128 MB

题目描述

从键盘输入10个整数(10个整数均不相同), 然后再从键盘中输入一个整数a,
如果该整数a为奇数, 且与这10个整数中的任一个数相同,则删掉与a相同的 这个数,并将剩下的9个数按升序排序输出;
若a为偶数, 且与这10个数都不同,则加入a,并将这11个数降序排 序输出;
否则,则将这10个数奇数在前,偶数在后, 降序排列输出。
例如,10个数分别为6,89,34,12, 0,-8,78,15,9,3.
若a为9,则输出为:-8,0,3, 6,12,15,34,78,89.
若a为2,则输出为:89,78, 34,15,12,9,6,3,2,0,-8.
若a为7或者12,则 输出为:89,15,9,3,78,34,12,6,0,-8.
要求:使用函数解决本题,至少编写以下几个函数,
1、升序排序函数void sort(int a[], int n);
2、查找函数int find(int a[],int n,int value)—-在大小为n的a数组中查找值为value的元素,找到就返回元素的下标,找不到,就返回-1;
3、输出函数display(int a[], int n)—逐个输出数组a的元素;
4、反转数组函数void reverse(int a[], int n)—-将数组逆序。

输入

测试数据的组数n
第一组测试数据的10个整数
第一组的整数a
第二组测试数据的10个整数
第二组的整数a
……
第n组测试数据的10个整数
第n组的整数a

输出

第一组数据排序输出
第二组数据排序输出
…….
第n组数据排序输出

样例输入

3
6 89 34 12 0 -8 78 15 9 3
9
6 89 34 12 0 -8 78 15 9 3
2
6 89 34 12 0 -8 78 15 9 3
7

样例输出

-8 0 3 6 12 15 34 78 89
89 78 34 15 12 9 6 3 2 0 -8
89 15 9 3 78 34 12 6 0 -8

提示

解决方案

#include <stdio.h>

#define FAILED_FIND_IN_ARRAY -1

struct Array {
    int data[16];
    int size;
};

void scanToArray(struct Array *array);
int findInArray(struct Array *array, int data);
void sortArray(struct Array *array);
void reverseArray(struct Array *array);
void sortArrayInOddAndEven(struct Array *array);
void printArray(struct Array *array);

int main() {
    int ctrl;
    scanf("%d", &ctrl);

    while (ctrl--) {
        struct Array array = {};
        scanToArray(&array);
        int number;
        scanf("%d", &number);
        if (number % 2 != 0 && findInArray(&array, number) != FAILED_FIND_IN_ARRAY) {
            array.size -= 1;
            for (int i = findInArray(&array, number); i < array.size; ++i) {
                array.data[i] = array.data[i + 1];
            }
            sortArray(&array);
        } else if (number % 2 == 0 && findInArray(&array, number) == FAILED_FIND_IN_ARRAY) {
            array.data[array.size] = number;
            array.size += 1;
            sortArray(&array);
            reverseArray(&array);
        } else {
            sortArrayInOddAndEven(&array);
        }
        printArray(&array);
    }

    return 0;
}

void scanToArray(struct Array *array) {
    array->size = 10;
    for (int i = 0; i < array->size; ++i) {
        scanf("%d", &array->data[i]);
    }
}

int findInArray(struct Array *array, int data) {
    for (int i = 0; i < array->size; ++i) {
        if (array->data[i] == data) {
            return i;
        }
    }
    return -1;
}

void sortArray(struct Array *array) {
    for (int i1 = 0; i1 < array->size - 1; ++i1) {
        for (int i2 = 0; i2 < array->size - 1 - i1; ++i2) {
            if (array->data[i2] > array->data[i2 + 1]) {
                int data = array->data[i2];
                array->data[i2] = array->data[i2 + 1];
                array->data[i2 + 1] = data;
            }
        }
    }
}

void reverseArray(struct Array *array) {
    for (int i = 0; i < array->size / 2; ++i) {
        int data = array->data[i];
        array->data[i] = array->data[array->size - 1 - i];
        array->data[array->size - 1 - i] = data;
    }
}

void sortArrayInOddAndEven(struct Array *array) {
    struct Array odd = {}, even = {};
    for (int i = 0; i < array->size; ++i) {
        if (array->data[i] % 2 == 0) {
            even.data[even.size] = array->data[i];
            even.size += 1;
        } else {
            odd.data[odd.size] = array->data[i];
            odd.size += 1;
        }
    }
    sortArray(&odd);
    reverseArray(&odd);
    sortArray(&even);
    reverseArray(&even);
    for (int i = 0; i < odd.size; ++i) {
        array->data[i] = odd.data[i];
    }
    for (int i = 0; i < even.size; ++i) {
        array->data[i + odd.size] = even.data[i];
    }
}

void printArray(struct Array *array) {
    printf("%d", array->data[0]);
    for (int i = 1; i < array->size; ++i) {
        printf(" %d", array->data[i]);
    }
    printf("\n");
}

计算字符串的长度

时间限制: 1 Sec 内存限制: 128 MB

题目描述

计算字符串S的长度,功能与strlen函数相同,但不能调用库函数strlen,否则不给分。输入的字符串不包含空格。

输入

输入测试组数t
对于每组测试,输入字符串S(长度<=30)

输出

对于每组测试,输出S的长度

样例输入

1
hehe

样例输出

4

提示

解决方案

#include <iostream>

int main() {
    int ctrl;
    std::cin >> ctrl;
    while (ctrl--) {
        char str[64] = {};
        std::cin >> str;
        int length = 0;
        while (str[++length] != '\0');
        std::cout << length << std::endl;
    }

    return 0;
}

单词统计

时间限制: 1 Sec 内存限制: 128 MB

题目描述

编写一个程序,根据单词的出现频率降序打印出所输入的的各个单词。每个单词前标有它的计数值。

输入

各个单词,输入0则表示单词结束

输出

降序打印单词出现的频率和单词。单词频度相同,按出现顺序输出。

样例输入

bad
good
bad
cute
how
good
good
0

样例输出

3 good
2 bad
1 cute
1 how

提示

解决方案

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

struct Pair {
    std::string string;
    int count;
    Pair(std::string &string, int count) : string(string), count(count) {}
};
bool operator==(Pair &pair, const std::string &string) {
    return pair.string == string;
}
bool operator<(const Pair &lhs, const Pair &rhs) {
    return lhs.count > rhs.count; // NG
}

int main() {
    std::vector<Pair> vector;
    std::string string;
    std::cin >> string;
    while (string != "0") {
        std::vector<Pair>::iterator it = std::find(vector.begin(), vector.end(), string);
        if (it != vector.end()) {
            it->count += 1;
        } else {
            vector.push_back(Pair(string, 1));
        }
        std::cin >> string;
    }
    std::sort(vector.begin(), vector.end());
    for (int i = 0; i < vector.size(); ++i) {
        std::cout << vector[i].count << ' ' << vector[i].string << std::endl;
    }

    return 0;
}

求矩阵的最大和最小值

时间限制: 1 Sec 内存限制: 128 MB

题目描述

定义一个函数用一级指针接收一个任意行任意列的矩阵并返回该矩阵元素的最大和最小值.

输入

矩阵的行数 矩阵的列数
矩阵各元素的值

输出

最大值
最小值

样例输入

3 3
1 2 3
4 5 6
7 8 9

样例输出

9
1

提示

解决方案

#include <iostream>

void findMinAndMaxValues(int *ptr, int row, int col);

int main() {
    int row, col;
    std::cin >> row >> col;
    int mat[row][col];
    for (int ir = 0; ir < row; ++ir) {
        for (int ic = 0; ic < col; ++ic) {
            std::cin >> mat[ir][ic];
        }
    }
    findMinAndMaxValues(&mat[0][0], row, col);

    return 0;
}

void findMinAndMaxValues(int *ptr, int row, int col) {
    int min = *ptr, max = *ptr;
    for (int ir = 0; ir < row; ++ir) {
        for (int ic = 0; ic < col; ++ic) {
            if (ptr[ir * col + ic] < min) {
                min = ptr[ir * col + ic];
            }
            if (ptr[ir * col + ic] > max) {
                max = ptr[ir * col + ic];
            }
        }
    }
    std::cout << max << std::endl << min << std::endl;
}