C筑基——基础概念

什么是程序设计语言

  • 系统软件:管理计算机的各种资源(操作系统,语言处理程序等);
  • 应用软件:专为实现某一应用开发的软件(QQ,WeChat等);
  • 软件=程序(为完成特定任务而用某种语言编写的一组指令序列)+文档+数据;
  • 程序设计语言分为:机器语言(计算机直接识别和执行,可读性差),汇编语言(加入助记符的二进制编码,通过Assembler转换为机器指令执行)和高级语言(易于学习和编程,编写成的程序成为源程序);
  • 源程序通过compiler翻译为机器语言(目标程序)执行,目标程序与支持库代码连接构成可执行文件(.exe)
  • C++语言特点:简洁高效,功能强(在C语言基础上增加了面向对象机制
  • C语言特点:结构化与模块化(面向过程)
  • 程序设计过程:问题分析→算法设计→编写程序(对算法的具体描述)→运行调试(先编译后执行)→文档编写

基本知识

所有变量在使用前都需要定义,包括类型和变量名int a

变量名的第一个字符只能是下划线或者字母

  • //单行注释; /* */多行注释;contol+/可对多行同时加注释,再按可取消

  • #预处理指令,类似于Python语言中的import 起到导入函数库的作用,在C语言中#include<stdio.h>中**.h**意味着导入头文件;C++中则是#include<iostream>

  • int mainreturn 0相对应,在一个程序中必须但也只能有一个以main命名的主函数;;是一条语句的终止符,每条可执行语句必须以此结束;

  • C语言中用printf格式化输出函数,\n换行,不过在C++中则用cout<<"s="<<s输出(成为输出流),用endl来换行;

  • int可用于定义整数&整数变量;(定义多个同类型变量可用”,”连接)

  • 对于C语言的scanf("%d",&a);中,scanf 用于获取键盘输入信息,%后加格式控制符,d表示一个十进制的整型数据,&表示输入值归属于哪个变量(只有在scanf函数中+&是这意思),a对应前面定义的变量;不要忘记“显示内容”;如果是C++,使用cin来进行输入,常为cin>>a>>b(也称为输入流)。

  • 函数体用**{}**括起来;

通过函数形式封装可重复使用并且使得程序功能更清晰,但需在主程序起始声明(void+函数)

1
2
3
4
5
6
7
/*这是一个最简单的C++程序*/
#include<iostream> //使用C++的命名空间
using namespace std;
int main(){
cout<<"Hello!"<<endl;
return 0;
}

[!NOTE]

C++标准库的类和函数是在命名空间std中声明的,如果源程序中使用到了标准库1里的有关内容就需要使用using namespace std;来做声明,否则会报错;

命名空间的来源是由于避免多团队开发时发生变量重名而提出的概念,一般有输入/输出的信息都要用iostream库中的预处理指令来提供cin/cout这些指令,也就要进行声明了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*这是一个基本的C语言程序*/
#include<stdio.h>

//定义主函数main()
int main(){
void comparing(int x);//函数comparing声明
void max(int x,int y);//函数max声明
int a,b; //定义两个整数变量
int sum;
printf("请输入第一个整数:\"\"");//加反斜杠做转义符使其只是"
scanf("%d",&a);
printf("请输入\n第二个变量:");//\n表示换行
scanf("%d",&b);
sum=a+b;
comparing(sum);
max(a,b);
return 0;
}
int comparing(int a,int b){
...
}
int max(int a,int b){
...
}

​ 对程序进行简单总结,可将其概括为3个部分:预处理指令+全局声明部分+函数(实现操作);而函数除函数名外,则由函数首部()和函数体{}(局部声明+执行部分)来构成;我们允许一个函数没有声明和执行,即作为一个空函数(这是合法的);

而语句通常分为声明语句(用于提供定义)和执行语句(用于实现指定操作);

在函数的实际执行过程中会从main函数开始执行并在其中结束执行,其余的函数需在其中进行调用。

正如之前所说,是C++相较于C的最重要发展,通过类的引入,我们可实现面向对象程序设计的封装、继承和派生等功能,一个类中可包含数据成员和成员函数并被分为私有(仅能被本类成员函数调用)/公有属性。

C++程序在书写上相对更自由,不过为了程序清晰,我们一行只写一个语句就好了,加上注释也更方便阅读(但要注意C++中对大小写是会区分的,h与H并不能认定为同一个变量);

程序设计错误原因分为三种:

  • 语法错误:编译器报错;
  • 运行错误:由于某种意外(进程冲突等)导致程序非正常终止;
  • 逻辑错误:程序可正常运行但结果非预期。

数据类型

数据(计算机处理的主要对象)存在的形式,作为数据的一种属性,我们通过声明不同的数据类型告知编译器/解释器如何使用数据。数据类型决定着数据占内存的字节数、数据的取值范围以及在其上可进行的操作。

对于正数来说,正,原,补码相同;对于负数,其补码等于绝对值后的反码加1;

字节/比特,是数据存储的单位——1B=8bit(位数)

常见的数值转换(用于表示存储大小,就像手机内存大小啥的)

1K 2^10 B
1M 2^10 K
1G 2^10 M
1T 2^10 G

相较于C语言,C++中新增了长双精度类型long double)以及派生的类型(class);此外我们用布尔型bool表示逻辑判断型,空类型void表示无值型。

类 型 字节数 位 数 取值范围
int(用于表示整型数字 4 32 -2147483~2147483647
unsigned int 4 32 0~4294967295
short(短整型) 2 16 -32768~32767(2^15-1)
unsigned short 2 16 0~65535(2^16-1)
long(对于long类型来说,在32位的系统中的大小还是4B取值范围也和int一样,不过现在大部分已经是64位系统了) 8 64 -9.2×10^18~9.2×10^18
unsigned long 8 64 0~18446744073709551615
char(字符型,表示字母数字等) 1 8 -128~127
unsigned char 1 8 0~255
float(6位有效数字) 4 32 -3.4×10^-33~3.4×10^33
double(15位有效数字) 8 64 -1.7×10^-308~1.7×10^308
long double(在linux环境下其取值范围可变大到128位) 8 64 -1.7×10^-308~1.7×10^308

​ 其实在C++中并没有严格规定每一种数据所占的字节数,只规定int型所占字节数须在long和short之间(由于短型在存储空间上只占一般型的一半,所以在其取值范围内使用短型可以节省存储空间提升效率);

数据一般以补码形式存放,最高位表示符号,但对于指定为unsigned的数据,全部位数都用于表示数值本身。因此若存储的数据类型没有负值的话用unsigned型可以扩大存储范围。

对于浮点型数据

float(4byte单精度浮点数类型,可精确到小数点后6位,超过六位会四舍五入截断),对应%f ,精确到4位可写%.4f;

double(8byte双精度类型,可精确到小数点后十二位),对应%lf,如果要精确到十位可写%.10lf;

在C语言中我们用printf类函数来做格式化输出,%c按字符输出,\n表示换行,\a响铃,\b退格(将光标后退一位),\r不换行回到本行开始位置,123.45E+5 = 123.45 * 10的5次;%e%E则表示用指数形式输出结果;%f表示用小数形式;%g将自动输出前两者中宽度较短的结果;%s输出字符串;%o无符号八进制输出,%u无符号十进制输出结果,%x无符号十六进制输出结果对于%4d当使用键盘输入12345时,系统将按照格式控制自动截取前4个数字存入变量中

关于printf函数输出解释

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
//主函数引入
int main()
{
int q=0;
p=q;//替换变量
q=24;//重新赋值
printf("c=%c\n",c); //输出单个字符
printf("d=%-3c\n",d); //左端对齐,宽度3列,右补2格
printf("e=%4c\n",e); //右端对齐,宽度4列,左补3格
printf("%zu\n",sizeof(int));
return 0; //程序成功运行
}

常量

​ 常量的值不能改变,常见的可分为三种:数值型、字符型(用两个单引号包含)和字符串型(用双引号包含)。

数值常量(a,b都是变量,等号右侧的是常量)

用于区分类型,以便于在赋值时或函数进行参数的虚实结合式匹配数据类型。例如

1
2
unsigned short a = 50000;  //√    
unsigned short b = 80000; //× ;因为unsigned short的取值范围为0~65535

对于整型的常量我们通常有三种进制表示方式:十进制(d),八进制(在常熟开头加数字0,表示以八进制形式进行表示),十六进制(在常数开头前加0x)。

对于浮点型数据C++一般默认按照double类型进行存储

1
2
3
4
5
6
7
/*直接按照小数形式写入*/
int a = 2.444;
float b = 35.6262727267;
double c = 59.672949442;
cout<<a<<endl; //输出2,警告:从double转int,可能丢失数据
cout<<b<<endl; //输出35.6262 警告:从double转float ,可能丢失数据
cout<<c<<endl; //输出59.6729 cout默认最多输出6位有效数字

我们也可以将浮点数写成指数形式,例如对于0.5442可以写成54.42×10^-2,在程序中我们可以表示为54.42e-2(e表示以10为底);其实无论是小数还是指数形式最终在内存中都会以指数形式进行存储,对于数字部分的规格化表示其小数点后第一个数字必须非0.

+(数符) .545422(数字部分) 4(指数部分)

字符常量

在内存中占1个字节,如’a’,’%’,’D’等(只包含一个字符,用于区分大小写字符,’AB’就不是);字符常量会对大小写字母做区分,其中 ‘ 是定界符,不属于字符常量的一部分,例如cout<<'a'会输出字符a。

在C++中转义字符与C中的基本相同不过对于“\n”这样输出一个换行,不如直接用cout<<endl这样来换行,此类控制字符是对于输出设备透明的,如果我们想正常输出一个单双引号,直接在他们前面加一个反斜杠就好了。

\101:(反斜杠后的三位)以八进制形式的ASCII码对应的字符,因为(101)8=(65)10,那这样就会输出其对应的字符A;但注意\0是输出一个空字符的意思;

\x31:(\x后的两位)以十六进制形式对应的ASCII码对应的字符,因为(31)16=(49)10,查表可知会输出对应的字符0.

转义字符也代表一个字符,因此会在内存中占用一个字节;当我们将字符常量存放到内存单元时,实际上是将该字符相应的ASCII值放入存储单元,例如存入一个a,那么实际上是存放的ASCII码值97(不过是以2进制形式来表示的)。若对字符数据进行算术运算,其实际上也是对ASCII码做运算;其实我们也可以将一个ASCII码数值范围在0~255的字符数据用int来表示,不过会占用更大内存空间?(我瞎猜的),一般直接char a这样表示就好了。(因为大小写字母在表中差32,因此我们也可以这样做大小写转换)

字符串常量

用双引号括起来的字符序列,每个字符串会自带一个占1字节的字符串结束标志\0(但不会实际输出)。(例如“abc”在内存中占4个字节),因此在实际应用时需要注意“a”占2字节,但是‘a’占1字节(比如char c; c=”a”,就会报错,因为char只能容纳1字节)。

符号常量(宏常量)

为了方便表示,我们也可以使用一个符号名(一般用大写字符,放开头,因为通常是全局的)来代表一个常量,例:#define PRICE 25(后不加分号),这样在之后使用的时候就可以直接sum = num *PRICE,需要注意的是,如果我们已经在之前声明了这个符号,那么之后我们就不能再去对其进行赋值;
它不会像变量那样占用内存地址,只是在编译前把代码里所有宏名替换成对应值

使用符号常量好处在于含义清晰(不过尽量只对特殊的量去这样定义,太多符号常量挺乱的),方便修改(如果有多处用到的话,这样改一个地方就可以了)

[!NOTE]

诚如之前所说cout会默认对浮点数输出精度为6位,我们可以通过引入#include<iomanip>库来实现对其的精度控制,cout<<fixed<<setprecision(2)<<a<<endl;因为是从右往左输出;fixed是让a里的浮点数全部展开,之后通过setpresicion()来实现具体输出的位数控制。

变量

我们说变量其实就是一个盒子,用于存放数据,在输出时就把盒子里的数据给出去。

​ 对于变量名的命名规则有:不能是C++原本就有的关键字(比如int),大小写字母是被区分的,必须以字母/下划线开头,但是整体组成可以由字母/下划线/数字来组成。常用的命名方式主要有studentName(骆驼表示法)和iCount(匈牙利变量命名法,即加一个变量类型的表示)。

​ 对于变量我们要先定义才能用;对于同一类型的多个变量声明我们可以用逗号分隔开。(变量只有声明了确定类型才能在编译时被分配相应的存储单元),我们可以给变量在定义时直接赋予一个初始值,没被赋值的变量其输出结果不可预测,当然我们也可以cin>>a这样,在输入时给它赋值

常变量

​ 即在变量前加const这类变量的值在程序的运行期间是不能改变的,且在定义时就必须给一个初始值;不过常变量是不能再被赋值的,只能在对其定义的地方改;

​ 宏常量和常变量的区别就在于常变量有具体类型,且可在函数中去定义。

1
2
const int a;
a = 2; //不合法

运算符与表达式

C++的运算符种类很多可分为算术运算符、关系运算符和逻辑运算符等等

基本运算符

+
-
*
/
% 取余(常用于判断奇偶或取模运算)
1
2
3
4
5
6
/*在实际应用中需引用cmath库*/
#include<cmath>
/*用于取模的示例*/
int k,n=10;
k=(6+n)%7;
cout<<k;

在算术运算中,我们可以用括号来提高运算优先级,同时如果有一个数是float型或double型,那么结果就是double型;所谓运算的结合性就是说这个运算方向是从右到左(左侧有=号的)还是从左到右(左结合,一般的)

在不同数据类型进行运算时会进行类型转换(在表达式中程序会自动转换类型,也叫隐式类型转换):

  • char/short→int→unsigned→long→double

  • float→double

  • 若需要强制转换类型,即b=(int)a%3;(c语言形式)/b=int(a)%3;(C++类型)这样先将a的值转换为整型,再➗3取余。这样的强制转换不会改变原来变量的值和类型,只用于当前的运算。

  • 对于赋值运算符=其左侧必须是单个变量,用于表示将右边表达式的值赋给左侧变量。(当然赋值前也会自动进行类型转换以匹配,若要将double型转成float型需要注意数值范围的溢出风险,末尾加分号形成一条完整语句,不加分号不能单独使用)

    对于逻辑运算符:!(逻辑非),自增/自减运算符>算术运算符>关系运算符>&&(逻辑与)>||(逻辑或)>赋值运算符

    1
    2
    3
    int a = 'a';
    a += 8;
    cout<<a; //输出结果为105 因为'a'的ASCII码是97,加8后输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*这是一个C语言的示例*/
#include <math.h>
#include <stdbool.h>

2+3*5 //运算表达式
a=3*4 //赋值表达式
b=3/4 /*在运算中结果类型会同输入类型,因此要想此步结果不为0,则此步骤输入的计算数字也要换为浮点数类型*/
-a+1//语句 语句!=表达式,因为有;号
7%4 //%号,取余数,连接的两个数字必须为整型数据,±同符号前数字
a*=3 //等价于a=a*3
sart(9); /*计算9的平方根*/
log(x); //求的ln
fabs(x); //对浮点数取绝对值
abs(x); //对整数型取绝对值
pow(x,y); /*用于计算x的y次方,可以在官方库上面做实现*/

/*使用布尔类型时*/
'a'=='b'; //等于
'a'!='b'; //不等于
'a'>='b'; //大于等于

  • 自增运算i++可用于替换i=i+1语句来提高运算速度;

  • 自减运算i--同理(这些都属于后置,即先使用变量,之后自身再加减;前置就是先加减,再使用,比如--i)。

    ​ 但它俩只能用于变量而不能用于表达式或常量在运算时用右结合。

    逗号运算符

    C++特有的,即用逗号把两个表达式表达式1,表达式2;连接起来(求解顺序为从左到右),最终整个表达式的值是表达式2的结果,不过其在在所有运算符中优先级最低,因此a=4*5,a*6就先算a==20,再做20*6==120,然后输出。