高级语言程序设计

⭐⭐⭐课程网站⭐⭐⭐

HLP2022Fall - 2022年秋季学期《高级语言程序设计》 (ouc.ai)

▶ OUC在线课程中心:中国海洋大学 (ouc.edu.cn)
▶ OUC在线评测平台:222.195.147.104

  • 关于课程:高级语言程序设计是面向大一学生开设的一门关于C语言学习和实践的课程。

  • 关于我:我因为转专业,在选课的时候课程冲突了,被迫在大四来补上这门课。

  • 关于网站:很多同学上课时遇到了很多问题,在微信群可以直接提问,老师和同学们都会为大家解答,我也会在微信群给大家解答,但很多问题需要重复答疑很多次,因此我汇总每周的问题,在网站中的该博文进行整理和解答,方便给大家答疑解惑,希望能帮助到大家。

$$
G_{total} = G_{OutClass} × 0.2 + G_{InClass} × 0.3 + G_{FinalExam} × 0.5 + G_{award} − G_{penalty} − G_{absence}
$$

对网站中我写的内容有疑惑或者建议的话,可以加我微信跟我说一下!

建议使用桌面端设备(宽屏设备)访问该网站,直接在浏览器中输入cosmicdusty.cc即可访问!宽屏设备访问网站时,页面右侧会有导航栏,点击可以直接跳转到对应位置。页面也有明亮暗黑两种模式,可自行切换。

第一周 课堂事务

1 第一周 2022年09月08日周四 课堂事务;课程定位和主要内容;计算机的基本原理。 幻灯片
L1 第一周 2022年09月08日周四 实验课一:熟悉Linux操作系统的基本操作。 实验手册

开发环境

郑老师和乔老师要求用了Linux系统,那我们就先按照老师说的做,在WSL/虚拟机/双系统/云服务器上做!

能不能在Windows上面用Dev和VS,要具体问一下老师。

目前用的系统是Linux的发行版Ubuntu 20.04,用这个的同学可能多一点,其他版本需要问一下老师。

环境的安装,下午和晚上讨论了很多了。可以翻翻微信群。遇到问题自己先尝试运用搜索引擎,看看能不能解答!!

以下都是针对Windows操作系统(大多数同学可能用的是Windows10/11)的,MacOS本身就是基于Unix的,这也是Linux的源头,所以,用MacOS的同学可以直接用终端就行了!

OS

WSL

老师发了WSL的安装教程,建议先跟着做一遍,试试有没有问题!

请同学们尽量在今晚的实验课前配置好Linux系统环境,推荐安装比较流形的发行版本:Ubuntu。推荐使用windows自带的Microsoft store进行安装,具体可参考博客链接:Windows Microsoft Store安装Ubuntu_斯维因yin的博客-CSDN博客_微软商店ubuntu
如果实在安装不上或不习惯使用这种方式,可到本课程的BB平台上下载虚拟机文件和Ubuntu系统安装包,通过创建虚拟机,安装Ubuntu系统。
有能力的同学,可尝试安装双系统,但请注意系统引导菜单的修复,以及不要覆盖原系统所在的分区,该安装方式存在风险,请慎重操作。

Linux的安装我之前也粗略的写过(更多的是物理机上面Linux的安装),可参考:Linux安装记录|Murphy

虚拟机

我平时用虚拟机不多,关于虚拟机的问题我可能解决不了,有问题可以发在群里,大家一起解决。

  • 虚拟机的安装:

群里同学发的:八分钟完成VMware和ubuntu系统安装_哔哩哔哩_bilibili

云服务器

想要探索一下的同学可以尝试一下。有学生优惠,买个云服务器其实很便宜!

阿里云-为了无法计算的价值 (aliyun.com)

腾讯云 - 产业智变 云启未来 (tencent.com)

共建智能世界云底座-华为云 (huaweicloud.com)

百度智能云-智能时代基础设施 (baidu.com)

美团云(MOS) - 云主机_云服务器_云计算_美团网云平台,基础设施服务,稳定提升价值 (mtyun.com)

AWS 云服务-专业的大数据和云计算服务以及云解决方案提供商 (amazon.com)

云计算服务 | Microsoft Azure

双系统

个人不是很推荐,因为很可能两个系统都用不了了!

微信群问题汇总

关于安装路径 WSL的安装通过Microsoft Store,目前是只能安装在C盘。WSL可能必须在C盘,我之前尝试过移动到别的盘,但是会出现很多的问题
关于WSL是什么 我们只安装的Linux发行版Ubuntu的命令行,没有安装桌面,所以这个黑黑的命令行就是所有,一切的操作均在这个命令行中。
密码输入不了问题 密码不是输入不了,是输入了但是不显示,输入完成之后直接enter就行。
设置root密码 sudo passwd root
用户与用户组 root代表的是超级用户,拥有Linux的绝对权限。
平时使用最好可以建一个普通用户,如果涉及到权限的话,使用sudo,或者是使用su -切换到root,更好些吧。
Linux中有个非常重要的概念用户和用户组

这个murphy是一个普通用户,这个root是个超级用户,有绝对的权限。
关于权限
用WSL还算虚拟机 WSL只有命令行,虚拟机下载的是完整的Ubuntu,有桌面,更符合Windows的使用习惯。使用WSL做C的学习和开发完全够用,老师也说了,用哪个都行!
老师要求我们在Linux系统上学习C的编程。大部分的人的电脑都是Windows吧。要想实现Windows上的Linux,要么使用WSL,要么使用虚拟机VM创建一个Ubuntu系统,要么云服务器。
MacOS的同学直接用系统的终端就行了。

进入系统

关于用户

很多同学是以root用户登陆的,可以用,但从长期来说,我感觉不合适(未来学了多了的话,可以使用adduser创建新用户)

参考链接:WSL Ubuntu设置普通用户为默认用户 - 简书 (jianshu.com)

另外一部分同学是两个用户,一个xxx(你自己注册是用的名字)和一个root。

以我自己的为例,我的普通用户是ubuntu,管理员用户是root

ubuntu切换到root的命令:先输入su或者su -,然后再按enter

su- 与su的区别 - 且如歌 - 博客园 (cnblogs.com)

su 和su -的区别_水墨胭脂的博客-CSDN博客

总结:有没有切换PATH环境变量

root切换到ubuntuCtrl+D或者su unbuntu

最好的情况是应该这样的(**忽略掉(base)**)

细说!

pt112:这是一个conda虚拟环境表示,大家应该都没有conda环境,所以没有这个,忽略就行!

ubuntu:这是我都普通用户的用户名,大家的可能不一样

@VM-S-15-ubuntu:是我的Linux主机的名称,大家的可能不一样

~:代表的是/home/ubuntu路径,每个人的都不同,但都是/home/“普通用户名”

$:表示的是普通用户,#表示的root用户

Linux有非常重要的文件结构,未来学的多了,就熟悉了,目前先熟悉两个!

/:根目录

/home:根目录下的home目录(说明:有两个用户的同学,会发现这个目录先有个文件夹的名字和注册的名字一样)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ubuntu@VM-8-15-ubuntu:~$ pwd 
/home/ubuntu

ubuntu@VM-8-15-ubuntu:~$ ll
total 84
drwx------ 11 ubuntu ubuntu 4096 Sep 8 22:33 ./
drwxr-xr-x 4 root root 4096 Sep 8 19:41 ../
-rw-r--r-- 1 ubuntu ubuntu 3032 Sep 8 22:49 .bash_history
……省略很多列出的文件……
drwxrwxr-x 3 ubuntu ubuntu 4096 Sep 8 18:48 userDoc/

ubuntu@VM-8-15-ubuntu:~$ cd ..

ubuntu@VM-8-15-ubuntu:/home$ ll
total 16
drwxr-xr-x 4 root root 4096 Sep 8 19:41 ./
drwxr-xr-x 20 root root 4096 Sep 8 22:49 ../
drwxr-xr-x 4 lighthouse lighthouse 4096 Sep 7 12:03 lighthouse/
drwx------ 11 ubuntu ubuntu 4096 Sep 8 22:33 ubuntu/

# pwd的意思就是显示当前所在的目录
# ll的意思就是列出当前目录下所有文件及其文件的各种信息!
# cd ..就是回退到上一个目录

# 我这里是两个:ubuntu和lighthouse 两个普通用户

关于命令

Linux的命令实在是太多了,记住是不可能的,用到什么要学会使用搜索引擎,自己尝试解决问题

老师推荐了一个很好的查询命令的网站:Linux命令搜索引擎 命令,Linux Linux命令搜索引擎 命令详解:最专业的Linux命令大全,内容包含Linux命令手册、详解、学习,值得收藏的Linux命令速查手册。 - Linux 命令搜索引擎 (wangchujiang.com)

另外,菜鸟教程也很不错:Linux 命令大全 | 菜鸟教程 (runoob.com)

推荐三本书:

Linux命令行大全 (豆瓣) (douban.com)

Linux命令行与shell脚本编程大全(第3版) (豆瓣) (douban.com)

鸟哥的Linux私房菜 基础学习篇 第四版 (豆瓣) (douban.com)

《鸟哥的Linux私房菜》也很不错,但这本书是以Linux的另一个发行版CentOS举例的。CentOS用在企业级的服务器可能多一点

第一周课堂(上机课)练习

练习1 文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ubuntu@VM-8-15-ubuntu:~$ cd /home
ubuntu@VM-8-15-ubuntu:/home$ sudo mkdir abc
ubuntu@VM-8-15-ubuntu:/home$ ll
total 20
drwxr-xr-x 5 root root 4096 Sep 8 19:39 ./
drwxr-xr-x 20 root root 4096 Sep 8 19:39 ../
drwxr-xr-x 2 root root 4096 Sep 8 19:39 abc/
drwxr-xr-x 4 lighthouse lighthouse 4096 Sep 7 12:03 lighthouse/
drwx------ 11 ubuntu ubuntu 4096 Sep 8 14:43 ubuntu/


# cd /home 进入家目录
# 看仔细一点:cd空格/home
# sudo mkdir abc 创建 abc
# mkdir abc 没有sudo也可以,mkdir命令不需要root模式
# ll:这里是两个小写的L(l:小写的L)
# ll就是ls -all的缩写
# ls 与 ll 是两个非常简单但又非常重要的命令,多用几次!!!
1
2
3
4
5
6
7
8
9
10
11
12
ubuntu@VM-8-15-ubuntu:/home$ sudo mv abc study
ubuntu@VM-8-15-ubuntu:/home$ ll
total 20
drwxr-xr-x 5 root root 4096 Sep 8 19:39 ./
drwxr-xr-x 20 root root 4096 Sep 8 19:39 ../
drwxr-xr-x 4 lighthouse lighthouse 4096 Sep 7 12:03 lighthouse/
drwxr-xr-x 2 root root 4096 Sep 8 19:39 study/
drwx------ 11 ubuntu ubuntu 4096 Sep 8 14:43 ubuntu/

# sudo mv abc study 重命名
# Linux mv(英文全拼:move file)命令用来为文件或目录改名、或将文件或目录移入其它位置。
# mv的其他用法可以去自己查一查
1
2
3
4
5
6
7
8
9
10
ubuntu@VM-8-15-ubuntu:/home$ sudo cp /etc/apt/sources.list study
ubuntu@VM-8-15-ubuntu:/home$ cd study
ubuntu@VM-8-15-ubuntu:/home/study$ ll
total 12
drwxr-xr-x 2 root root 4096 Sep 8 19:40 ./
drwxr-xr-x 5 root root 4096 Sep 8 19:39 ../
-rw-r--r-- 1 root root 936 Sep 8 19:40 sources.list

# sudo cp /etc/apt/sources.list study 复制文件:把第一个参数指代的文件复制到第二个参数指代的文件夹下
# cd study 进入 study 文件夹
1
2
3
4
ubuntu@VM-8-15-ubuntu:/home/study$ less sources.list

# 使用less 命令读文件,也可以用cat
# 之后按‘q’就能推出阅读模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ubuntu@VM-8-15-ubuntu:/home/study$ sudo rm sources.list
ubuntu@VM-8-15-ubuntu:/home/study$ ll
total 8
drwxr-xr-x 2 root root 4096 Sep 8 19:41 ./
drwxr-xr-x 5 root root 4096 Sep 8 19:39 ../

ubuntu@VM-8-15-ubuntu:/home/study$ cd ..
ubuntu@VM-8-15-ubuntu:/home$ ls
lighthouse study ubuntu
ubuntu@VM-8-15-ubuntu:/home$ ll
total 20
drwxr-xr-x 5 root root 4096 Sep 8 19:39 ./
drwxr-xr-x 20 root root 4096 Sep 8 19:41 ../
drwxr-xr-x 4 lighthouse lighthouse 4096 Sep 7 12:03 lighthouse/
drwxr-xr-x 2 root root 4096 Sep 8 19:41 study/
drwx------ 11 ubuntu ubuntu 4096 Sep 8 14:43 ubuntu/

ubuntu@VM-8-15-ubuntu:/home$ sudo rmdir study
ubuntu@VM-8-15-ubuntu:/home$ ll
total 16
drwxr-xr-x 4 root root 4096 Sep 8 19:41 ./
drwxr-xr-x 20 root root 4096 Sep 8 19:41 ../
drwxr-xr-x 4 lighthouse lighthouse 4096 Sep 7 12:03 lighthouse/
drwx------ 11 ubuntu ubuntu 4096 Sep 8 14:43 ubuntu/
ubuntu@VM-8-15-ubuntu:/home$

# sudo rm sources.list 删除文件
# cd .. 退回到上一个目录
# sudo rmdir study 删除‘空的’文件夹
# 非空文件夹的删除用 sudo rm -rf study

练习2 程序编写与编译

1. vim

Linux中自带了vim,不需要下载!

Vim基本语法与使用方法:Vim|Murphy

  1. GCC安装

教程链接:如何在 Ubuntu 20.04 上安装 GCC(build-essential)-阿里云开发者社区 (aliyun.com)

默认的 Ubuntu 软件源包含了一个软件包组,名称为 “build-essential”,它包含了 GNU 编辑器集合,GNU 调试器,和其他编译软件所必需的开发库和工具。
想要安装开发工具软件包,以 拥有 sudo 权限用户身份或者 root 身份运行下面的命令:

1
2
3
4
sudo apt update
sudo apt install build-essential

# 这个命令将会安装一系列软件包,包括gcc,g++,和make。

通过运行下面的命令,打印 GCC 版本,来验证 GCC 编译器是否被成功地安装。(在 Ubuntu 20.04 软件源中 GCC 的默认可用版本号为9.3.0:)

1
2
3
4
5
6
7
8
gcc --version

# 输出这个就是正常的
ubuntu@VM-8-15-ubuntu:~$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  1. 文件的编写与运行
1
2
3
4
5
6
7
8
9
10
11
12
ubuntu@VM-8-15-ubuntu:~/userDoc/HLP$ vim hello.c
ubuntu@VM-8-15-ubuntu:~/userDoc/HLP$ gcc hello.c -o hello
ubuntu@VM-8-15-ubuntu:~/userDoc/HLP$ ll
total 56
drwxrwxr-x 2 ubuntu ubuntu 4096 Sep 8 19:53 ./
drwxrwxr-x 3 ubuntu ubuntu 4096 Sep 8 18:48 ../
-rwxrwxr-x 1 ubuntu ubuntu 16696 Sep 8 19:53 hello*
-rw-rw-r-- 1 ubuntu ubuntu 80 Sep 8 19:52 hello.c

ubuntu@VM-8-15-ubuntu:~/userDoc/HLP$ ./hello
hello world
ubuntu@VM-8-15-ubuntu:~/userDoc/HLP$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 建议大家自己建一个自己的文件夹在文件夹下写自己的程序
# 例如:
# mkdir HLP
# cd HLP
# vim xxx.c



# vim hello.c 建立文件 名字是hello.c (关于sudo:如果你所在的文件夹下面所使用的用户没有w权限,那就要用sudo,要不然无法保存)
# 在vim中,按i进入插入模式(左下角显示INSERT),输入代码,按esc退出插入模式(左下角的INSERT消失);
# 输入:wq (:wq的意思就是保存并退出),注意是“:wq”,别落掉“冒号”
# 如果无法保存,建议输入:q!(表示强制退出)
# :q (表示退出)

# gcc hello.c -o hello 用gcc编译c文件 生成hello
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ubuntu@VM-8-15-ubuntu:~/userDoc/HLP$ ./hello
hello world

# 运行hello文件,就成功了
# 执行文件,参考链接: https://blog.csdn.net/xinfeismile123/article/details/118908363
# 执行文件,参考链接: https://blog.csdn.net/qq_36255988/article/details/100172528

# Windows中的可执行文件的后缀是.exe,但是,在Linux中,没有.exe代表可执行文件的说法。

# -rwxrwxr-x 1 ubuntu ubuntu 16696 Sep 8 19:53 hello*
# -:表示文件
# 三组rwx表示不同权限的用户可进行的操作
# r:read 读
# w:write 写
# x:执行
# 关于权限,参考文章: http://c.biancheng.net/view/757.html
1
2
3
4
5
6
7
8
9
# include <stdio.h>

int main()
{
printf("hello world\n");
return 0;
}

// 注意:都是英文的标点符号

第二周 计算机简介

2 第二周 2022年09月15日周四 计算机的历史与未来;程序运行的基本原理;操作系统与编程语言。 幻灯片
L2 第二周 2022年09月15日周四 实验课二:熟悉Linux操作系统下的编程环境。 实验手册

Linux简介

Linux,全称GNU/Linux,是一种免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·本纳第克特·托瓦兹于1991年10月5日首次发布,它主要受到Minix和Unix思想的启发,是一个基于POSIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。Linux有上百种不同的发行版,如基于社区开发的debianarchlinux,和基于商业开发的Red Hat Enterprise Linux、SUSE等。

Linux_百度百科 (baidu.com)

Shell

1
2
ubuntu@VM-8-15-ubuntu:~$ echo $SHELL
/bin/bash

Linux 目录结构

Linux 系统目录结构 | 菜鸟教程 (runoob.com)

  • /bin
    bin 是 Binaries (二进制文件) 的缩写, 这个目录存放着最经常使用的命令

  • /boot:
    这里存放的是启动 Linux 时使用的一些核心文件,包括一些连接文件以及镜像文件。

  • /dev :
    dev 是 Device(设备) 的缩写, 该目录下存放的是 Linux 的外部设备,在 Linux 中访问设备的方式和访问文件的方式是相同的。

  • /etc:
    etc 是 Etcetera(等等) 的缩写,这个目录用来存放所有的系统管理所需要的配置文件和子目录。

  • /home
    用户的主目录,在 Linux 中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的,如上图中的 alice、bob 和 eve。

  • /lib
    lib 是 Library(库) 的缩写这个目录里存放着系统最基本的动态连接共享库,其作用类似于 Windows 里的 DLL 文件。几乎所有的应用程序都需要用到这些共享库。

  • /lost+found
    这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件。

  • /media
    linux 系统会自动识别一些设备,例如U盘、光驱等等,当识别后,Linux 会把识别的设备挂载到这个目录下。

  • /mnt
    系统提供该目录是为了让用户临时挂载别的文件系统的,我们可以将光驱挂载在 /mnt/ 上,然后进入该目录就可以查看光驱里的内容了。

  • /opt
    opt 是 optional(可选) 的缩写,这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。

  • /proc
    proc 是 Processes(进程) 的缩写,/proc 是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息。
    这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件,比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器:

    1
    echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
  • /root
    该目录为系统管理员,也称作超级权限者的用户主目录。

  • /sbin
    s 就是 Super User 的意思,是 Superuser Binaries (超级用户的二进制文件) 的缩写,这里存放的是系统管理员使用的系统管理程序

  • /selinux
    这个目录是 Redhat/CentOS 所特有的目录,Selinux 是一个安全机制,类似于 windows 的防火墙,但是这套机制比较复杂,这个目录就是存放selinux相关的文件的。

  • /srv
    该目录存放一些服务启动之后需要提取的数据。

  • /sys

    这是 Linux2.6 内核的一个很大的变化。该目录下安装了 2.6 内核中新出现的一个文件系统 sysfs 。

    sysfs 文件系统集成了下面3种文件系统的信息:针对进程信息的 proc 文件系统、针对设备的 devfs 文件系统以及针对伪终端的 devpts 文件系统。

    该文件系统是内核设备树的一个直观反映。

    当一个内核对象被创建的时候,对应的文件和目录也在内核对象子系统中被创建。

  • /tmp
    tmp 是 temporary(临时) 的缩写这个目录是用来存放一些临时文件的。

  • /usr
    usr 是 unix shared resources(共享资源) 的缩写,这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于 windows 下的 program files 目录。

  • /usr/bin:
    系统用户使用的应用程序

  • /usr/sbin:
    超级用户使用的比较高级的管理程序和系统守护程序。

  • /usr/src:
    内核源代码默认的放置目录。

  • /var
    var 是 variable(变量) 的缩写,这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。

  • /run
    是一个临时文件系统,存储系统启动以来的信息。当系统重启时,这个目录下的文件应该被删掉或清除。如果你的系统上有 /var/run 目录,应该让它指向 run。

在 Linux 系统中,有几个目录是比较重要的,平时需要注意不要误删除或者随意更改内部文件。

/etc: 上边也提到了,这个是系统中的配置文件,如果你更改了该目录下的某个文件可能会导致系统不能启动。

/bin, /sbin, /usr/bin, /usr/sbin: 这是系统预设的执行文件的放置目录,比如 ls 就是在 /bin/ls 目录下的。

值得提出的是 /bin、**/usr/bin** 是给系统用户使用的指令(除 root 外的通用用户),而/sbin, /usr/sbin 则是给 root 使用的指令。

/var: 这是一个非常重要的目录,系统上跑了很多程序,那么每个程序都会有相应的日志产生,而这些日志就被记录到这个目录下,具体在 /var/log 目录下,另外 mail 的预设放置也是在这里。

helloworld问题再记录

系统问题:第一周➡开发环境
开发环境问题:第一周➡第一周课堂(上机课)练习➡练习2 程序编写与编译➡vim&GCC安装

⭐建议用桌面端浏览器进行观看,页面右侧有导航栏可以直接跳转

再记录的内容重点是权限问题,这又涉及到第一周➡进入系统➡关于用户的内容了。

只有一个root登陆WSL的同学,可以采用下面这个链接的方式(我测试过了,可用),但是有点麻烦;第二种方法就是卸载了WSL重装!

参考链接:WSL Ubuntu设置普通用户为默认用户 - 简书 (jianshu.com)

使用普通用户登陆Linux的同学,建议把课堂下的文件放在~下,在这个文件夹下建立文件是不需要权限的(至于是为什么,读一下上面这张图片,或许可以明白!)

1
2
3
4
5
(base) ubuntu@VM-8-15-ubuntu:~/userDoc/HLP$ cd ~
(base) ubuntu@VM-8-15-ubuntu:~$ pwd
/home/ubuntu

# ~:就是你登陆的用户所在的目录

举了例子!

apt 换源

ubuntu | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror

ubuntu镜像_ubuntu下载地址_ubuntu安装教程-阿里巴巴开源镜像站 (aliyun.com)

Ubuntu 20.04 && Ubuntu 18.04 修改 apt 源_星汉空兮的博客-CSDN博客

之前安装系统的时候,大部分同学安装的都是Ubuntu 20.04,但也有同学安装了22.04,换apt的源的时候,需要自己注意一下!

1
2
3
4
5
6
7
8
在WSL上,更换源时,一定要搞清楚目前安装的的Ubuntu是什么版本的!

部分ubuntu系统LTS版本代号
Ubuntu 16.04代号为:xenial
Ubuntu 17.04代号为:zesty
Ubuntu 18.04代号为:bionic
Ubuntu 20.04代号为:focal
Ubuntu 22.04代号为:jammy
1
2
3
4
5
6
(base) ubuntu@VM-8-15-ubuntu:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.5 LTS
Release: 20.04
Codename: focal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# https://developer.aliyun.com/mirror/ubuntu?spm=a2c6h.13651102.0.0.3e221b11mptltH
#阿里源 ubuntu 20.04

deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/
# 清华源 ubuntu20.04

# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse
# 预发布软件源,不建议启用
# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-proposed main restricted universe multiverse

sudo apt-get update 与upgrade的用法_Darren_pty的博客-CSDN博客_apt-get update

第三周 程序简介

3 第三周 2022年09月22日周四 感性认识计算机程序;快步走进C程序。 幻灯片
L3 第三周 2022年09月22日周四 实验课三:C程序的编译、运行和调试。 实验手册

C程序编译过程

gdb介绍

gdb下载:Ubuntu1804下安装gdb与使用 - michaelchengjl - 博客园 (cnblogs.com)

1
2
3
4
5
6
# 下载
sudo apt-get update
sudo apt-get install gdb

# 检查
gdb --version

学习资料:

GDB 入门教程_恋喵大鲤鱼的博客-CSDN博客_gdb教程

gdb的基本用法_一只向前的程序猿的博客-CSDN博客_gdb用法

GDB十分钟教程_Liigo的博客-CSDN博客_gdb教程

Docs (feishu.cn)

Linux 使用gdb调试入门。_哔哩哔哩_bilibili

程序中的错误主要分为 2 类,分别为语法错误和逻辑错误:

  • 程序中的语法错误几乎都可以由编译器诊断出来,很容易就能发现并解决;
  • 逻辑错误指的是代码思路或者设计上的缺陷,程序出现逻辑错误的症状是:代码能够编译通过,没有语法错误,但是运行结果不对。对于这类错误,只能靠我们自己去发现和纠正。

也就是说,程序中出现的语法错误可以借助编译器解决;但逻辑错误则只能靠自己解决。实际场景中解决逻辑错误最高效的方法,就是借助调试工具对程序进行调试。

调试器名称 特 点
Remote Debugger Remote Debugger 是 VC/VS 自带的调试器,与整个IDE无缝衔接,使用非常方便。
WinDbg 大名鼎鼎的 Windows 下的调试器,它的功能甚至超越了 Remote Debugger,它还有一个命令行版本(cdb.exe),但是这个命令行版本的调试器指令比较复杂,不建议初学者使用。
LLDB XCode 自带的调试器,Mac OS X 下开发必备调试器。
GDB Linux 下使用最多的一款调试器,也有 Windows 的移植版。

正如从事 Windows C/C++ 开发的一定要熟悉 Visual Studio、从事 Java 开发的要熟悉 Eclipse 或 IntelliJ IDEA、从事 Android 开发的要熟悉 Android Studio、从事 iOS 开发的要熟悉 XCode 一样,从事 Linux C/C++ 开发要熟悉 GDB。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
GDB通常和gcc命令一起使用,编译选项加入-g才可使可执行文件处于debug模式。

- gdb命令格式:
gdb [-help] [-nx] [-q] [-batch] [-cd=dir] [-f] [-b bps] [-tty=dev]
[-s symfile] [-e prog] [-se prog] [-c core] [-x cmds] [-d dir]
[prog[core|procID]]

- 常用的操作:
gdb [可执行文件]:选中可执行程序,进入gdb的debug模式;
(gdb) b(breakpoint):b 函数名 :对此函数进行中断 ;b 文件名:行号;
(gdb) r (run):启动程序,运行至程序的断点或者结束;
(gdb) l (list):l funcname,查看函数源码。或 l linenum,显示指定行周围的源码。或 l -,显示当前行前面的源码。或l,显示当前行后面的源码;
(gdb) s(step):进入函数,逐语句运行;
(gdb) n(next):不进入函数,逐过程运行;
(gdb) c(continue):继续运行,跳至下一个断点;
(gdb) p(print):打印显示变量值;
(gdb) set variable=value,为变量赋值;
(gdb) kill:终止调试的程序;
(gdb) h(help):列出gdb详细命令帮助列表;
(gdb) clear filename.c:30:清除30行处的断点;
(gdb) info break:显示断点信息;
(gdb) delete 断点编号:断点编号是info break 后显示出来的;
(gdb) bt(backtrace):回溯到段出错的位置;
(gdb) frame 帧号:帧号是bt命令产生的堆栈针;
(gdb) q(quit):退出;

GDB基本用法

GDB基本用法 - Euphie - 博客园 (cnblogs.com)

  1. 基本命令
  • 进入GDB:#gdb test

  test是要调试的程序,由gcc test.c -g -o test生成。进入后提示符变为(gdb) 。

  • 查看源码:(gdb) l

  源码会进行行号提示。

  如果需要查看在其他文件中定义的函数,在l后加上函数名即可定位到这个函数的定义及查看附近的其他源码。或者:使用断点或单步运行,到某个函数处使用s进入这个函数。

  • 设置断点:(gdb) b 6

  这样会在运行到源码第6行时停止,可以查看变量的值、堆栈情况等;这个行号是gdb的行号。

  • 查看断点处情况:(gdb) info b

  可以键入”info b”来查看断点处情况,可以设置多个断点;

  • 运行代码:(gdb) r
  • 显示变量值:(gdb) p n

  在程序暂停时,键入”p 变量名”(print)即可;

  GDB在显示变量值时都会在对应值之前加上 N N”,而无需写冗长的变量名;

  • 观察变量:(gdb) watch n

在某一循环处,往往希望能够观察一个变量的变化情况,这时就可以键入命令”watch”来观察变量的变化情况,GDB在”n”设置了观察点;

  • 单步运行:(gdb) n
  • 程序继续运行:(gdb) c

  使程序继续往下运行,直到再次遇到断点或程序结束;

  • 退出GDB:(gdb) q
  1. 断点调试
命令格式 例子 作用
break + 设置断点的行号 break n 在n行处设置断点
tbreak + 行号或函数名 tbreak n/func 设置临时断点,到达后被自动删除
break + filename + 行号 break main.c:10 用于在指定文件对应行设置断点
break + <0x…> break 0x3400a 用于在内存某一位置处暂停
break + 行号 + if + 条件 break 10 if i==3 用于设置条件断点,在循环中使用非常方便
info breakpoints/watchpoints [n] info break n表示断点编号,查看断点/观察点的情况
clear + 要清除的断点行号 clear 10 用于清除对应行的断点,要给出断点的行号,清除时GDB会给出提示
delete + 要清除的断点编号 delete 3 用于清除断点和自动显示的表达式的命令,要给出断点的编号,清除时GDB不会给出任何提示
disable/enable + 断点编号 disable 3 让所设断点暂时失效/使能,如果要让多个编号处的断点失效/使能,可将编号之间用空格隔开
awatch/watch + 变量 awatch/watch i 设置一个观察点,当变量被读出或写入时程序被暂停
rwatch + 变量 rwatch i 设置一个观察点,当变量被读出时,程序被暂停
catch 设置捕捉点来补捉程序运行时的一些事件。如:载入共享库(动态链接库)或是C++的异常
tcatch 只设置一次捕捉点,当程序停住以后,应点被自动删除
  1. 数据命令
命令格式 例子 作用
display +表达式 display a 用于显示表达式的值,每当程序运行到断点处都会显示表达式的值
info display 用于显示当前所有要显示值的表达式的情况
delete + display 编号 delete 3 用于删除一个要显示值的表达式,被删除的表达式将不被显示
disable/enable + display 编号 disable/enable 3 使一个要显示值的表达式暂时失效/使能
undisplay + display 编号 undisplay 3 用于结束某个表达式值的显示
whatis + 变量 whatis i 显示某个表达式的数据类型
print(p) + 变量/表达式 p n 用于打印变量或表达式的值
set + 变量 = 变量值 set i = 3 改变程序中某个变量的值
  • 在使用print命令时,可以对变量按指定格式进行输出,其命令格式为print /变量名 + 格式,其中常用的变量格式:x:十六进制;d:十进制;u:无符号数;o:八进制;c:字符格式;f:浮点数。
  1. 调试运行环境相关命令
命令格式 例子 作用
set args set args arg1 arg2 设置运行参数
show args show args 参看运行参数
set width + 数目 set width 70 设置GDB的行宽
cd + 工作目录 cd ../ 切换工作目录
run r/run 程序开始执行
step(s) s 进入式(会进入到所调用的子函数中)单步执行,进入函数的前提是,此函数被编译有debug信息
next(n) n 非进入式(不会进入到所调用的子函数中)单步执行
finish finish 一直运行到函数返回并打印函数返回时的堆栈地址和返回值及参数值等信息
until + 行数 u 3 运行到函数某一行
continue(c) c 执行到下一个断点或程序结束
return <返回值> return 5 改变程序流程,直接结束当前函数,并将指定值返回
call + 函数 call func 在当前位置执行所要运行的函数
  1. 堆栈相关命令
命令格式 例子 作用
backtrace/bt bt 用来打印栈帧指针
frame frame 1 用于打印指定栈帧
info reg info reg 查看寄存器使用情况
info stack info stack 查看堆栈使用情况
up/down up/down 跳到上一层/下一层函数
1
backtrace/bt 也可以在该命令后加上要打印的栈帧指针的个数,查看程序执行到此时,是经过哪些函数呼叫的程序,程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个“帧”,最近调用的函数在 0 号帧中(“底部”帧)
  1. 跳转执行
  • jump:指定下一条语句的运行点。可以是文件的行号,可以是file:line格式,可以是+num这种偏移量格式。表式着下一条运行语句从哪里开始。相当于改变了PC寄存器内容,堆栈内容并没有改变,跨函数跳转容易发生错误。
  1. 信号命令
  • signal:signal SIGXXX,产生XXX信号,如SIGINT。一种速查Linux查询信号的方法:# kill -l
  1. 运行Shell命令
  • 如(gdb)shell ls来运行ls
  1. 更多程序运行选项和调试
  • 程序运行参数
1
2
set args 可指定运行时参数。(如:set args 10 20 30 40 50
show args 命令可以查看设置好的运行参数。
  • 运行环境
1
2
3
4
path 可设定程序的运行路径。 
show paths 查看程序的运行路径。
set environment varname [=value] 设置环境变量。如:set env USER=hchen
show environment [varname] 查看环境变量。
  • 工作目录
1
2
cd 相当于shell的cd命令。 
pwd 显示当前的所在目录。
  • 程序的输入输出
1
2
3
info terminal 显示你程序用到的终端的模式。 
使用重定向控制程序输出。如:run > outfile
tty命令可以指写输入输出的终端设备。如:tty /dev/ttyb
  • 调试已运行的程序
1
2
3
两种方法: 
(1)在UNIX下用ps查看正在运行的程序的PID(进程ID),然后用gdb PID格式挂接正在运行的程序。
(2)先用gdb 关联上源代码,并进行gdb,在gdb中用attach命令来挂接进程的PID。并用detach来取消挂接的进程。
  • 暂停 / 恢复程
1
序运行当进程被gdb停住时,你可以使用info program 来查看程序的是否在运行,进程号,被暂停的原因。 在gdb中,我们可以有以下几种暂停方式:断点(BreakPoint)、观察点(WatchPoint)、捕捉点(CatchPoint)、信号(Signals)、线程停止(Thread Stops),如果要恢复程序运行,可以使用c或是continue命令。
  • 线程(Thread Stops)
1
2
3
4
5
6
如果程序是多线程,可以定义断点是否在所有的线程上,或是在某个特定的线程。 
break thread
break thread if ...
linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,可以通过“info threads”命令来查看正在运行程序中的线程信息。如果不指定thread 则表示断点设在所有线程上面。还可以为某线程指定断点条件。如:
(gdb) break frik.c:13 thread 28 if bartab > lim
当你的程序被GDB停住时,所有的运行线程都会被停住。这方便查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。
  1. 调试core文件
  • Core Dump:Core的意思是内存,Dump的意思是扔出来,堆出来。开发和使用Unix程序时,有时程序莫名其妙的down了,却没有任何的提示(有时候会提示core dumped),这时候可以查看一下有没有形如core.进程号的文件生成,这个文件便是操作系统把程序down掉时的内存内容扔出来生成的, 它可以做为调试程序的参考
  • 生成Core文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
一般默认情况下,core file的大小被设置为了0,这样系统就不dump出core file了。修改后才能生成core文件。
#设置core大小为无限
ulimit -c unlimited
#设置文件大小为无限
ulimit unlimited

这些需要有root权限, 在ubuntu下每次重新打开中断都需要重新输入上面的第一条命令, 来设置core大小为无限

core文件生成路径:输入可执行文件运行命令的同一路径下。若系统生成的core文件不带其他任何扩展名称,则全部命名为core。新的core文件生成将覆盖原来的core文件。

1/proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作为扩展。文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.xxxx;为0则表示生成的core文件同一命名为core。
可通过以下命令修改此文件:
echo "1" > /proc/sys/kernel/core_uses_pid

2)proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式。
可通过以下命令修改此文件:
echo "/corefile/core-%e-%p-%t" > core_pattern,可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
以下是参数列表:
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名
  • 用gdb查看core文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行.
gdb [exec file] [core file]
如:
gdb ./test core

或gdb ./a.out
core-file core.xxxx
gdb后, 用bt命令backtrace或where查看程序运行到哪里, 来定位core dump的文件->行.

待调试的可执行文件,在编译的时候需要加-g,core文件才能正常显示出错信息

1)gdb -core=core.xxxx
file ./a.out
bt
2)gdb -c core.xxxx
file ./a.out
bt
  • 用gdb实时观察某进程crash信息
1
2
3
4
5
6
启动进程
gdb -p PID
c
运行进程至crash
gdb会显示crash信息
bt

第四周 计算机程序

4 第四周 2022年09月29日周四 从现实问题到计算机程序。 幻灯片
L4 第四周 2022年09月29日周四 实验课四:感性接触C程序。 实验手册

第五周 C语言简介

5 第五周 2022年10月06日周四 C语言的由来、标准和构成;数据成分。 幻灯片
L5 第五周 2022年10月06日周四 实验课五:C语言的数据成分。 实验手册

C语言规范定义的非常宽泛:
long型数据长度不短于int型;short型不长于int
-> 相同的程序在不同编译器上具有不同解释
-> 相同的程序在不同平台上运行结果不同

冒泡排序算法

1.1 冒泡排序 | 菜鸟教程 (runoob.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
void bubble_sort(int arr[], int len) {
int i, j, temp;

// 注意for循环条件
for (i = 0; i < len - 1; i++)
for (j = 0; j < len - 1 - i; j++)
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
int main() {
int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 };
int len = (int) sizeof(arr) / sizeof(*arr);
bubble_sort(arr, len);
int i;
for (i = 0; i < len; i++)
printf("%d ", arr[i]);
return 0;
}

// 来源:菜鸟教程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# include <stdio.h>

int main()
{
// 冒泡排序
int num;
scanf("%d", &num);
int i, j, tmp;
int array[100];
for(i = 0; i < num ; i++)
{
scanf("%d", &array[i]);
}

// 注意for循环条件
for (i = 0; i < num - 1; i++)
{
for (j = 1; j < num - i; j++)
{
if (array[j - 1] > array[j])
{
tmp = array[j];
array[j] = array[j - 1];
array[j - 1] = tmp;
}
}
}
for (i = 0; i < num; i++)
{
printf("%d\n", array[i]);
}
return 0;
}


// 来源:MurphyHou

第六周 运算控制传输

6 第六周 2021年10月13日周四 运算成分;控制成分;传输成分。 幻灯片
L6 第六周 2022年10月13日周四 实验课六:C语言的数据、运算和控制成分。 实验手册

推荐网课:翁恺C语言_哔哩哔哩_bilibili

控制成分

break:在switch语句、while语句、 do-while语句、for语句中使用; 以跳出switch语句或内层循环,继续执行逻辑上的下一条语句。

continue:用于循环语句中; 结束本次循环,接着立即测试循环控制表达式,判断是否继续执行下一次循环。

传输成分

scanf的工作原理

scanf是从标准输入中读取字符,然后进行相关转化;
如果是空格或者换行,直接跳过;
如果不符合格式化要求,不处理同时也不减少缓冲区中的信息。
将数据按照行缓冲送到键盘缓冲区; 按照格式化要求从缓冲区中读取数据到相应内存空间。
scanf并不能保证后面的参数都被正确赋值。

scanf是格式化输入,printf是格式化输出。
cin是输入流,cout是输出流。
格式化输出效率比较高,但是写代码麻烦。
流输出操作效率稍低,但书写简便。
cout之所以效率低,是先把要输出的东西存入缓冲区再输出。

以下参考:”C Primer Plus”

scanf

buffer

其他参考资料:

scanf函数%c前为什么加空格_hhhhhyyyyy8的博客-CSDN博客

c语言防止缓冲区数据作为有效字符被读入 - cmustard - 博客园 (cnblogs.com)

第七周 数组

7 第七周 2022年10月20日周四 数组。 幻灯片
L7 第七周 2022年10月20日周四 实验课七:C语言的数组。 实验手册

第八周 字符串

8 第八周 2022年10月27日周四 字符串。 幻灯片
L8 第八周 2022年10月27日周四 实验课八:C语言的字符串。 实验手册

C语言中的字符串是个难点!

C++中的输入

  • cin

    • string str; cin >> str; 接受一个字符串,遇“空格”、“TAB”、“回车”都结束
  • cin.get()

    • cin.get(ch),可以用来接收一个字符
    • cin.get(字符数组名,接收字符数目)用来接收一行字符串,可以接收空格
    • cin.get(无参数)没有参数主要是用于舍弃输入流中的不需要的字符,或者舍弃回车,弥补cin.get(字符数组名,接收字符数目)的不足.
  • cin.getline()

    • 接受一个字符串,可以接收空格并输出
  • getchar()

  • getline(cin, string)// 需要string的头文件

    • 第一个参数是输入流对象、第二个参数是保存字符串的字符串对象。getline从输入流中读取对象保存到string中,但是不包括换行符,这样就可以保存一行中的空格、制表符了。

C中的输入

  • scanf(“%d”, &aa);
  • scanf(“%f”, &bb);
  • scanf(“%c”, &ch);
    • 输入一个字符。
  • scanf(“%s”, str); //用scanf把字符串读入字符数组中,不需要&
    • 从第一个非空白字符开始,到下一个空白字符之前的所有的字符都是输入。(简单来讲,就是只能输入一个单词)
  • ch = getchar();// 输入一个字符
    • putchar(ch); // 输出一个字符
  • gets(str);(C11中不能用了)
    • 读取整行输入,直到遇到换行符,然后丢弃换行符,存储其余字符,并在这些字符的末尾添加一个空字符使其成为一个C字符串。
    • C11标准中,直接废除了gets()函数!因为可能有缓冲区溢出的安全隐患!
    • puts(str); 输出字符串。
  • fgets(str, STLEN, stdin);
    • STLEN是读入字符的最大数量。
    • 如果fgets()读到一个换行符,会把他存储到字符串中。这一点与gets()不一样。
    • fputs(str, stdout); 输入
  • gets_s(str, STLEN); (C11 中新增的)

第九周 函数

9 第九周 2022年11月03日周四 函数。 幻灯片
L9 第九周 2022年11月03日周四 实验课九:C语言的函数。 实验手册
  • 函数原型;函数声明
  • 参数传递:值传递
    • 实参与形参具有不同的存储单元,实参与形参变量的数据传递是 “值传递”。
    • 函数调用时,系统给形参分配存储单元,并将实参对应的值传递给形参。
    • 实参与形参的类型必须相同或可以兼容。
  • 局部变量/全局变量
    • 局部变量:在函数内或块内定义,只在这个函数或块内起作用。
    • 全局变量:在所有函数外定义,作用域从定义变量的位置开始到本程序文件结束。
    • 当全局变量与局部变量同名时,局部变量将在自己作用域内有效,它将屏蔽同名的全局变量。
    • 不在非常必要的情况下,不要使用全局变量。
  • 数组与函数
    • 数组元素作为函数参数
    • 数组名(地址)作为函数参数:数组名就是数字在内存中的地址
      • 形式参数是一个未定义大小的数组:void myFunction(int param[])
      • 数组名作为函数参数
      • 形式参数是一个已定义大小的数组:void myFunction(int param[10])
      • 形式参数是一个指针:void myFunction(int *param)

第十周 递归

10 第十周 2022年11月10日周四 递归。 幻灯片
L10 第十周 2022年11月10日周四 实验课十:C语言的递归。 实验手册
  • 递归简单来说就是函数的嵌套。
  • 递归:一个函数在其定义中直接或间接调用自身的一种方法。
  • 递归与递推
    • 不同:递推的关注点放在起始点条件;递归的关注点放在求解目标上、
    • 相同:重在表现第 i 次与第 i + 1次的关系
    • 斐波那契数列;进制转换;汉诺塔;逆波兰表达式;放苹果(把这几个例子动手实现看看)
    • 斐波那契数列

第十一周 指针

11 第十一周 2022年11月17日周四 指针(一)。 幻灯片
L11 第十一周 2022年11月17日周四 实验课十一:C语言的指针(一)。 实验手册

指针!难点!

指针是什么

  • 变量三要素:变量的地址*(变量在内存重点位置)*,变量的值,变量的名字

  • 指针:把某个变量的地址称为“指向该变量的指针”

  • 如何知道一个变量的地址:取地址符‘&’

  • 指针运算符 ‘*’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# include <iostream>
using namespace std;

int main()
{
int var = 0;
cout << &var << endl;
cout << *&var << endl;
return 0;
}

/*
0x7ffe393d5874
0
*/
  • 指针变量:存放地址(指针)的变量
  • 定义一个指针变量
1
2
3
4
5
6
7
int *p;

/*
int: 指针变量的基类型(基类型:指针变量指向的变量的类型)
*: 指针运算符 p的类型
p: 指针变量的名字
*/
1
2
3
4
5
6
7
8
9
// 定义指向int型变量var的指针p的两种方式

// 方式一(推荐)
int var;
int *p;
p = &var;
// 方式二(两步并作一步)
int var;
int *p = &var
  • 指针变量也是变量,是变量就有地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# include <iostream>
using namespace std;

int main()
{
int var = 10;
int *p;
p = &var; // 定义了一个指向var的指针p

cout << var << endl; // 变量var的内容
cout << &var << endl; //内存中变量var的地址

cout << p << endl; // 指针变量p的内容
cout << &p << endl; // 内存中指针变量p的地址

cout << *p << endl; // 指针变量所指向的变量的内容
}
/*
10
0x7ffd85a1584c
0x7ffd85a1584c
0x7ffd85a15850
10
*/

第十二周 指针

12 第十二周 2022年11月24日周四 指针(二)。 幻灯片
L12 第十二周 2022年11月24日周四 实验课十二:C语言的指针(二)。 实验手册

数组与指针

数组名代表数组首元素的地址;数组名是指向数组第一个元素的指针;注意:a是地址常量,不是变量,不能给其赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# include <iostream>
using namespace std;

int main()
{
int a[5] = {10, 11, 12, 13, 14};

int *p=NULL;
cout<<a<<endl;
p=a;
cout<<p<<endl;

cout<<*p<<endl; //输出10,没什么问题
cout<<*p++<<endl; //输出也是10,后置++的优先级最高为什么还是10是因为p++的属性,

// ++i will increment the value of i, and then return the incremented value.
// i++ will increment the value of i, but return the original value that i held before being incremented.

cout<<*p++<<endl;

cout<<*p<<endl;

return 0;
}

/*
0x7ffc3ce50a80
0x7ffc3ce50a80
10
10
11
12
*/
  • 利用指针变量引用数组元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# include <iostream>
using namespace std;

int main()
{
int num;
cin >> num;
int array[num];

for (int i = 0; i < num; i++)
{
cin >> array[i];
}

cout << "array:" <<endl;
for(int i = 0; i < num; i++)
{
cout << "array elements:" << array[i] << endl;
}
int *p;
p = array;
cout << "pointer:" <<endl;
for (int i = 0; i < num; i++)
{
cout << "array elements:" << *(p + i) << endl;
}

return 0;
}

字符串与指针

1
2
3
char a[10];
char *p;
p = a;

第十三周 指针

13 第十三周 2022年12月01日周四 指针(三)。 幻灯片
L13 第十三周 2022年12月01日周四 实验课十三:C语言的指针(三)。 实验手册

二维数组与指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# include <iostream>
using namespace std;
int main()
{
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

cout<<"a="<<a<<endl;
cout<<"&a[0]="<<&a[0]<<endl<<endl;

cout<<"a+1="<<a+1<<endl;
cout<<"&a[0]+1="<<&a[0]+1<<endl<<endl;

cout<<"*a="<<*a<<endl;
cout<<"a[0]="<<a[0]<<endl;
cout<<"&a[0][0]="<<&a[0][0]<<endl<<endl;

cout<<"*a+1="<<*a+1<<endl;
cout<<"a[0]+1="<<a[0]+1<<endl;
cout<<"&a[0][0]+1="<<&a[0][0]+1<<endl<<endl;
return 0;
}

/*
a=0x7ffc31245390
&a[0]=0x7ffc31245390

a+1=0x7ffc312453a0
&a[0]+1=0x7ffc312453a0

*a=0x7ffc31245390
a[0]=0x7ffc31245390
&a[0][0]=0x7ffc31245390

*a+1=0x7ffc31245394
a[0]+1=0x7ffc31245394
&a[0][0]+1=0x7ffc31245394

*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# include <iostream>
using namespace std;
int main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

int (*p)[4]; // 定义一个指向“包含4个int型元素的一维数组”的指针变量
int i,j;
p=a;
// cin>>i>>j;
i = 1;
j = 2;
cout<<*(*(p+i)+j);
return 0;
}

函数与指针

  • 指针变量做函数参数
  • 数组名做函数参数
  • 多维数组名做函数参数
    • 一般而言,声明一个指向N维数组的指针时,只能省略最左边方括号中值。
  • 指向符号常量的指针
  • 返回指针值的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# include <iostream>
using namespace std;

int *get(int arr[][4], int n, int m);

int main()
{
int a[4][4]={1,2,3,4,5,6,7,8, 9,10,11,12,13,14,15,16};
int *p;
p=get(a,2,3); cout<<*p<<endl;
return 0;
}

int *get(int arr[][4], int n, int m)
{
int *pt;
pt=*(arr+n-1)+m-1;
return(pt);
}
/*
对指向二维数组的指针,取两次*才能得到数值;
这个程序,在函数部分取*,所以返回的pt仍然是个指针,在main函数中,再取*,得到的是一个数值。

关键是:pt=*(arr+1)+2;
arr是指向二维数组第一个元素(一维数组)的指针(可以理解成二维数组的第一行),那么+1之后就是指向了第二行。
取*后就是指向二维数组第二行第一个元素的指针,再+2后,就是指向二维数组第二行第三个元素,也就是&a[1][2]
所以输出的答案就是7
*/
  • 静态局部变量
    • 函数中的局部变量的值在函数调用结束后不消失而保留原值;
    • 即其占用的存储单元不释放,在下一次该函数调用时,仍可以继续使用该变量。
    • 用关键词static进行声明

第十四周 结构体和链表

14 第十四周 2022年12月08日周四 结构体与链表。 幻灯片
L14 第十四周 2022年12月08日周四 实验课十四:C语言的结构体与链表。 实验手册

结构体

  • 结构体的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 先定义结构体,再用已经声明的结构体来定义变量
struct Stu
{
int id;
char name[100];
}; // 别忘了分号

Stu sam, tom;

// 也可以
/*
Stu sam = {
123,
{'s','a','m', '\0'}
};
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 定义结构体的同时定义了变量
struct Stu
{
int id;
char name[100];
}sam, tom;

// 也可以
/*
struct Stud
{
int id;
char name[100];
}sam = {
123,
{'s','a','m', '\0'}
};
*/
  • 应用举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# include <iostream>
using namespace std;

struct Student
{
int id_num;
char name[20];
}; // 这里有个分号别忘了

int main()
{
Student mike1 = {123,{'m','i','k','e','\0'}};
Student mike2;

mike2 = mike1; //copy

mike2.id_num = 456;
for(int i=0; mike2.name[i] != '\0'; i++)
{
mike2.name[i] = toupper(mike2.name[i]);
}

cout<<mike1.id_num<<" "<<mike1.name<<endl;
cout<<mike2.id_num<<" "<<mike2.name<<endl;
return 0;
}

// 123 mike
// 456 MIKE
  • 结构体做函数参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# include <iostream>
using namespace std;

struct Student
{
int id_num;
char name[100];
};

void renew(Student one)
{
one.id_num = 456;
for(int i=0; one.name[i] != '\0'; i++)
{
one.name[i] = toupper(one.name[i]);
}
cout<<one.id_num<<" "<<one.name<<endl;
}

int main()
{
Student mike1 = {
123,
{'m','i','k','e','\0'}
};

renew(mike1); // 相当于复制了一个新的结构体变量,函数中对这个新结构体变量的改变,不影响原来的结构体变量。

cout<<mike1.id_num<<" "<<mike1.name<<endl;
return 0;
}

// 456 MIKE
// 123 mike
  • 结构体做函数返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# include <iostream>
using namespace std;

struct Student
{
int id_num;
char name[100];
};

Student newone()
{
Student one = {
456,
{'M','I','K','E','\0'}
};

return one;
}

int main()
{
Student mike = newone();

cout<<mike.id_num<<" "<<mike.name<<endl;
return 0;
}

// 456 MIKE
  • 指向结构体的指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# include <iostream>
using namespace std;

struct Student
{
int id_num;
char name[100];
};

int main()
{
Student mike = {123,{'m','i','k','e','\0'}};
Student *one = &mike;
cout << (*one).id_num <<" "<<(*one).name << endl;
return 0;
}
// 123 mike
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# include <iostream>
using namespace std;

struct Student
{
int id_num;
char name[100];
};

int main()
{
Student mike = {123,{'m','i','k','e','\0'}};
Student *one = &mike;
cout << one->id_num << " " << one->name << endl;
return 0;
}

// 123 mike

C 语言中,「.」与「->」有什么区别? - 知乎 (zhihu.com)

【转】C语言中 -> 是什么意思?_c语言中->表示什么_littesss的博客-CSDN博客

->是一个整体,它是用于指向结构体、C++中的class等含有子数据的指针用来取子数据。

换种说法,如果我们在C语言中定义了一个结构体,然后申明一个指针指向这个结构体,那么我们要用指针取出结构体中的数据,就要用到->.

  • 结构体数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# include <iostream>
using namespace std;

struct Student
{
int id_num;
char name[100];
};

int main()
{
Student myclass[3]= {
123,{'m','i','k','e','\0'},
133,{'t','o','m','\0'},
143,{'j','a','c','k','\0'}
};

Student *one = myclass;

cout<<one->id_num<<" "<<one->name<<endl;
one++;
cout<<one->id_num<<" "<<one->name<<endl;
return 0;
}

// 123 mike
// 133 to

链表

用指针把结构体链起来

  • 链表头:指向第一个链表结点的指针。

  • 链表结点:链表中的每一个元素,包括:

    • ▶ 当前节点的数据; \
    • ▶ 下一个结点的地址。
  • 链表尾:不再指向其他结点的结点,其地址部分放一个NULL,表示链表到此结束。

第十五周 面向对象

15 第十五周 2022年12月15日周四 面向对象程序设计。 幻灯片
L15 第十五周 2022年12月15日周四 实验课十五:C程序设计(综合)。 实验手册

第十六周 总结复习

16 第十六周 2022年12月22日周四 课程总结。
L16 第十六周 2022年12月22日周四 实验课十六:程序设计实践。

期末复习

重点习题

1 pre9 冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// exp5 assignment1
// 冒泡排序
# include <iostream>
using namespace std;

int main()
{
int n;
cin >> n;

int array[n]; //定义数组之前,一定要先输入n的大小!

for(int i = 0; i < n; i++) // 遍历输入数组元素
{
cin >> array[i];
}

for(int i = 0; i < n - 1; i++) // 注意i的取值范围
{
for (int j = 1; j < n - i; j++) // 注意j的取值范围
{
int tmp;
if (array[j - 1] > array[j]) // 注意是正序还是逆序
{
tmp = array[j];
array[j] = array[j - 1];
array[j - 1] = tmp;
}
}
}

for(int i = 0; i < n; i++) // 遍历数组元素进行输出
{
cout << array[i] << endl;;
}

return 0;
}

2 pre12 整数1 5 10的个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// exp5 assignment1
// 整数的个数
// 给定k(1 < k < 100)个正整数,其中每个数都是大于等于1,小于等于10的数。
// 写程序计算给定的k个正整数中,1,5和10出现的次数。

# include <iostream>
using namespace std;

int main()
{
int k;
cin >> k;

int num1 = 0;
int num5 = 0;
int num10 = 0;

for (int i = 0; i < k; i++)
{
int tmp;
cin >> tmp;
if(tmp == 1){num1++;} // 在输入的这个循环中,直接进行数字的判断就可以。可以仅用该中间变量,不用保存所有的输入数据。
else if(tmp == 5){num5++;}
else if(tmp == 10){num10++;}
}
cout << num1 << endl;
cout << num5 << endl;
cout << num10 << endl;

return 0;
}

3 pre19 细菌实验分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// exp6 assignment2
// 细菌实验分组
/*
有一种细菌分为A、B两个亚种,它们的外在特征几乎完全相同,仅仅在繁殖能力上有显著差别,A亚种繁殖能力非常强,B亚种的繁殖能力很弱。在一次为时一个 小时的细菌繁殖实验中,实验员由于疏忽把细菌培养皿搞乱了,请你编写一个程序,根据实验结果,把两个亚种的培养皿重新分成两组。
*/

# include <iostream>
using namespace std;

void bubble(int *arraybianhao, float *arrayfanzhilv, int n); // 注意一维数组作为函数参数时,声明时用指针的方式写!

int main()
{
int n;
cin >> n;

int bianhao[100];
int shiyanqian[100];
int shiyanhou[100];

float fanzhilv[100];

for (int i = 0; i < n; i++)
{
cin >> bianhao[i] >> shiyanqian[i] >> shiyanhou[i]; // 这里其实不需要保存实验前和实验后的数据,因为该数据仅参与了计算繁殖率。
fanzhilv[i] = (float) shiyanhou[i] / shiyanqian[i];
}

// 繁殖率强的在前,繁殖率弱的在后
bubble(bianhao, fanzhilv, n); // 调用自定义的冒泡排序函数,根据繁殖率大小,对编号和繁殖率进行排序。
//这里注意,一维数组作为函数实参时候的写法,只用写数组名字就行,相当于输入的是一个地址。
float fanzhilvmax = fanzhilv[0]; // A强 B弱
float fanzhilvmin = fanzhilv[n - 1];

int Anum = 0;
int Bnum = 0;

for (int i = 0; i < n; i++)
{
if (fanzhilv[i] - fanzhilvmin < fanzhilvmax - fanzhilv[i]) // 注意怎么衡量繁殖率的高低
{
Bnum++;
}
else
{
Anum++;
}
}

cout << Anum << endl;
for(int i = Anum - 1; i > -1 ; i--) // 这里注意i的取值范围
{
cout << bianhao[i] << endl;
}

cout << Bnum << endl;
for(int i = n - 1; i > Anum -1; i--) // 这里注意i的取值范围
{
cout << bianhao[i] << endl;
}

return 0;
}

void bubble(int *arraybianhao, float *arrayfanzhilv, int n)
{
int bianhaotmp;
int fanzhilvtmp;
for (int i = 0; i < n - 1; i++)
{
for (int j = 1; j < n - i; j++)
{
if (arrayfanzhilv[j] > arrayfanzhilv[j - 1]) // 根据繁殖率对编号和繁殖率同时排序。
{
bianhaotmp = arraybianhao[j];
arraybianhao[j] = arraybianhao[j - 1];
arraybianhao[j - 1] = bianhaotmp;

fanzhilvtmp = arrayfanzhilv[j];
arrayfanzhilv[j] = arrayfanzhilv[j - 1];
arrayfanzhilv[j - 1] = fanzhilvtmp;
}
}
}
}

4 pre24 分离整数的各位数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// exp6 assignment2
// 分离整数的各位数
// 从键盘输入一个任意的9位以内的整数,要求正确地分离出它的每一位数,并分别在屏幕上输出,输出采用每行输出一个数的方式,不带其它符号。

# include <iostream>
using namespace std;

int main()
{
int x;
cin >> x;

int array[10];
int d = 10;
int i = 0;

while (x > 0) // 注意while结束的条件
{
array[i] = x % d;
x /= d;
i++;
}

// x的位数:i

for (int j = i - 1; j > -1; j--)
{
cout << array[j] << endl;
}

return 0;
}

5 pre25 成绩判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// assignment3
// 成绩判断

# include <iostream>
using namespace std;

int main()
{
int score;
cin >> score;

if (95<= score && score <= 100){cout << '1';} // 注意用&&连接前后两个条件
else if (90<= score && score < 95){cout << '2';}
else if (85<= score && score < 90){cout << '3';}
else if (80<= score && score < 85){cout << '4';}
else if (70<= score && score <80){cout << '5';}
else if (60<= score && score < 70){cout << '6';}
else{cout <<'7';}

return 0;
}

6 pre43 找和为K的两个元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// assignment3
// 找和为K的两个元素
// 在一个长度为n(n < 1000)的整数序列中,判断是否存在某两个元素之和为k。

# include <iostream>
using namespace std;

int main()
{
int n;
int k;
cin >> n >> k;

int array[n];
int flag = 0;
for (int i = 0; i < n; i++)
{
cin >> array[i];
}

for (int i = 0; i < n; i++)
{
for (int j = 1; j < n; j++) // 注意看两次循环是否遍历了所有的可能
{
if (array[i] + array[j] == k)
{
flag = 1;
break;
}
}
}

if (flag == 1){cout << "yes" << endl;}
else {cout << "no" << endl;}

return 0;
}

7 pre49 最长单词

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// assignment4
// 最长单词
// 一个以'.'结尾的简单英文句子,单词之间用空格分隔,没有缩写形式和其它特殊形式,求句子中的最长单词。

# include <iostream>
using namespace std;

int main()
{
char array[500];

fgets(array, 500, stdin);

int location = 0; // 最长单词的首字母下标
int place = 0; // 出现新单词,则首字母下标就赋给place
int count = 0; // 每个单词字母数的统计
int maxword = 0; // 最长单词的有多少个字母

int i = 0; // 遍历开始

while (array[i] != '.')
{
if (array[i] == ' ') // 单词之间的空格情况
{
i++;
}

place = i; // 这代表了i是一个新单词的首字母的下标

for (; array[i] != ' ' && array[i] != '.'; i++) // 对i循环,统计这个单词有多少个字母。这里for循环没有起始条件,是正确的。
{
count++;
}

// cout << "ceshi count:" << count << endl;

if (count > maxword) // 和已知的最长单词进行比较
{
maxword = count;
location = place;
}
count = 0; // 计数归零,重新寻开始循环,找其他单词。
}

for(i = location; array[i] != ' ' && array[i] != '.'; i++)
{
cout << array[i];
}

return 0;
}

8 pre51 循环移动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// assignment4
// 循环移动
/*
给定一组整数,要求利用数组把这组数保存起来,再利用指针实现对数组中的数循环移动。假定共有n个整数,则要使前面各数顺序向后移m个位置,并使最后m各数变为最前面的m各数。
注意,不要用先输出后m个数,再输出前n-m个数的方法实现,也不要用两个数组的方式实现。
要求只用一个数组的方式实现,一定要保证在输出结果时,输出的顺序和数组中数的顺序是一致的。
*/

# include <iostream>
using namespace std;

int main()
{
int n, m;
cin >> n >> m;

int array[n];

for (int i = 0; i < n; i++)
{
cin >> array[i];
}

for (int i = 0; i < m; i++) // 这道题理解的方法就是画图,一步一步的慢慢来,就知道指针是怎么变化的了。
{
int *p;
p = array + n - 1;
int tmp = *p;

for (int j = 1; j <n; j++)
{
*p = *(p - 1);
p--;
}
*p = tmp;
}

for(int i = 0; i < n; i++)
{
cout << array[i] << ' ';
}
cout << endl;

return 0;
}

9 pre55 简单计算器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// assignment 5
// 简单计算器

# include <iostream>
using namespace std;

int main()
{
int a, b;
char c;

cin >> a >> b >> c;

switch (c)
{
case '+' : cout << a + b; break;
case '-' : cout << a - b; break;
case '*' : cout << a * b; break;
case '/' :
if (b != 0){cout << a / b; break;} // 除法的情况一定要注意被除数的取值情况
else {cout << "Divided by zero!"; break;}
default: cout << "Invalid operator!"; //switch case语句,在default不需要break。
}
return 0;
}

10 pre56 角谷猜想

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// assignment 7
// 角谷猜想

// 所谓角谷猜想,是指
// 对于任意一个正整数,如果是奇数,则乘 3 加 1,
// 如果是偶数,则除以2,
// 得到的结果再按照上述规则重复处理,最终总能够得到 1。

// 如,假定初始整数为 5,计算过程分别为 16、8、4、2、1。

// 程序要求输入一个整数,将经过处理得到 1 的过程输出来。

# include <iostream>
using namespace std;
int even(int xx);
int odd(int xx);

int main()
{
int x;
cin >> x;

while (x != 1) // 对x进行奇数操作和偶数操作,直到xx成为1才结束操作。
{
if (x % 2 == 0)
{
x = even(x);
}
else
{
x = odd(x);
}
}
cout << "End";

return 0;
}

int even(int xx)
{
int ret = xx / 2;
cout << xx << '/' << '2' << '=' << ret << endl;
return ret;
}
int odd(int xx)
{
int ret = xx * 3 + 1;
cout << xx << '*' << "3+1" << '=' << ret << endl; // 注意字符串和字符所使用的引号
return ret;
}


11 pre58 括号匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// assignment 7
// 在某个字符串(长度不超过 100)中有左括号、右括号和大小写字母;
// 规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。
// 写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。
// 不能匹配的左括号用”$”标注, 不能匹配的右括号用”?” 标注.
/*
输入:
((ABCD(x)
)(rttyy())sss)(

输出:
((ABCD(x)
$$
)(rttyy())sss)(
? ?$
*/

# include <iostream>
# include <stack>
using namespace std;

stack<int> S; // 定义堆栈,不要忘记stack的头文件

int main()
{
char instr[1000] = {'\0'};
char outstr[1000] = {'\0'};

while(scanf("%s", instr) != EOF) // 这里注意,scanf()读取字符数组的时候,instr本身就是地址了;注意“!= EOF”的使用。
{
int i;
for(i = 0; instr[i] != '\0'; i++)
{
if(instr[i] == '(')
{
S.push(i); // 把左括号的需要压入栈
outstr[i] = ' ';
}
else if (instr[i] == ')')
{
if (S.empty() == false) // 栈非空,也就是有左括号。
{
S.pop();// 左括号出栈。
outstr[i] = ' ';
}
else
{
outstr[i] = '?';
}
}
else
{
outstr[i] = ' ';
}
}
outstr[i] = '\0'; // 输出的字符数组的结束标志

int k;
while(!S.empty())
{
k = S.top(); // 取栈顶元素
outstr[k] = '$';
S.pop(); // 出栈
}
fputs(instr, stdout);
cout << endl;
fputs(outstr, stdout);
cout << endl;
}
return 0;
}

12 pre59 判断闰年

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// assignment 5
// 判断闰年

# include <iostream>
using namespace std;

int main()
{
int year;
cin >> year;

if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) // 这个条件要记住
{
cout << 'Y';
}
else
{
cout << 'N';
}

return 0;
}

13 pre60 寻找下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// assignment 6
// 寻找下标
// 已知一个整数数组 x[], 其中的元素彼此都不相同。
// 找出给定的数组中是否有一个元素满足x[i] = i 的关系,数组下标从 0 开始。
// 举例而言,如果 x[] = −2, −1, 7, 3, 0, 8, 则 x[3] = 3, 因此 3 就是答案。


# include <iostream>
using namespace std;

int main()
{
int n;
cin >> n;
int array[n];

int flag = 1000;

for (int i = 0; i < n; i++)
{
cin >> array[i];
}

for (int i = 0; i < n; i++)
{
if (i == array[i])
{
flag = i;
break;
}
}

if (flag == 1000)
{
cout << 'N';
}
else
{
cout << flag;
}

return 0;
}

14 pre63 四大湖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// assignment 6
// 四大湖
/*
A 说:洞庭湖最大,洪泽湖最小,鄱阳湖第三。
B 说:洪泽湖最大,洞庭湖最小,鄱阳湖第二,太湖第三。
C 说:洪泽湖最小,洞庭湖第三。
D 说:鄱阳湖最大,太湖最小,洪泽湖第二,洞庭湖第三。
已知这 4 个湖的大小均不相等,4 个人每人仅答对一个,请编程按照鄱阳湖、洞庭湖、太湖、洪泽湖的顺序给出他们的大小排名。
*/

# include <iostream>
using namespace std;

// a 洞庭湖
// b 洪泽湖
// c 鄱阳湖
// d 太湖

int main()
{
int a, b, c, d;
a = b = c = d = 0;

for(a = 1; a <= 4; a++)
{
for(b = 1; b <= 4; b++)
{
for(c = 1; c <= 4; c++)
{
for (d = 1; d <= 4; d++)
{
if (
(a != b && a != c && a != d && b != c && b != d && c != d)
&&((a == 1) + (b == 4) + (c == 3) == 1)
&&((b == 1) + (a == 4) + (c == 2) + (d == 3) == 1)
&&((b == 4) + (a == 3) == 1)
&&((c == 1) + (d == 4) + (b == 2) + (a == 3) == 1)
)
{
cout << c << endl;
cout << a << endl;
cout << d << endl;
cout << b << endl;
}
}
}
}
}

return 0;
}

15 pre66 文字排版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// assignment 8
// 给一段英文短文,单词之间以空格分隔(每个单词包括其前后紧邻的标点符号)。请将短文重新排版,要求如下:
// 每行不超过80个字符;每个单词居于同一行上;在同一行的单词之间以一个空格分隔;行首和行尾都没有空格。

// 这个题目有点难,很绕

# include <iostream>
using namespace std;

int main()
{
int num;
cin >> num;
char danci[1000][100];
int zifushu[1000];

int i, j;
for (i = 0; i < num; i++)
{
cin >> danci[i];
}
for(i = 0; i < num; i++)
{
for (j = 0; ;j++)
{
if (danci[i][j] != '\0')
{
zifushu[i]++;
}
else
{
break;
}
}
}

int leiji = 0;
int shuchudancishu = 0;
for(i = 0; i < num; i++)
{
leiji = leiji + zifushu[i] + 1;
if (leiji > 81)
{
for(j = shuchudancishu; j < i - 1; j++)
{
cout << danci[j] << ' ';
}
cout << danci[j] << endl;
leiji = 0;
shuchudancishu = i;
i--;
}
}
for (j = shuchudancishu; j < i -1; j++)
{
cout << danci[j] << ' ';
}
cout << danci[j] << endl;
return 0;

}

16 pre70 矩阵边缘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// assignment 8
// 输入一个整数矩阵,计算位于矩阵边缘的元素之和。
// 所谓矩阵边缘的元素,就是第一行和最后一行的元素以及第一列和最后一列的元素。

// 第一行为整数k,表示有k组数据。
// 每组数据有多行组成,表示一个矩阵: 第一行分别为矩阵的行数m和列数n(1 <= m < 100,1 <= n < 100),两者之间以空格分隔。
// 接下来输入的m行数据中,每行包含n个整数,整数之间以空格作为间隔。

# include <iostream>
using namespace std;

int main()
{
int k;

int m, n;

cin >> k;

while (k != 0) // 关键部分
{
cin >> m >> n;
int array[m][n];

int sum = 0;

for (int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
cin >> array[i][j];
if(i == 0 || j == 0 || i == (m - 1) || j == (n - 1)) // 关键部分
{
sum += array[i][j];
}
}
}

cout << sum << endl;

k--;
}
return 0;
}

17 pre72 配对碱基链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// assignment 9
// 配对碱基链

/*
5
A
TCG
ATCG
GCAAA
TTTTTT
*/


# include <iostream>
# include <cstring>
using namespace std;

int main()
{
int k;
cin >> k;
getchar(); // 必要
while (k != 0)
{
char lian[1000];
fgets(lian, 1000, stdin);

int len = strlen(lian);
// cout << len << endl;

for(int i = 0; i < len; i++)
{
if (lian[i] == 'A') { cout <<'T'; }
if (lian[i] == 'T') { cout <<'A'; }
if (lian[i] == 'G') { cout <<'C'; }
if (lian[i] == 'C') { cout <<'G'; }
}
cout << endl;

k--;
}

return 0;
}

18 pre310 跳跃游戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// assignment 10
// 跳跃游戏

# include <iostream>
using namespace std;

int main()
{
int n;
cin >> n;
int array[n];

for (int i = 0; i < n; i++)
{
cin >> array[i];
}

int count = 0; // 计数器
int k = 0;

while(count <= 20)
{
if (k > n - 1 || k < 0)
{
cout << "no";
return 0;
}
else if(k == n - 1)
{
cout << k << ' ' << array[k] << endl;
cout << "yes";
return 0;
}
else
{
cout << k << ' ' << array[k] << endl;
k += array[k];
count++;
}
}

cout << "no";
return 0;
}

19 pre311 合法括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// assignment 10
// 合法括号

# include <iostream>
# include <stack>
# include <cstring>
using namespace std;

# define N 50000
stack<int> S; // 定义栈
int judge(char *array);

int main()
{
char array[N];
fgets(array, N, stdin);
// fputs(array, stdout);
if(judge(array) == 1){ cout << '1';}
else{ cout << '0';}
return 0;
}
int judge(char *array)
{
char *p;
p = array;
while(*p != '\0')
{
char left = '\0';
char right = '\0';
char ch = *p;
// cout << "ceshi:" << ch << endl;
switch(ch)
{
case '(': S.push(ch); break;
case '{': S.push(ch); break;
case '[': S.push(ch); break;
case ')': left = '('; right = ch; break;
case '}': left = '{'; right = ch; break;
case ']': left = '['; right = ch; break;
default: break;
}
// cout << "ceshi:" << S.size() << endl;
if (right == ch)
{
if (S.empty()){return 0;}
else
{
char t = S.top();
S.pop();
// 这里是C++ 栈的用法,pop()出栈不返回值,只删除栈顶元素。所以 t = S.pop()不行。
// 合适的做法就是先用top取栈顶元素,然后使用pop删除栈顶元素。
if (t != left){return 0;}
}
}
p++;
}
if (S.empty()){return 1;}
else { return 0;}
}

20 pre312 含k个3的数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// assignment 11
// 含k个3的数

# include <iostream>
using namespace std;
int coutnum(int mm);
int main()
{
int m;
int k;
cin >> m >> k;
int count = coutnum(m);

if (m % 19 == 0 && count == k)
{
cout << "YES";
}
else
{
cout << "NO";
}

return 0;

}

int coutnum(int mm)
{
int cout = 0;
while (mm > 0)
{
if (mm % 10 == 3){cout++;} // 这个统计数字3个数的方法很巧妙
mm /= 10;
}

return cout;
}

21 pre314 运算符判定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// assignment 1
// 运算符判定
/*
两个整数 a 和 b 运算后得到结果 c。表示为:a ? b = c,
其中,?可能是加法 +,减法 -,乘法 *,整除 / 或 取余 %。请根据输入的 a,b,c 的值,确定运算符。
如果某种运算成立,则输出相应的运算符,如果任何运算都不成立,则输出 error.
*/

# include <iostream>
# include <cstdio>

using namespace std;

int main()
{
int a, b, c;
scanf("%d,%d,%d", &a, &b, &c);

if (b != 0) // 被除数b是否为0要进行分类讨论
{
if (a + b == c)
{
cout << '+';
}
else if (a - b == c)
{
cout << '-';
}
else if (a * b == c)
{
cout << '*';
}
else if (a / b == c)
{
cout << '/';
}
else if (a % b == c)
{
// printf("%%");
cout << "%"; // 这里需要注意一下
// cout 直接可以输出‘%’
// printf 输出‘%%’才是‘%’
}
else
{
cout << "error";
}
}
else
{
if (a + b == c)
{
cout << '+';
}
else if (a - b == c)
{
cout << '-';
}
else if (a * b == c)
{
cout << '*';
}
else
{
cout << "error";
}
}

return 0;
}

知识点复习

条件控制

  • 什么时候用if、if-if-if-if……、if-else、if-else if-else if-else

关系运算、逻辑运算

  • &&
  • ||
  • !

循环

  • for
  • while
  • do while

整数取余、取赏

  • x%10
  • x\10(这里是利用是int型变量的特性,利用除法巧妙地去掉最后一位数。)

特殊类型整数分析判断:素数、偶数、最大公约数、最小公倍数、回文数

  • 素数(质数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

int main()
{
int n;
cin >> n;
int flag = 0;
for(int i = 2; i < n; i++)
{
if(n % i == 0)
{
flag = 1;
break;
}
}
if (flag){cout << n << " is not a prime" << endl;}
else{cout << n << " is a prime" << endl;}
return 0;
}
  • 最大公约数(最大公因数)GCD & 最小公倍数LCM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 穷举法
#include <iostream>
using namespace std;

int main()
{
int a, b;
cin >> a >> b;

for(int i = a; i>0 ;i--)
{
if(a%i==0 && b%i==0)
{
cout << a << "和" << b << "的最大公因数是" << i << endl;
break;
}
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
// 最大公约数
int gcd(int x,int y)
{
return y? gcd(y,x%y):x;
}
// 最小公倍数
int lcm(int a, int b) {
return a * b / gcd(a, b);
}

int main() {
int a,b;
cin >> a >> b;
cout << gcd(a, b) << endl;
cout << lcm(a, b) << endl;
return 0;
}
  • 回文数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
using namespace std;
// 最大公约数

int main()
{
int num = 121;

int rnum = 0;
int tmp = num;
while(tmp)
{
int r = tmp%10;
rnum = rnum*10 + r; // 反转。
tmp /= 10;
}

if (rnum == num)
{
cout << "Huiwen" << endl;
}
else
{
cout << "Not huiwen" << endl;
}

return 0;
}

十进制与二进制转换(位运算)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 输入一个10进制正整数和进制数,输出此10进制数的k进制中包含的1的个数

# include <iostream>
# include<cstdio>
using namespace std;

char d[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

int count = 0;
int turn(int n, int k)//将n转为k进制
{
int r=0;
r = n%k;
n = n/k;
if(n!=0) {turn(n,k);}
cout << "ceshi1:" << d[r] << endl;
if (d[r] == '1'){count++;}
return count;
}
int main()
{
int x, m;
scanf("%d%d",&x,&m);
int ret = turn(x,m);
cout << ret;
return 0;
}
运算符 作用 示例
& 按位与 两个操作数同时为1结果为1,只要有一个为0,结果为0
| 按位或 两个操作数只要有一个为1,结果就为1
~ 按位非 操作数为1,结果为0;操作数为0,结果为1
^ 按位非 两个操作数相同,结果为0;不相同结果为1
<< 左移 右侧空位补0,左侧溢出舍弃
>> 右移 右端溢出舍弃,对于无符号数,高位补0。对于有符号数,某些机器将对左边空出的部分用符号位填补(即“算术移位”)

数组的定义、使用(元素访问、排序、修改值)

函数的基本定义和实现

字符串的基本处理:统计长度、数量

递归运算

  • 斐波那契数列;进制转换;汉诺塔;逆波兰表达式;放苹果(把这几个例子动手实现看看)

  • 斐波那契数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// This program demonstrates a recursive function that calculates Fibonacci numbers.
#include <iostream>
using namespace std;
int fib(int); // Function prototype
int main()
{
cout << "The first 10 Fibonacci numbers are: \n";
for (int x = 0; x < 10; x++)
cout << fib(x) << " ";
cout << endl;
return 0;
}
int fib (int n)
{
if (n <= 0) //base case
return 0;
else if (n == 1) //base case
return 1;
else
return fib(n - 1) + fib(n - 2);
}
  • 进制转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 输入一个10进制正整数和进制数,输出此10进制数的k进制中包含的1的个数

# include <iostream>
# include<cstdio>
using namespace std;

char d[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

void turn(int n, int k)//将n转为k进制
{
int r=0;
r = n%k;
n = n/k;
if(n!=0) {turn(n,k);}
cout << d[r];
}
int main()
{
int x, m;
scanf("%d%d",&x,&m);
turn(x,m);
return 0;
}
  • 汉诺塔
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;

void hanoi(int N ,char source , char relay ,char destination)
{
if(N == 1)
cout << source << "-->" << destination << endl ;
else
{
hanoi(N-1 , source , destination , relay) ;
cout << source << "-->" << destination << endl ;
hanoi(N-1 , relay , source , destination) ;
}
}

int main()
{
cout << "移动盘子:" << endl ;
hanoi(3, 'A' , 'B' , 'C') ;

return 0;
}
  • 逆波兰表达式

逆波兰表示法在求值时不需要使用括号,也不需要考虑优先级。

比如:1 + 1 * 2 转换为 1 1 2 * +(1 + 1) * 2 转换为 1 1 + 2 *

C++实现逆波兰表达式的例题详解_C 语言_脚本之家 (jb51.net)

  • 放苹果

递推递归算法解决放苹果问题:把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#include <bits/stdc++.h>
using namespace std;
int f(int m,int n)
{
if(m==0||n==1)
{
return 1;
}
if(n>m)
{
return f(m,m);
}
else
{
return f(m,n-1)+f(m-n,n);
}
}
int main()
{
int m,n,c;
cin>>c; // 实验次数
while(c--)
{
cin>>m>>n;
cout<<f(m,n);
}
return 0;
}

拓展内容

常用编辑器与编译器

Windows

  1. Microsoft Visual Studio ⭐

Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 (microsoft.com)

免费的开发人员软件和服务 - Visual Studio (microsoft.com)

在我心中Visual Studio 是全宇宙最强大的IDE(非嵌入式领域),唯唯诺诺(bushi)的Microsoft在VS这里我觉得是真巨硬!

集成开发环境(Integrated Development Environment,简称IDE,也称为Integration Design Environment、Integration Debugging Environment)是一种辅助程序开发人员开发软件应用软件,在开发工具内部就可以辅助编写源代码文本、并编译打包成为可用的程序,有些甚至可以设计图形接口。

IDE通常包括编程语言编辑器自动构建工具、通常还包括调试器。有些IDE包含编译器解释器,如微软的Microsoft Visual Studio,有些则不包含,如EclipseSharpDevelop等,这些IDE是通过调用第三方编译器来实现代码的编译工作的。有时IDE还会包含版本控制系统和一些可以设计图形用户界面的工具。许多支持面向对象的现代化IDE还包括了类别浏览器、对象查看器、对象结构图。虽然目前有一些IDE支持多种编程语言(例如EclipseNetBeansMicrosoft Visual Studio),但是一般而言,IDE主要还是针对特定的编程语言而量身打造(例如Visual BasicSpyder)。

集成开发环境 - 维基百科,自由的百科全书 (wikipedia.org)

编辑器:VS的用户界面

编译器:Visual C++ (简称 MSVC)

一个现代编译器的主要工作流程:源代码 (source code) →预处理器 (preprocessor) → 编译器 (compiler) → 汇编程序 (assembler) → 目标代码 (object code) → 链接器 (Linker) → 可执行程序 (executables)。

VS的编译器使用的是Visual C++ (简称 MSVC)是由微软开发的,只能用于 Windows 操作系统。

  • 优点:功能强大,非常非常强大!!!
  • 缺点:操作较为复杂,更适合用在大型项目的开发中;过高的安全性,Microsoft为了追求安全性对很多函数进行了自定义,使用C/C++的标准函数时,VS会直接报错;软件非常大,好像差不多20+GB。
  • 总结:VS非常强大,但非适合所有的人,我平时用的最多的编程语言并不是C/C++,并且写C/C++时,也不怎么写很大的程序,所以我平时用VS很少,主要用下面提及的几种工具。但对于初学者来说,使用VS也算是一个不错的选择(有利有弊),开发环境都配置好了,基本上来说就是无脑操作。

  1. Microsoft Visual Studio Code + MinGW-W64 GCC⭐

Visual Studio Code - Code Editing. Redefined

MinGW-w64 - for 32 and 64 bit Windows download | SourceForge.net

编辑器:VScode

编译器:MinGW-W64 GCC

  • VScode

Microsoft在开源领域之前是做到非常差的,什么软件都和Windows进行捆绑,但Visual Studio Code的出现,让我对Microsoft有了一个新看法。

VScode全称是Visual Studio Code,是微软推出的一个跨平台的编辑器,能够在windows、Linux、IOS等平台上运行,通过安装一些插件可以让这个编辑器变成一个编译器。 VSCode支持C++、Python、Java、C#、Go等多种语言,功能强大、插件丰富并且启动速度极快,值得每个开发人员尝试一把!

简单来说,VSCode就是一个非常厉害的文本编辑器,但是他支持插件,配置好环境之后就能编写程序,编译,调试,运行。当VScode配置好开发环境之后,就相当于一个精简版的VS了。

  • MinGW-W64 GCC

MinGW 提供了一套简单方便的Windows下的基于GCC 程序开发环境。MinGW 收集了一系列免费的Windows 使用的头文件和库文件;同时整合了GNU ( http://www.gnu.org/ )的工具集,特别是GNU 程序开发工具,如经典gcc, g++, make等。MinGW是完全免费的自由软件,它在Windows平台上模拟了Linux下GCC的开发环境,为C++的跨平台开发提供了良好基础支持,为了在Windows下工作的程序员熟悉Linux下的C++工程组织提供了条件。

  • 优点:简介,自定义程度强
  • 缺点:配置环境较复杂,学习成本也比较高
  • 总结:VScode是我目前用的比较多的,基本上所有我会的开发语言,我都会在VScode上进行。对于初学者可能要有一定的学习成本,但如果对VS的过于复杂的功能所困扰的话,可以尝试一下VScode + MinGW-W64 GCC

  1. Sublime Text + Microsoft Terminal + MinGW-W64 GCC⭐

Sublime Text - Text Editing, Done Right

Windows Terminal installation | Microsoft Docs

MinGW-w64 - for 32 and 64 bit Windows download | SourceForge.net

这三个工具的组合本质上和 Microsoft Visual Studio Code + MinGW-W64 GCC 的功能一模一样!!但使用起来这三个工具的组合更像Vim + GCC。

Sublime Text比记事本功能强,比VSCode功能弱。


  1. Vim + Microsoft Terminal + MinGW-W64 GCC

download : vim online

Windows Terminal installation | Microsoft Docs

MinGW-w64 - for 32 and 64 bit Windows download | SourceForge.net

没想到吧!Windows下也有Vim。不赘述了


  1. Dev-C++

Dev-C++ download | SourceForge.net

对于Dev-C++我用的不是很多,只在我的Surface Go电脑上配置了一下,但也没怎么用过,也许可以说是低配版VS(?)

编辑器:Dev-C++的用户界面

编译器:MingW64/TDM-GCC

dev c++是什么编译器?有什么优缺点? - 行业资讯 - 亿速云 (yisu.com)


  1. CLion + MinGW-W64 GCC

CLion: A Cross-Platform IDE for C and C++ by JetBrains

CLion是一个编辑器,没有内置编译和调试工具,所有也是要搭配MinGW-W64 GCC才能使用的。

CLion是商业软件,需要收费,我用的不是很多,不再赘述。

需要介绍一下的是Jet Brains,CLion就是该公司推出的一个软件,该公司的其他软件也非常有名且好用!

Jet Brains是一家捷克软件开发公司,该公司位于捷克的布拉格!

IDE

.NET

团队工具

编程语言

  • Kotlin - 一个用于现代多平台应用的静态编程语言 。Kotlin可以编译成Java字节码,支持在JVM上运行;也可以编译成JavaScript,方便在没有JVM的设备上运行。Kotlin已正式成为Android官方支持开发语言。


Linux

  1. Vim + GCC⭐

GCC的安装:如何在 Ubuntu 20.04 上安装 GCC(build-essential)-阿里云开发者社区 (aliyun.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
# Ubuntu没有提供C/C++的编译环境,因此还需要手动安装。但是如果单独安装gcc以及g++比较麻烦,但Ubuntu提供了一个build-essential软件包。

# 查看该软件包的依赖关系:
(base) ubuntu@VM-8-15-ubuntu:~$ apt-cache depends build-essential
build-essential
|Depends: libc6-dev
Depends: <libc-dev>
libc6-dev
Depends: gcc
Depends: g++
Depends: make
make-guile
Depends: dpkg-dev

编辑器:Vim:Vim|Murphy

编译器:gcc/g++:Linux GCC简明教程(使用GCC编写C语言程序) (biancheng.net)

调试器:gdb:Ubuntu1804下安装gdb与使用 - michaelchengjl - 博客园 (cnblogs.com)

1
2
3
sudo apt-get install gbd

gbd --version
1
2
3
4
5
6
(base) ubuntu@VM-8-15-ubuntu:~$ gdb --version
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Linux上简单的C/C++程序是编写与运行我还会一点点,但程序复杂了,运行报错我看不懂了,需要调试器gbd的参与了,那这个时候我也凉凉了,因为我也不怎么会。还是那句话,我现在主要用到编程语言并不是C,很多复杂的东西我也不会😥😭。


  1. 使用VScode SSH 连接云服务器 ⭐

Connect over SSH with Visual Studio Code

这是我目前使用Linux系统最常用的方法了!


参考资料

Windows端个人常用软件清单|Murphy

VS2019提示scanf不安全问题 - Townmacro - 博客园 (cnblogs.com)

VScode 看这一篇就够了_Jack_joker的博客-CSDN博客_vscode

MinGW-w64简介_itas109的博客-CSDN博客_mingw64是什么

MinGW 是什么?_大儿童梦里花开的博客-CSDN博客_mingw是什么

JetBrains_百度百科 (baidu.com)

最适合C/C++语言初学者使用的编译软件—–Dev-C++的详细下载安装教程!




查看C&C++编译器所实现的标准

Language Standards

Language standards requires the existence of pre-defined macros.

Name Macro Standard
C89 __STDC__ ANSI X3.159-1989
C90 __STDC__ ISO/IEC 9899:1990
C94 __STDC_VERSION__ = 199409L ISO/IEC 9899-1:1994
C99 __STDC_VERSION__ = 199901L ISO/IEC 9899:1999
C11 __STDC_VERSION__ = 201112L ISO/IEC 9899:2011
C18 __STDC_VERSION__ = 201710L ISO/IEC 9899:2018
C++98 __cplusplus = 199711L ISO/IEC 14882:1998
C++11 __cplusplus = 201103L ISO/IEC 14882:2011
C++14 __cplusplus = 201402L ISO/IEC 14882:2014
C++17 __cplusplus = 201703L ISO/IEC 14882:2017
C++/CLI __cplusplus_cli = 200406L ECMA-372
DSP-C ISO/IEC JTC1/SC22 WG14/N854
EC++ __embedded_cplusplus Embedded C++

C

年份 C标准 通用名 别名 标准编译选项 GNU扩展选项
1972 Birth C - - - -
1978 K&R C - - - -
1989-1990 X3.159-1989, ISO/IEC 9899:1990 C89 C90, ANSI C, ISO C -ansi, -std=c90, -std=iso9899:1990 -std=gnu90
1995 ISO/IEC 9899/AMD1:1995 AMD1 C94, C95 -std=iso9899:199409 -
1999 ISO/IEC 9899:1999 C99 - -std=c99, -std=iso9899:1999 -std=gnu99
2011 ISO/IEC 9899:2011 C11 - -std=c11, -std=iso9899:2011 -std=gnu11
2018 ISO/IEC 9899:2018 C18 - -std=c18, -std=iso9899:2018 -std=gnu18

C++

年份 C++标准 通用名 别名 标准编译选项 GNU扩展选项
1978 C with Classes - - - -
1998 ISO/IEC 14882:1998 C++98 - -std=c++98 -std=gnu++98
2003 ISO/IEC 14882:2003 C++03 - -std=c++03 -std=gnu++03
2011 ISO/IEC 14882:2011 C++11 C++0x std=c++11, std=c++0x std=gnu++11, std=gnu++0x
2014 ISO/IEC 14882:2014 C++14 C++1y std=c++14, std=c++1y std=gnu++14, std=gnu++1y
2017 ISO/IEC 14882:2017 C++17 C++1z std=c++17, std=c++1z std=gnu++17, std=gnu++1z
2020 to be determined C++20 C++2a -std=c++2a std=gnu++2a

C编译器的标准

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>

int main()
{
printf("%d\n", __STDC__);
printf("%ld\n",__STDC_VERSION__);
return 0;
}

C++编译器的标准

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <stdlib.h>

int main()
{
printf("%ld\n",__cplusplus);
return 0;
}

image-20221003120708294

gcc/g++

gcc发展到今天已经不单单可以编译C语言了,还可以编译C++、Java、Object-C等多种其他语言
有一种说法是GCC的全名是GNU Compiler Collection(GUN 编译器集合),而gcc是GCC中用于编译c语言的编译器
事实上,gcc看起来并不像是一个编译器,而像一个调度器,针对于不同的文件调用不同编程语言的编译器
对于后缀为*.c的文件,gcc把它当作是C语言程序源代码,而g++当作是C++程序源代码
对于后缀为*.cpp的文件,gcc和g++都会当作是C++程序源代码
使用g++编译文件时,g++会自动链接标准库STL,而gcc不会自动链接STL,所以再使用gcc编译C++程序是有时会报错
在用gcc编译C++文件时,为了能够使用STL,需要加参数 –lstdc++ ,但这并不代表 gcc –lstdc++ 和 g++等价
据说g++会调用gcc,对于C++代码,因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接
需要注意的是,虽说g++会调用gcc,对于*.c文件来说,编译出来的可执行文件也不一样,因为gcc会当成C语言程序编译,而g++调用的gcc会把它当做C++语言程序来编译,这或许就能解释为什么用g++就可以编译所有C/C++的程序,还要有gcc的存在(就我测试来看,同样的C语言代码,g++编译出来的程序体积要大一些)

C语言的标准变化

C语言标准——C89、C99、C11、C17、C2x … - 知乎 (zhihu.com)

C的标准化过程

C语言自诞生到现在,期间经历了多次标准化过程,主要分成以下几个阶段:

Traditional C

此时的 C 语言还没有标准化,来自“C Programming Language, First Edition, by Brian W. Kernighan, Dennis M. Ritchie. Prentice Hall PTR 1978”的 C 描述可算作“正式”的标准,所以此时的 C 也称为“K&R” C。

期间 C 语言一直不断的发生细微的变化,各编译器厂商也有自己的扩展,这个过程一直持续到20世纪80年代末。

C89

考虑到标准化的重要,ANSI(American National Standards Institute)制定了第一个 C 标准,在1989年被正式采用(American National Standard X3.159-1989),故称为 C89,也称为 ANSI C。

该标准随后被 ISO 采纳,成为国际标准(ISO/IEC 9899:1990)。

C89 的主要改动:

  • 定义了 C 标准库;
  • 新的预处理命令和特性;
  • 函数原型(prototype);
  • 新关键字:const、volatile、signed;
  • 宽字符、宽字符串和多字节字符;
  • 转化规则、声明(declaration)、类型检查的改变。

C95

这是对 C89 的一个修订和扩充,称为“C89 with Amendment 1”或 C95,严格说来并不是一个真正的标准。

C95 的主要改动:

  • 3个新标准头文件:iso646.h、wctype.h、wchar.h;
  • 一些新的标记(token)和宏(macro);
  • 一些新的 printf/scanf 系列函数的格式符;
  • 增加了大量的宽字符和多字节字符函数、常数和类型。

C99

1999年,在做了一些必要的修正和完善后,ISO 发布了新的 C 语言标准,命名为 ISO/IEC 9899:1999,简称“C99”。

C99 的主要改动:

  • 复数(complex);
  • 整数(integer)类型扩展;
  • 变长数组;
  • Boolean 类型;
  • 非英语字符集的更好支持;
  • 浮点类型的更好支持;
  • 提供全部类型的数学函数;
  • C++ 风格注释(//)。

C11

2007 年,C语言标准委员会又重新开始修订C语言,到了 2011 年正式发布了 ISO/IEC 9899:2011,简称为 C11 标准。

C11 标准新引入的特征尽管没 C99 相对 C90 引入的那么多,但是这些也都十分有用,比如:字节对齐说明符、泛型机制(generic selection)、对多线程的支持、静态断言、原子操作以及对 Unicode 的支持。

C17

C17(也被称为为 C18)是于2018年6月发布的 ISO/IEC 9899:2018 的非正式名称,也是目前(截止到2020年6月)为止最新的 C语言编程标准,被用来替代 C11 标准。

C17 没有引入新的语言特性,只对 C11 进行了补充和修正。

C2x

下一个版本的 C 标准,预计将于2022年12月1日完成。

有许多人提出想为 C 语言添加面向对象的特性,包括增加类、继承、多态等已被 C++ 语言所广泛使用的语法特性,但是最终被委员会驳回了。因为这些复杂的语法特性并不符合 C 语言的设计理念以及设计哲学,况且 C++ 已经有了这些特性,C 语言无需再对它们进行支持。


高级语言程序设计
https://cosmicdusty.cc/post/Knowledge/HighlevelLanguageProgramming/
作者
Murphy
发布于
2022年9月8日
许可协议