C---面向过程

面向过程的C++程序设计

基于过程的程序设计和算法

算法

算法的概念

一个基于过程设计的程序应该具有两个部分

1.对数据的描述: 程序中数据和其表现形式(数据结构)

2.对操作的描述: 操作步骤(算法)

所以就产生了一个等式: 程序 = 算法 + 数据

算法必须具体的描述出问题的每一步解决方案

算法的表示

无非就那么几种

自然语言 伪代码 流程图 计算机语言

C++的程序结构和C++语句

每一个程序单位由以下3个部分组成

  • 预处理指令: 如#include#define
  • 全局声明: 在函数外部对数据类型和函数以及变量的声明和定义
  • 函数: 包括函数首部和函数体, 在函数体中可以包含声明语句和执行语句

如一个最简单的hello_world程序

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>  //预处理指令
using namespace std; //函数外的全局声明
int a = 1; //函数外的全局声明
int main() //函数首部
{
cout << "Hello world"; //执行语句
int b, sum; //函数内的声明
cin >> b; //执行语句
sum = a + b; //执行语句
cout << "a + b = " << sum << endl; //执行语句
return 0; //执行语句
}

如果一个变量在函数之外进行声明, 此变量就是全局变量(生命周期为整个程序)

C++语句

C++的语句可以分为以下的四种

  • 声明语句

    如: int变量定义

  • 执行语句

    通知计算机完成一系列操作的语句:

    • 程序流程控制语句

      语句 效果
      if() ~ else ~ 条件选择
      for() ~ for循环
      while() ~ while循环
      do ~ while() dowhile循环
      continue 跳过循环
      break 跳出循环
      switch 多分枝选择
      goto 转到
      return 返回

      其中的()表示其中包括一个判断条件, ~表示内嵌语句

  • 函数和流对象调用语句

    1
    2
    name(x, y, z); //调用name函数的语句
    cout << x << endl; //流对象调用语句
  • 表达式语句

    一个表达式+;

  • 空语句

    1
    ;

    其作用等同于python的pass

    它什么也不做, 只是有时被当作是程序的转向点, 或是循环体占位

  • 复合语句

    {}框选的语句就是复合语句, 符合语句被当作是一条语句

    1
    2
    3
    4
    5
    6
    {
    z = x + y;
    if (z > 100)
    z = z - 100;
    cout << z;
    }

    符合语句的应用范围很广, 比如循环体和条件判断

赋值操作

赋值语句是由赋值表达式+;组成的语句

因为赋值符号可以被用作表达式当中的赋值, 所以表达式就可以很很多其他的运算符和操作连用, 就产生了很多灵活的操作(跟python很相似)

C++的输入和输出

Python: input输入 print输出—函数

C: scanf输入 printf输出—函数

C++: cin输入 cout输出—流对象

C++的输入和输出是由”流”的方式呈现的, 流就就是设备之间通信的数据流

数据流是由一系列字节组成的, 数据按照流的顺序排列的

cout是输出流的名字 cin是输入流的名字 <<是流插入运算符 >>是流提取运算符

cin流的路径

键盘 —“数据”—> cin流对象 ——> >>(提取) ——> 计算机(指定变量)

cout流的路径

程序数据 —“Hello”—> <<(插入) ——> cout(流对象) ——> 控制台

有关流对象和插入/提取运算符的信息都是存放在输入输出流库中的<iostream>中的

如果需要使用, 必须要进行预处理导入

cin/cout的基本使用

cin >> 变量1 >> 变量2 >> 变量n;

cout << 表达式1 << 表达式2 << 表达式3 << 表达式n;

cin的输入会自动根据变量类型来在输入流中提取相应长度的数据(尽管你只是输入了一段数据)

比如char型的数据只会按顺序读取数据中的单个字符

每当遇到空格和换行的时候, cin就会当成下一个变量的开始, 如果没有下一个变量, 就会执行确认, 所以cin不能把空格字符和回车换行符作为字符输入给变量, 它们将被跳过, 如果需要, 会将用到接下来的getchar函数

cout是没有结束字符的, 每一条cout都会紧跟着上一条的输出结尾后面输出

所以文字间的空格都需要手动添加

在组织流输入的时候记得要注意输入的数据类型, 否则容易出错

在标准输入/输出流中使用控制符

流控制符都包含在头文件<iomanip>内, 其作用有很多, 比如定义小数位数等

控制符 作用
dec 设置数的基数为10
hex 设置数的基数为16
oct 设置数的基数为8
setfill(c) 设置填充字符c(c可以是变量也可以是常量)
setprecision(n) 设置浮点精度为n位, 在以fixed(固定小数位数)和scientific(指数)形式输出时n为小数位数
setw(n) 设字符宽度n位
setiosflags(ios:fixed) 浮点数固定小数位数
setiosflags(ios::scientific) 浮点数科学计数法表示
setiosflags(ios::left) 输出数据左端对其
setiosflags(ios::right) 输出数据右端对齐
setiosflags(ios::skipws) 忽略前导空格
setiosflags(ios::uppercase) 16进制大写
setiosflags(ios::lowercase) 16进制小写
setiosflags(ios::showpos) 正数+显示

如果cout语句中没有任何的流数据, 只有操作符的话, 则可以统一设定数据流输出格式

putchar和getchar

putchar(c)可以向终端输出一个字符, 其中的C可以是字符(转义字符也可)也可以是ASCII码

getchar(c)的返回值是输入数值的整数ASCII码, 所以可以获取空格字符以及回车字符等特殊字符的输入

scanf和printf

C语言的输入和输出函数是scanfprintf函数, 它们使用起来要比C++的流式输入输出繁琐很多

下面是源码举例, 我们先按照书上的做法, 对scanf的赋值对象设定为其对应的变量地址(我加了一条, 输出各个变量的内存地址)

scanf

可以正常输出变量内的值

如果不使用内存操作符&的话, 就出现这样的情况

wrong

可以看出调试控制台中并没有相应的变量对应值

关系/逻辑运算

对于if函数, 我们在python中已经可以很熟练的使用了, 在C++中, 关键字都是一样的, 只不过有一些语法格式上的出入需要解释, 先来看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int ori_prices;
float discount = 0.75;
float prices;
cout << "Input your origin prices: \n";
cin >> ori_prices;
if (ori_prices >= 1000)
{
prices = (ori_prices * discount);
cout << "You got a discount !\nYour new prices is " << prices << "$\n";
}
else
{
prices = ori_prices;
cout << "You don't have the discount quallity. The prices is " << prices << "$\n";
}
return 0;
}

就是很简单一个价格判断的例子

但是跟书上的源码有些差别, 这是因为在我打出if之后, vscode的代码补全自动帮我补全至以下格式

if_format

1
2
3
4
if (/* condition */)
{
/* code */
}

这是因为if之后的候选只能是一条语句, 所以我们就可以用符合语句{}将多条语句整合

关系运算和关系表达式

if后的/* condition */就是关系表达式

而我们在python中实现的> < >= <= == !=就不再是算数运算了, 而是关系运算符

运算符之间有优先级, 是:算数 > 关系 > 赋值

而就像算数符之间有优先级一样, 关系符之间也有: <= < >= > > == !=

关系表达式的一般格式: 表达式 关系运算符 表达式

式子的真假是根据两端表达式的值和关系符来确定的

赋值表达式的值分为真和假两大, 注意是, 因为其值会根据赋值对象的类型不同而自适应:

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;
int main(int argc, char const *argv[])
{
int a = 0, b = 1;
int Rint, Fint;
char Rchar, Fchar;
float Rfloat, Ffloat;
Rchar = (a < b);
Rint = (a < b);
Rfloat = (a < b);
Fint = (a > b);
Fchar = (a > b);
Ffloat = (a > b);
cout << Rchar << '\n'
<< Rint << '\n'
<< Rfloat << '\n'
<< Fint << '\n'
<< Fchar << '\n'
<< Ffloat << '\n';
return 0;
}

用这段代码, 并在return处设置断点, 即可获得变量具体状态

value_of_bool

逻辑常量和逻辑变量

C语言没有提供逻辑型的数据, 所有有关逻辑的都是1, 有关值的都是0

C++中有, false和true

逻辑型的数据要用bool关键字声明, 值只能是tureorfalse

测试源码(简略版):

1
2
3
bool bl;
bl = (1>2);
cout << bool;

但这样做程序结果返回值是0

原因就是编译器在处理bool型的truefalse时会直接将其处理为1或0

tf

所以, bool还可以参与算术运算

逻辑运算和逻辑表达式

逻辑运算符就三种, 与&& 非! 或||, 其运算性质就不过多赘述了

其中非!符号的运算优先级最高(有改变变量属性的设定), 所以要最先运算

选择关系和if

选择关系就是if

正常结构

1
2
3
4
5
6
7
8
9
10
if (表达式){
/* 真 所执行的代码 */
}
else if (表达式){
/* 真 所执行的代码 */
}

else{
/* 所有表达式都不满足 所执行的代码 */
}

此外, 除了正常结构中的if和else, if内的复合语句还可以嵌套if(个人习惯: 把if后面的语句一律包在符合语句中, 格式化代码, 更为易读)

条件运算符和条件表达式

条件运算符是一对符号? :每个符号后面各有一个对应的备选值

比如这部分代码

1
2
3
4
5
char t = 'T';
char f = 'F';
int a, b;
cin >> a >> b;
cout << "a>b?" << char((a>b)?t:f);

最后一串等同于下面这一行

1
2
3
4
if (a>b)
cout << char(t);
else
cout << char(f);

所以? :的作用一目了然了, 真取?后面的值, 假取:后面的值

多分支选择和switch

当程序出现多分支需求的时候, 可以采用if嵌套的方法, 去逐级的判断条件, 但如果分支过多, 就会出现if的层层嵌套, 使代码过于冗长, 且丢失可读性, 这时候switch语句就可以解决这个问题

1
2
3
4
5
6
7
8
9
10
11
switch (/* 表达式 */)
{
case /* 常量表达式1 */:
/* 语句 */
break;
case /* 常量表达式n */:
/* 语句 */
break;
default:
break;
}

switch中固定表达式数值等于与case中的值对应上, 就会触发相应的语句, 每一个常量表达式都匹配不上的话就会执行默认的default下面的语句

注意: 每个case中的值必须要互不相同, 且是一个常量值, 不然就会引起矛盾

而且case语句不起到流程控制的作用, 各自的语句执行完后会进入下面一个判断, 从而导致执行default的语句运行, 所以每条语句的内部添加一个break语句用来跳出switch

循环

while

1
2
3
4
while (/* 表达式 */)
{
/* 语句 */
}

while就是先进行循环判断, 若为真则进入循环体, 若为假则结束循环

do-while

1
2
3
4
do
{
/* 语句 */
} while (/* 表达式 */);

do-while就是在while前面多加一个do, 顾名思义就是先进行循环体, 再进行判断是否要进行下一次的循环, 所以无论如何, do-while都会执行一次

for

1
2
3
4
for (/* 初值设定语句 */; /* 条件语句 */; /* 循环变量增值语句 */)
{
/* 语句 */
}

省略初值设定语句: 可以在for循环就设置初值, 但切记使用时要声明

省略条件语句: 即构成死循环

省略循环变量增值语句: 需要自行在循环体中改变循环变量, 要不然也是死循环

省略初值设定语句 循环变量增值语句: 变成一个while

continue和break

continue关键字, 结束本循环
break关键字, 结束本循环

函数

自定义函数, 跟python上的大同小异, 只不过声明用的def要换成对应的格式即可

函数又分为四类: 自定义 有参 无参 库函数(可以互相涵盖)

定义函数的一般形式

1
2
3
4
5
6
7
8
9
10
//无参一般形式
类型名 函数名([void])
{
/* code */
}
//有参一般形式
类型名 函数名(形参列表[没有声明的要进行声明 有初始值的可以进行初始化])
{
/* code */
}

若非特殊情况, 每个函数一定要根据计算情况返回计算值, 这样才能保证函数的工作结果能被使用, void函数的本身就无返回值, 所以不需要添加

return关键字

return就是用来进行函数的返回值输出的

其后方可以添加一个变量, 或者是一个表达式, return就是一个函数的结尾, return执行完成后函数就结束了, 有return的函数才能在主程序调用是赋给某个变量

return返回值的类型要和其所在函数开头所定义的类型一致

函数的调用

  • 函数语句

    单独的函数语句调用, 就是并不需要该函数的返回值仅仅是需要其完成一系列操作的情况

    1
    function_name(parameter_list)
  • 函数表达式

    就是把函数调用在一个表达式当中, 需要用到其返回值(就是函数对式子的输入值)

    1
    a = function_name(parameter_list)
  • 函数实参

    就是把函数返回值当作实参导入另一个函数或方法

    1
    function_name0(function_name1(parameter_list),function_name2(parameter_list))

函数的嵌套

注意: C++中是不允许函数嵌套定义的

也就是说, 你不能在函数中去定义一个新函数

但是, 函数和函数之间可以互相调用

比如有a, b, c三个函数

你可以在a的函数体中调用函数b和c, 同理其他的也一样

函数的声明

在之前学习的时候有过几个例子, 你需要把函数写在主函数之后时, 需要提前在主函数中声明

这一点在函数之间调用时也非常重要, 如果不想考虑程序编写的顺序, 那么就要添加几行函数声明

函数原型

其实你在声明函数的时候也不一定要对形参进行定义, 你只需要给出形参的类型即可

比如

1
float max(float, float)

这是C/C++的一个重要特点, 他的主要作用是根据函数原型在程序编译阶段对调用函数的合法性进行全面检查, 以确保调用时, 函数名, 导入的数据类型、个数都要与定义时一致

函数的外部声明

如果嫌麻烦, 该如何用一个总声明来影响到整个文件呢?

mian之前写好函数原型, 且与mian同级, 这样的声明叫做外部声明, 作用域为整个文件

函数的重载

C++中允许使用相同的函数名来定义不同的工作, 即为函数重载

但互相重名的函数之间需要满足参数个数 参数类型 参数顺序三者至少有一个不i一样, 否则编译器就会混淆函数, 导致错误

函数模板

比如一个求最大值的函数, 需要针对其不同的数据类型来定制函数

如果使用函数重载的方法, 函数体还是要在文件中重复出现, 那么有没有方法能够省略函数体, 只让其出现一次呢?

C++提供了函数模板功能

使用方法:

1
2
3
4
5
template <typename T> // 模板声明, typename T就是指定'T'代表类型参数
T function(T a, T b, T c, T d) // 定义通用函数, T作为虚拟的类型名
{
/* code */
}

调用时只需要带入相对应的数据即可(前提是互相可以运算)

内调函数

如果一个函数在主函数中被调用, 其在主函数中的表达方式是一个调用方法, 函数本身则存在于内存空间的另一个位置所以整个调用过程为

主函数启动 -> 函数调用语句(指向内存空间) -> 转到函数 -> 执行

所以中间指向内存和转到内存也是需要时间的, 当一个函数足够短小简单, 直接运行相比函数调用要花费更少的时间, 但又有重复调用的需求时, 就可以使用内调关键字inline

在函数定义(或者声)的前面加上inline关键字

1
2
3
4
5
6
7
inline void three()
{
if ("")
{
cout << '1';
}
}

当遇到该函数的调用语句时, 编译器就会直接将这个函数的源码直接插入到主函数当中, 顺序执行, 遇到这样的需求时可提高程序执行效率, 但使用不当会造成可执行文件的主函数冗长


有关变量属性的笔记因为笔记结构问题, 移至变量笔记当中


内部函数和外部函数

还是两个神奇的关键字staticcxtern

static可以让函数仅限于所在文件被引用, 以防不同的文件的同名函数之间相互干扰

extern可以让函数在其他文件被调用, 比如一个文件[source.cpp], 其内部只定义一个函数 int fu1(int, int) (没有main), 而想在其他文件中调用这个函数, 就要这样使用extern关键字

1
2
3
4
5
6
7
#include <iostream>
using namespace std;
int main(){
extern int fu1(int, int);
cout << fu1(1, 2);
return 0;
}

.h 头文件

头文件就像python的模块

  • 其内部包含:

    对类型的声明

    函数声明

    内调(inline)函数的定义

    宏定义

    全局变量定义(包括extern定义)

    外部变量的定义

    还可以包含其他头

  • Copyrights © 2024 Cdog Shen
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信