C/C++ 算法基础

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++ 算法基础相关的知识,希望对你有一定的参考价值。

如果要真正掌握算法,必须要写代码的,那这时候就必须选择一门语言来进行,而具体的语言其实无所谓 ,C、C++、Java、Python,go甚至javascript、VB都可以,关键是自己要用的熟悉,面试时候能用,这里总结一下C和C++方面的基础。

由于我们不是语言课程,因此我们主要介绍与算法密切相关的技术问题。本文的重点是通过一些编程实例介绍程序设计中常用的思想方法和实现手段,不侧重介绍某种高级程序设计语言的语法细节。在这一章里,我们对将要使用的C/C++语言的相关内容做一个概要介绍。主要包括:变量、常量、表达式、赋值语句、分支语句、循环语句、数组、指针、函数等内容。

本书中的所有程序,都没有使用面向对象的编程方法。实际上,如果不涉及面向对象的部分,那么C++语言和C语言的语法90%以上是一样的,只不过略有扩充,用起来更为方便而已。因此,当提及的某项语法特性在C语言和C++语言中都适用时,我们就会说:“在C/C++语言中,......”

由于C/C++语言针对不同的平台有不同的特性,字段长度等在32位和64位机器上就不一样,本文大部分以32位为准。

目录

第一章C/C++语言概述

1.1 程序的基本框架

1.2变量

1.2.1变量的定义

1.2.2变量的赋值

1.2.3变量的引用

1.3 C/C++语言的数据类型

1.4 常量

1.5运算符和表达式

1.5.1算术运算符

1.5.2赋值运算符

1.5.3关系运算符

1.5.4逻辑运算符

1.5.5位运算符

1.6注释

1.7分支语句

1.7.1if语句

1.7.2 switch语句

1.8循环语句

1.8.1for语句

1.8.2while语句

1.8.3do-while语句

1.8.4break语句

1.8.5continue语句

1.9函数

1.9.1函数的定义

1.9.2函数的调用

1.9.3参数传递和返回值

1.9.4库函数和头文件

1.10标准输入输出

1.10.1printf函数(标准输出函数)

1.10.2 scanf函数(标准输入函数)

1.11全局变量和局部变量

1.12数组

1.12.1一维数组

1.12.2二维数组

1.12.3数组的初始化

1.12.4数组越界

1.13字符串

1.13.1字符串常量

1.13.2用字符数组存放的字符串

1.14指针

1.14.1指针的基本概念

1.14.2指针运算

1.14.3空指针

1.14.4指向指针的指针

1.14.5指针和数组

1.14.6字符串和指针

1.14.7void指针

1.14.8函数指针

1.14.9指针和动态内存分配

1.14.10误用无效指针

1.15结构

1.15.1“结构”的概念

1.15.2结构变量的定义

1.15.3访问结构变量的成员变量

1.15.4结构变量的初始化

1.15.5结构数组

1.15.6指向结构变量的指针

1.15.7动态分配结构变量和结构数组

1.16C语言标准库函数

1.16.1数学函数

1.16.2字符处理函数

1.16.3字符串和内存操作函数

1.16.4字符串转换函数


第一章C/C++语言概述

1.1 程序的基本框架

我们以简单程序Hello World为例说明程序的基本框架。此程序在屏幕上输出一行“Hello World!”:

#include <stdio.h>
int main()

   printf("Hello World!\\n");

这段程序包括二个部分:

  • 1.#include<stdio.h> #include是C语言的保留字,表示要把另一个文件中的内容包含在本文件中。<stdio.h>是被包含的文件的文件名。C语言中提供了一些可以被直接拿来使用、能够完成某些特定功能的库函数,分别声明于不同的头文件中。例如:stdio.h中定义了一些与输入输出有关的函数。printf就是一个能往屏幕上输出一串字符的库函数。

  • 2.后面的三行是程序的主函数。每个程序都必须包含这个main()函数。程序运行时,从void main()...的第一个语句开始执行。用户编写的程序的主要框架写在main函数里。

  • 3.printf("Hello World!\\n");这条语句的作用是在屏幕上输出一串字符“Hello Word!”然后换行。“\\n”的作用就是换行。换行后,如果以后再用printf语句来输出,那么输出的内容就会出现在屏幕的下一行。

1.2变量

变量是内存中的一块区域,在程序运行过程中可以修改这块区域中存放的数值。变量由两个要素构成:变量的名称和变量的类型。变量的名称是这个内存区域的唯一标识。变量的类型决定了这个内存区域的的大小、对所存储数值的类型要求。在程序中,有三种与变量有关的语句:变量的定义、变量的赋值和变量的引用。

1.2.1变量的定义

如下的语句定义了一个变量:

int number;

这里‘number’是变量名,‘int’代表该变量是整数类型的变量,‘;’表示定义语句结束。

在目前流行的机器配置下,整型变量一般占4个字节的内存空间。变量的名字是由编写程序的人确定的,它一般是一个单词或用下划线连接起来的一个词组,说明变量的用途。在C/C++语言中变量名是满足如下规定的一个符号序列:

  1. 由字母、数字或(和)下划线组成;

  2. 第一个符号为字母或下划线。注意,同一个字母的大写和小写是两个不同的符号。所以,team和TEAM是两个不同的变量名。

定义变量时,也可以给它指定一个初始值。例如:

int numberOfStudents=80;

对于没有指定初始值的变量,它里面的内容可能是任意一个数值。变量一定要先定义,然后才能使用。

1.2.2变量的赋值

给变量指定一个新值的过程称为变量的赋值,通过赋值语句完成。例如:

number=36; 表示把36写入变量number中。下面给出一些变量赋值语句的例子:

int temp;
int count;
temp=15;
count=temp;
count=count+1;
temp=count;

1.2.3变量的引用

变量里存储的数据可以参与表达式的运算,或赋值给其它变量。这一过程称为变量的引用。例如:

int total=0;
int p1=5000;
int p2=300;
int p3=1000;
int p4=1000;
total=p1+p2+p3+p4;

最后一个赋值语句表示把变量‘p1’,‘p2’,‘p3’和‘p4’的值取出来相加,得到的和赋给变量‘total’。最后一句执行后,‘total’的值变为7300。

1.3 C/C++语言的数据类型

前面我们介绍了变量的定义语句:

int nNumber;

此处的“int”表示了变量nNumber的“数据类型”,它说明nNumber是一个“整型变量”,即nNumber中存放的是一个整数。“数据类型”能够说明一个变量表示什么样的数据(整数,浮点数,还是字符等)。不同数据类型的变量,占用的存储空间大小不同。除了“int”以外,C/C++中还有其他一些基本数据类型,现列举其中几个如下:

  • int:整型。int型变量表示一个整数,其范围是-2^31~2^31-1,占用4个字节。

  • long:长整型。和int类型一样,也占用4个字节。

  • short:短整型。short型变量表示一个整数,但它占用2个字节,因而能表示的数的范围是-2^15~2^15-1。

  • unsigned int:无符号整型。unsignedint类型的变量表示一个非负整数,占用4个字节。

  • unsigned long:和unsigned int一样。

  • unsigned short:无符号短整型。unsigned short类型的变量表示一个非负整数,占用2个字节,能表示的数的范围是:0-2^16-1,本书中,我们将上面几种类型统称为“整数类型”。

  • char:字符型。char类型的变量表示一个字符,如’a’,’0’等。占用1个字节。字符型变量存放的实际上是字符的Ascii码。比如’a’的Ascii码是97,即16进制的0x61,那么如果有:charc=‘a’; 则实际上c中就存放着16进制数0x61,或二进制数01100001。 unsigned char:无符号字符型。unsigned char类型的变量表示一个字符,占用1个字节。

  • float:单精度浮点型。float类型的变量表示一个浮点数(实数),占用4个字节。

  • double:双精度浮点型。double类型的变量也表示一个浮点数,但它占用8个字节,因而精度比float类型高。

以上的“int”、“double”、“short”、”unsignedchar”等标识符,都是“类型名”。C++中的“类型名”可以由用户定义,后文“结构”一节会进一步阐述。在赋值语句中,如果等号左边的变量类型为T1,等号右边的变量或常量类型为T2,T1和T2不相同,那么编译器会将等号右边的变量或常量的值,自动转换为一个T1类型的值,再将此值赋给等号左边的变量。这个过程叫做“自动类型转换”。自动类型转换不会改变等号右边的变量。能进行自动类型转换的前提是,T1和T2是两个兼容的类型。上面提到的所有类型,正好都是两两互相兼容的。但是后面会碰到一些类型,比如指针类型,结构类型,它们和上述所有的类型都不兼容。如果等号左边是个整型变量,等号右边是个“结构类型”的变量,这样的赋值语句在编译的时候就会报错。

下面以一个程序来说明上述数据类型之间的自动转换:

#include <stdio.h>
 int main()
 
 int n1 = 1378;
 short n2;
 char c = 'a';
 double d1 = 7.809;
 double d2;
 n2 = c; //n2变为97
 printf("c=%c,n2=%d\\n", c, n2);
 c = n1; //c 变为 ’b’
 printf("c=%c,n1=%d\\n", c, n1);
 n1 = d1; //n1变为7
 printf("n1=%d\\n", n1);
 d2 = n1; //d2变为7
 printf("d2=%f", d2);
 return 0;

上面输出的结果是:

c=a,n2=97
c=b,n1=1378
n1=7
d2=7.000000

思考题:1.3.1:

假定char类型的变量c中存放着一个’w’之前的小写字母,请写一条赋值语句,使得c变为其后的第4个字母(比如,将c从’a’变成’e’)。解答见后。

提示:小写字母的Ascii码是连续的。

1.4 常量

常量是程序需要访问的一个数据,它在程序的运行过程中不发生改变。常量有两种表现形式:直接写出值,或用#define语句为数据定义一个由符号组成的标识符,标识符的命名规则与变量的命名规则相同。不同的数据类型有不同形式的常量。例如:123,-56,0,38,-1是整数类型的常量;1.5,23.6,0.0,-0.6789,100.456是浮点类型的常量;'a','p','0','¥','#'是字符类型的常量;“abc”,“definitely”,“1234”,“0.6”,“AE4%(Ap)”等是字符串类型的常量。这些都是直接给出数据值的常量,它们的类型可以很容易地从数据形式上判断。另一种用#define语句,为需要访问的数据指定一个容易理解的名字(标识符),例如:

#include<stdio.h>
#define MAPLENGTH 100
#define MAPWIDTHTH 80
int main()

int mapSize;
mapSize=MAPLENGTH*MAPWIDTHTH;
printf("Themapsize is%d\\n",mapSize);

这段代码中MAPLENGTH是一个整数类型的常量,它的值是100。在定义语句之后,

所有出现符号MAPLENGTH的地方,都等效于出现数值100。同样地,MAPWIDTH也是一个整数类型的常量,它的值是80。这段程序的运行结果是输出一个整数8000。

C/C++语言中,整数类型常量还可以有八进制、十六进制的写法。

八进制常量以数字“0”开头的,比如0123就是八进制的123。0987是不合法的常量,因为以0开头代表是八进制数,而八进制数中是不能出现数字8和9的。

十六进制常量以“0x”开头。比如,0x12就是16进制的12,换算成十进制就是18。0xfd0678、0xff44f都是合法的十六进制常量。十六进制表示法中,用a代表10、b代表11、c代表12、d代表13、e代表14、f代表15。这几个字母大、小写均可。由于16进制中的每一位正好对应于二进制的4位,因此,十六进制常量用起来十分方便,也非常有用。

有一些字符常量的写法比较特殊,比如单引号应写为''',“\\”应写为'\\'。

思考题:

什么样的常量在程序运行期间会象变量一样,需要用一片内存空间来存放,什么样的常量不需要?

1.5运算符和表达式

C/C++语言中的“+”、“-”“*”“/”等符号,表示加、减、乘、除等运算,这些表示数据运算的符号称为“运算符”。运算符所用到的操作数个数,称为运算符的“目数”。比如,“+”运算符需要两个操作数,因此它是双目运算符。

将变量、常量等用运算符连接在一起,就构成了“表达式”。如“n+5”、“4-3+1”。实际上,单个的变量、常量也可以称为“表达式”。表达式的计算结果称为“表达式的值”。如表达式“4-3+1”的值就是2,是整型的。如果f是一个浮点型变量,那么表达式“f”的值就是变量f的值,其类型是浮点型。

C/C++语言的运算符有赋值运算符、算术运算符、逻辑运算符、位运算符等多类。常用的介绍如下。

1.5.1算术运算符

算术运算符用于数值运算。包括加(+)、减(-)、乘(*)、除(/)、求余数(%)、自增(++)、自减(--)共七种。

1.5.1.1模运算符

求余数的运算符“%”也称为模运算符。它是双目运算符,两个操作数都是整数类型的。a%b的值就是a除以b的余数。

1.5.1.2除法运算符

C/C++的除法运算符有一些特殊之处,即如果a、b是两个整数类型的变量或者常量,那么a/b的值是a除以b的商。比如,表达式“5/2”的值是2,而不是2.5。请看下面的程序片断div_test.c:

#include<stdio.h>
int main()

int a=10;
int b=3;
double d=a/b; //①
printf("%f\\n",d);
d=5/2;//②
printf("%f\\n",d);
d=5/2.0;//③
printf("%f\\n",d);
d=(double)a/b;//④
printf("%f\\n",d);
return0;

上面程序的输出结果是:

3.000000
2.000000
2.500000
3.333333
  • 语句①中,由于a、b都是整型,所以表达式a/b的值也是整型,其值是3,因此d的值就变成3.0。

  • 语句②和语句①类似,执行后d的值变为2.0。

  • 语句③中,要求5除以2的精确值,为此要将5或者2表示成浮点数。除法运算中,如果有一个操作数是浮点数,那么结果就也会是较为精确的浮点数。因此表达式5/2.0的值是2.5。

  • 语句④求a除以b的较为精确的小数形式的值。“(double)”的是一个“强制类型转换运算符”,它是一个单目运算符,能将其右边的操作数强制转换成double类型。用此运算符先将a的值转换成一个浮点数值,然后再除以b,此时算出来的结果就是较为精确的浮点型的了。

1.5.1.3自增自减运算符

自增运算符“++”用于将整型或浮点型变量的值加1。只有一个操作数,是单目运算符。它有两种用法:

用法1: 变量名++; 用法2: ++变量名; 这两种用法都能使得变量的值加1,但它们是有区别的,请看例子:

#include <stdio.h>
main()

int n1,n2=5;
n2++;//n2=6
++n2;//n2=7
n1=n2++;//①
n1=++n2;//②

上面的①执行过程,是先将n2的值赋给n1,然后再增加n2的值,因此语句7执行后,n1的值是7,n2的值是8。也可以说,表达式“n2++”的值,就是n2加1以前的值;

语句②的执行过程,先将n2的值加1,然后再将n2的新值赋给n1。因此语句8执行后,n1的值是9,n2的值也是9。也可以说,表达式“++n2”的值,就是n2加1以后的值;

上面两个语句体现了“++”写在变量前面和后面所造成的不同。自减运算符“--”用于将整型或浮点型变量的值减1。它的用法和“++”相同,不再赘述。

1.5.2赋值运算符

赋值运算符用于对变量进行赋值,分为简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)和复合位运算赋值(&=,|=,^=,>>=,<<=)三类共十一种。

表达式“a=b”的值就是a,类型和a的类型一样。因此,可以写: int a,b; a=b=5; 上面这条语句先将b的值赋为5;然后求得b=5这个表达式的值5,再赋值给a。a+=b等效于a=a+b,但是前者执行速度比后者快。

-=、*=、/=、%=的用法和+=类似。

1.5.3关系运算符

关系运算符用于数值的大小比较。包括大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)和不等于(!=)六种。他们都是双目运算符。

关系运算符运算的结果是整型,值只有两种:0或非0。0代表关系不成立,非0代表关系成立。

比如表达式“3>5”,其值就是0,代表该关系成不成立,即运算结果为假;表达式“3==3”,其值就是非0,代表该关系成立,即运算结果为真。至于这个非0值到底是多少,C/C++语言没有规定,我们编程的时候也不需要关心这一点。C/C++语言中,总是用0代表“假”,用非0代表“真”,在后面章节会看到其用法。

请看下面的例子:

#include<stdio.h>
main()

int n1=4,n2=5,n3;
n3=n1>n2;//n3的值变为0
n3=n1<n2;//n3的值变为某非0值
n3=n1==4;//n3的值变为某非0值
n3=n1!=4;//n3的值变为0
n3=n1==5;//n3的值变为0

1.5.4逻辑运算符

逻辑运算符用于数值的逻辑操作。包括与(&&)、或(||)、非(!)三种。前二者是双目运算符,第三个是单目运算符。其运算规则如下:

当且仅当表达式exp1和表达式exp2的值都为真(非0)时,“exp1&&exp2”的值为真,其他情况,“exp1&&exp2”的值均为假。例如,如果n=4,那么“n>4&&n<5”的值就是假,“n>=2&&n<5”的值就是真。

当且仅当表达式exp1和表达式exp2的值都为假(就是0)时,“exp1||exp2”的值为假,其他情况,“exp1||exp2”的值均为真。例如,如果n=4,那么“n>4||n<5”的值就是真,“n<=2||n>5”的值就是假。

如果表达式exp的值为真,那么“!exp”的值就是假;如果exp的值为假,那么“!exp”的值就是真。比如,表达式“!(4<5)”的值就是假。

1.5.5位运算符

有时我们需要对某个整数类型变量中的某一位(bit)进行操作,比如,判断某一位是否为1,或只改变其中某一位,而保持其他位都不变。C/C++语言提供了“位运算”的操作,实现类似的操作。C/C++语言提供了六种位运算符来进行位运算操作:

& 按位与
| 按位或
^ 按位异或
~ 取反
<< 左移
>> 右移

位运算的操作数是整数类型(包括long、int、short、unsigned int等)或字符型的,位运算的结果是无符号整数类型的。

1.5.5.1按位与运算符

按位与运算符"&"是双目运算符。其功能是,将参与运算的两操作数各对应的二进制位进行与操作。只有对应的两个二进位均为1时,结果的对应二进制位才为1,否则为0。

例如:表达式“21&18”的计算结果是16(即二进制数10000),因为:

21用二进制表示就是:00000000000000000000000000010101 18用二进制表示就是:00000000000000000000000000010010 二者按位与所得结果是:00000000000000000000000000010000 按位与运算通常用来将某变量中的某些位清0或保留某些位不变。例如,如果需要将int型变量n的低8位全置成0,而其余位不变,则可以执行:

n=n&0xffffff00;

也可以写成:

n&=0xffffff00;

如果n是short类型的,则只需执行:

n&=0xff00;

如果要判断一个int型变量n的第7位(从右往左,从0开始数)是否是1,则只需看表达式“n&0x80”的值是否等于0x80即可。

1.5.5.2按位或运算符

按位或运算符"|"是双目运算符。其功能是将参与运算的两操作数各对应的二进制位进行或操作。只有对应的两个二进位都为0时,结果的对应二进制位才是0,否则为1。

例如:表达式“21|18”的值是23(即二进制数10111)。

按位或运算通常用来将变量中的某些位置1或保留某些位不变。例如,如果需要将int型变量n的低8位全置成1,而其余位不变,则可以执行:

n|=0xff;

1.5.5.3按位异或运算符

按位异或运算符"^"是双目运算符。其功能是将参与运算的两操作数各对应的二进制位进行异或操作。只有对应的两个二进位不相同时,结果的对应二进制位才是1,否则为0。

例如:表达式“21^18”的值是7(即二进制数111)。

异或运算的特点是:如果a^b=c,那么就有c^b==a以及c^a==b。此规律可以用来进行最简单的快速加密和解密。

思考:如何用异或运算对一串文字进行加密和解密?进一步,如果只使用一个字符做密钥,恐怕太容易被破解,如何改进?

1.5.5.4按位非运算符

按位非运算符"~"是单目运算符。其功能是将操作数中的二进制位0变成1,1变成0。例如,表达式“~21”的值是无符号整型数0xffffffea,下面的语句:

printf("%d,%u,%x",~21,~21,~21);

输出结果是:-22,4294967274,ffffffea

1.5.5.5左移运算符

左移运算符“<<”是双目运算符。其计算结果是将左操作数的各二进位全部左移若干位后得到的值,右操作数指明了要左移的位数。左移时,高位丢弃,左边低位补0。左移运算符不会改变左操作数的值。

例如,常数9有32位,其二进制表示是: 00000000000000000000000000001001

表达式“9<<4”就是将上面的二进制数左移4位,得到:

00000000000000000000000010010000

即为十进制的144。

实际上,左移1位,就等于是乘以2,左移n位,就等于是乘以2。而左移操作比乘法操作快得多。请看下面的例子程序:

#include<stdio.h>
main()

int n1=15;
short n2=15;
unsigned short n3=15;
unsigned char c=15;
n1<<=15;
n2<<=15;
n3<<=15;
c<<=6;
printf("n1=%x,n2=%d,n3=%d,c=%x,c<<4=%d",n1,n2,n3,c,c<<4);

上面程序的输出结果是:n1=78000,n2=-32768,n3=32768,c=c0,c<<4=3072

  • n1<<=15;一行是对n1左移15位。将32位的n1用二进制表示出来后,即可得知新的n1值是0x78000。

  • n2<<=15;将n2左移15位。注意,n2是short类型的,只有16位,表示为二进制就是0000000000001111,因此左移15位后,一共从左边移出去了(丢弃了)3个1,左移后n2中存放的的二进制数就是1000000000000000。由于n2是short类型,此时n2的最高位是1,因此n2实际上表示的是负数,所以在语句12中输出为-32768。

  • n3<<=15;将n3左移15位。左移后n3内存放的二进制数也是1000000000000000,但由于n3是无符号的,表示的值总是非负数,所以在语句12中,n3输出为32768。

  • c<<=6;将c左移6位。由于c是unsignedchar类型的,一共只有8位,其二进制表示就是00001111,因此左移6位后,就变为11000000,在语句12中以16进制输出为c0。语句12中,表达式“c<<4”的计算过程是首先将c转换成一个int类型的临时变量(32位,用16进制表示就是00000000000000c0),然后将该临时变量左移4位,得到的结果是十六进制的0000000000000c00,换算成十进制就是3072。

  • 表达式“c<<4”的求值过程不会改变c的值,就像表达式“c+4”的求值过程不会改变c的值一样。

1.5.5.6右移运算符

右移运算符“>>”是双目运算符。其计算结果是把“>>”的左操作数的各二进位全部右移若干位后得到的值,要移动的位数就是“>>”的右操作数。移出最右边的位被丢弃。

对于有符号数,如long、int、short、char类型变量,在右移时,符号位(即最高位)将一起移动,并且大多数C/C++编译器规定,如果原符号位为1,则右移时右边高位就补充1,原符号位为0,则右移时高位就补充0。

对于无符号数,如unsigned long、unsigned int、unsigned short、unsigned char类型的变量,右移时高位总是补0。

右移运算符不会改变左操作数的值。请看例子程序:

#include<stdio.h>
main()

int n1=15;
short n2=-15;
unsigned short n3=0xffe0;
unsigned char c=15;
n1=n1>>2;
n2>>=3;
n3>>=4;
c>>=3;
printf("n1=%x,n2=%d,n3=%x,c=%x",n1,n2,n3,c);

上面的程序输出结果是:n1=3,n2=-2,n3=ffe,c=1

  • n1=n1>>2;一行,n1的值是0xf,右移2位后,变成0x3。

  • n2>>=3;一行,n2是有符号16位整数,而且原来值为负数,表示成二进制是1111111111110001。由于最高位(符号位)是1,右移时仍然在高位补充1,所以右移完成后其二进制形式是1111111111111110,对于一个有符号16位整数来说,这个二进制形式就代表-2。

  • n3>>=4;一行,n3是无符号的16位整数,原来其值为0xffe0。尽管最高位是1,但由于它是无符号整数,所以右移时在高位补充0,因此右移4位后,n3的值变为0xffe。

  • c>>=3;一行,c是无符号的,原来值为0xf,右移动3位后自然就变成1。实际上,右移n位,就相当于左操作数除以2n,并且将结果往小里取整。

思考题

有两个int型的变量a和n(0<=n<=31),要求写一个表达式,使该表达式的值和a的第n位相同。

1.5.5.7sizeof运算符

“sizeof”是C/C++语言中的保留字,也是一个运算符。它的作用是求某一个变量占用内存的字节数,有两种用法:

第一种用法: sizeof(变量名) 比如,表达式sizeof(n)的值是n这个变量占用的内存字节数。如果n是short类型的变量,那么sizeof(n)的值就是2。第二种用法:

sizeof(类型名) 比如,sizeof(int)的值是4,因为一个int类型的变量占用4个字节。

1.5.5.8类型强制转换运算符

强制类型转换运算符的形式是: (类型名) 比如,(int)、(double)、(char)等,都是强制类型转换运算符。它是单目运算符,功能是

将其右边的操作数的值转换得到一个类型为“类型名”的值,它不改变操作数的值。比如:

1.doublef=9.14
2.intn=(int)f;
3.f=n/2;
4.f=(double)n/2;

上面的语句2将f的值9.14强制转换成一个int型的值,即转换成9,然后赋值给n。这条语句中是否使用(int)运算符结果都一样,因为编译器会自动转换。但是有时我们需要在类型不兼容的变量之间互相赋值,这时就需要在赋值时对等号右边的变量、常量或表达式进行强制类型转换,转换成和等号左边的变量类型相同的一个值。

上面的语句3执行后,f的值是4.0,因为表达式n/2的值是整型的,为4。

而语句4使用强制转换运算符(double)将n的值转换为一个浮点数,然后再除以2,那么得到的值就是一个浮点数。因此本语句执行后,f的值为4.5。2.5.1.2小节的例子程序的语句11,也说明了强制转换运算符的这种用法。

1.5.5.9运算符的优先级

一个表达式中可以有多个、多种运算符。不同的运算符优先级不同,优先级决定了表达式该先算哪部分、后算哪部分。

比如表达式4&2+5,由于“+”的优先级高于“&”,所以这个表达式是先算2+5,再算4&7,结果是4。

可以用括号来规定表达式的计算顺序,比如(4&2)+5的值是5,先算4&2。下表列出了大部分运算符的优先级:

1.6注释

有时我们会需要在程序中用自然语言写一段话,提醒自己或者告诉别人,某些变量代表什么,某段程序的逻辑是怎么回事,某几行代码的作用是什么,等等。当然,这部分内容不能被编译,不属于程序的一部分。这样的内容,称为“注释”。C++的注释有两种写法。第一种注释可以是多行的,以“/”开头,以“/”结尾。例如:

/**
*主函数
*/

注释可以出现在任何地方,注释里的内容不会被编译的,因此,随便写什么都行。例如可以为某一行代码加:

int n1=15;/*定义变量n1*/

第二种注释是单行的。写法是使用两个斜杠“//”。从“//”开始直到行末的内容,就都算是注释了。例如:

short n2=-15;//定义short类型变量n2

注释非常重要。它的主要功能是帮助理解程序。一定不要认为程序是自己写的,自己当然能理解。只要程序稍长一些、或者变量名不够直观,那么写时能理解,并不意味着一个星期后自己还能理解。更何况,软件开发是团队工作,没有人希望在看别人的程序的时候如读天书,恨不得自己重写一个。所以,在程序中加入足够的、清晰易懂的注释,是程序员的基本修养。

1.7分支语句

在C/C++语言中,语句以“;”结束。某些情况下,一组语句在一起共同完成某一特定的功能,可以将它们用大括号括起来。我们称之为语句组。语句组可以出现在任何单个语句出现的地方。一般情况下,语句的出现顺序就是其执行顺序。但是在某些情况下,需要根据不同的运行情况而执行不同的语句组。这时可以选用分支语句。我们介绍两种分支语句:if和switch。

1.7.1if语句

if语句有两种形式:

if(表达式)
语句/语句组

如果表达式的值为真(非零),则其后的语句/语句组被执行。如果表达式的值为假(等于零),则其后的语句/语句组被忽略。

if(表达式)
  语句/语句组1
else
  语句/语句组2

如果表达式的值为真(非零),则其后的语句/语句组1被执行,语句/语句组2被忽略。如果表达式的值为假(等于零),则其后的语句/语句组1被忽略,语句/语句组2被执行。

下面是一个if语句的例子:

if(i>0)
  y=x/i;
else
  x=i;
  y=-x;

在这个例子中,i,x,y是变量。如果i的值大于0,则y被赋值为x/i;如果i的值小于或等于0,则x被赋值为i,y被赋值为-x。当if语句后面只有一个语句时,可以不用大括号将其括起来。

if语句可以嵌套使用。在没有大括号来标识的情况下,else语句被解释成与它最近的if语句共同构成一句。例如:

if(i>0)/*没有大括号*/
if(j>i)
  x=j;
  else
  x=i;

如果上面的例子中else是与第一个if配对的,则应该写成如下格式:

if(i>0)/*加上括号*/
  if(j>i)
  x=j;
else
   x=i;

1.7.2 switch语句

switch和case语句用来控制比较复杂的条件分支操作。switch语句的语法如下:

switch(表达式)
  case常量表达式1:语句/语句组1
  case常量表达式2:语句/语句组2
  ...
  default:语句/语句组n

switch语句可以包含任意数目的case条件,但是不能有两个case后面的常量表达式完全相同。进入switch语句后,首先表达式的值被计算、并与case后面的常量表达式逐一匹配,当与某一条case分支的常量表达式匹配成功时,则开始执行它后面的语句/语句组,然后顺序执行之后的所有语句,直到遇见一个整个switch语句结束,或者遇到一个break语句(break语句后面会有介绍)。如果表达式与所有的常量表达式都不相同,则从default后面的语句开始执行到switch语句结束。

各case分支后的“常量表达式”必须是整数类型或字符型的。

如果各个case分支后面的语句/语句组彼此独立,即在执行完某个case后面的语句/语句组后,不需要顺序执行下面的语句,可以用break语句将这些分支完全隔开。在switch语句中,如果遇到break语句,则整个switch语句结束。例如:

switch(表达式)
case常量表达式1:语句/语句组1;break;
case常量表达式2:语句/语句组2;break;
...
default:语句/语句组n

default分支处理除了明确列出的所有常量表达式以外的情况。switch语句中只能有一个default分支,它不必只出现在最后,事实上它可以出现在任何case出现的地方。switch后面的表达式与case后面的常量表达式必须类型相同。象if语句一样,case语句也可以嵌套使用。

下面是一个switch语句的例子:

switch(c)
	case 'A':
		capa++;
	case 'a':
		lettera++;
	default:
		total++;

因为没有break语句,如果c的值等于'A',则switch语句中的全部三条语句都被执行;如果c的值等于'a',则lettera和total的值加1。如果c的值不等于'a'或'A',则只有total的值加1。下面是一个加入了break语句的例子:

switch(i)
  case -1:
    n++;
    break;
  case 0:
    z++;
    break;
  case 1:
    p++;
    break;

在这个例子中,每个分支都加入了一个break语句,使得每种情况处理完之后,就结束switch语句。如果i等于–1,只有n加1;如果i等于0,只有z加1;如果i等于1,只有p加1。最后一个break不是必须的,因为程序已经执行了最后,保留它只是为了形上的统一。

如果有多种情况要执行的任务相同,可以用如下的方式表达:

case'a':
case'b':
case'c':
case'd':
case'e':
case'f':
x++;

在这个例子中,无论表达式取值在'a'到'f'之间的哪个值,x的值都加1。

1.8循环语句

在有些程序中,需要反复执行某些语句。将n条相同的语句简单地复制会使程序变得不合理的冗长,因此高级语言中提供了支持程序重复执行某一段程序的循环控制语句。相关的语句有:for;while;dowhile;break;continue;等。

1.8.1for语句

for可以控制一个语句或语句组重复执行限定的次数。for的语句体可以执行零或多次,直到给定的条件不被满足。可以在for语句开始时设定初始条件,并在语句的每次循环中改变一些变量的值。for语句的语法如下:

for(初始条件表达式;循环控制表达式;循环操作表达式)语句/语句组执行一个for语句包括如下操作:

  • 初始条件表达式被分析执行。这个条件可以为空。

  • 循环控制表达式被分析执行。这一项也可以为空。循环控制表达式一定是一个数值表达式。在每次循环开始时,它的值都会被计算。计算结果有三种可能: 如果循环控制表达式为真(非零),语句/*语句组被执行;然后循环操作表达式被执行。循环操作表达式在每次循环结束时都会被执行。下面就是下一次循环开始,循环操作表达式被执行。

  • 如果循环控制表达式被省略,它的值定义为真。一个for循环语句如果没有循环控制表达式,它只有遇到break或return语句时才会结束。

  • 如果循环控制表达式为假(零),for循环结束,程序顺序执行它后面的语句。

break,goto,或return语句都可以结束for语句。continue语句可以把控制直接转移至for循环的循环控制表达式。当用break语句结束for循环时,循环控制表达式不再被执行。下面的语句经常被用来构造一个无限循环,只有break或return语句可以从这个循环中跳出来。

for(;;);

下面是一个for循环语句的例子:

for(i=n2=n3=0;i<=100;i++)
  if(i%2==0)
    n2++;
  else if(i%3==0)
    n3++;

这个例子计算从0到100的整数中,有多少个数是偶数(包括0在内),有多少个数是3的整数倍。最开始i、n2和n3被初始化成0。然后把i与100做比较,之后for内部的语句被执行。根据i的不同取值,n2被加1、或者n3被加1、或者两者都不加。然后i++被执行。接下来把i与100做比较,之后for内部的语句被执行。如此往复直到i的值大于100。

1.8.2while语句

while语句重复执行一个语句或语句组,直到某个特定的条件表达式的值为假。它的语

法表示如下: while(表达式)语句/语句组 式中的表达式必须是数值表达式。while语句执行过程如下:

  • 表达式被计算。

  • 如果表达式的值为假,while下面的语句被忽略,程序直接转到while后面的语句执行。 如果表达式的值为真(非零),语句/*语句组被执行。之后程序控制转向1。

下面是一个while语句的例子:

int i=100;
int sum=0;
while(i>0)
  sum=sum+i*i;
  i--;
 

上面的例子计算从1到100的平方和,结果保存在sum中。循环每次判断i是否大于0,如果i大于0,则进入循环,在sum上累加i的平方,将i的值减1,到此次循环结束。下一步重新判断i是否大于0。当某次判断i不大于0时,while语句结束。

1.8.3do-while语句

do-while语句重复执行一个语句或语句组,直到某个特定的条件表达式的值为假。下

面是它的语法表示:

do语句/语句组while(表达式);

do-while语句中,表达式是在语句/语句组被执行之后计算的。所以do后面的语句/语句组至少被执行一次。其中表达式必须是一个数值表达式。do-while语句的执行过程如下:

do后面的语句/*语句组被执行。

  • 表达式被计算。如果其值为假,则do-while语句结束,程序继续执行

它后面的语句。如果表达式的值为真(非零),跳转回1重复执行do-while语句。

do-while语句同样可以通过break,goto,或return语句结束。

下面是一个do-while的例子:

int i=100;
int sum=0;
do
  sum=sum+i*i;
  i--;
while(i>0);

这个do-while语句完成跟了上面的while相同的功能,即计算从1到100的平方和。前面两句定义了两个整型变量i和sum。在进入do-while语句后,i的平方被累加到sum中,之后i的值被减1。接下来判定i是否大于0,如果i大于0,则重复do后面的语句,否则do-while语句结束。

1.8.4break语句

break语句用来结束离它最近的do、for、switch、或while语句。

下面是一个break语句的例子:

for(i=0;i<10;i++) 
  for(j=1;j<=5;j++)
  if((i+j)%5==0)
    printf(“i=%dj=%d\\n”,i,j);
    break;
    
  

这个例子中,i从0循环到9,每次j从1循环到5,如果有某个j值使得i+j是5的整数倍,则输出i和j的值,并跳出j循环,开始下一轮的i循环。

1.8.5continue语句

在do、for、或while语句中,continue语句使得其后的语句被忽略,直接回到循环的顶部,开始下一轮的循环。continue语句的语法表示如下:

continue;

do、for、或while语句的下一轮循环用如下方法确定:

  • 对于do或while语句,下一轮循环从计算条件表达式的值开始。

  • 对于for语句,下一轮循环从计算第一个循环控制条件表达式的值开始。

下面是一个continue语句的例子:

int i=100;
int x=0;
int y=0;
while(i>0)
  i--;
  x=i%8;
if(x==1)
  continue;
  y=y+x;

这段程序计算i从99开始到0为止,累加除了8的倍数加1以外的所有数模8而得到的值。每次while循环开始,判断i的值是否大于0,如果i大于0,则进入循环体,先将i的值减1,然后将i模8的值赋给x,下面的if语句判断x是否等于1,如果x等于1,则回到while语句的开始,判断i是否大于0;如果x不等于1,则将x的值累加到y中。循环在x等于0时结束。

1.9函数

函数是C/C++语言中的一种程序组件单位。一个函数通常代表了一种数据处理的功能,由函数体和函数原型两部分组成。函数原型为这个数据处理功能指定一个标识符号(函数的名称)、说明被处理数据的组成及其类型、处理结果的类型。函数体由一组语句组成,具体实现数据处理的功能。这称为函数的定义。在某段程序中,一个函数可以被当作一个表达式来运行,称为函数的调用。函数的定义并不执行函数体中的语句,只是声明该函数包含这些语句、以及这些语句的运行顺序。函数在被调用之前,必须说明它的原型。被函数处理的数据一般作为函数的参数,在函数调用时确定它们的值。但是在函数体的语句中,可以直接访问函数的参数。函数运行后可以把它的结果返回给调用它的程序。

如果一个程序代码中需要多次实现同一种数据处理功能,通常将这个数据处理功能定义成一个函数,开发成一个单独程序组件。使得整个程序开起来更简洁。此外,当一个程序代码段实现的功能很复杂时,也常常将这个功能分解成若干个相对简单的子功能。每个子功能分别作为一个函数,用一个程序组件实现。

1.9.1函数的定义

函数的定义形式如下:

返回值类型函数名([参数1类型参数名*1*,参数2类型参数名*2*,......])
语句1;//语句可能与参数有关
语句2;//语句可能与参数有关
......
return返回值;//如果返回值类型为void,则不用返回语句

其中,返回值类型表示该函数如果被调用,它执行完之后向调用它的程序返回何种数据类型的值。函数名是程序员自己定义的、能够表明函数用途的标识符号,命名规则与变量的命名规则相同。参数是可选的,有些函数没有参数,有些可以有一至多个参数。每个参数都应说明其类型,以便调用它的程序可以填入正确的参数值。小括号和大括号是必须的。语句中可以把参数当作变量来使用。下面是一个函数定义的例子:

int add(int x,int y)
return x+y;

这个函数的函数名是add,它有两个参数分别是整数类型的x和整数类型的y;它的返回值类型也是整型,功能是计算两个整数的和,执行的结果是将计算出来的和返回给调用它的程序。两个参数x和y的值是调用它的函数给定的。

函数定义也可以分成两部分,即函数原型说明和函数体。函数原型说明必须在函数调用之前。函数体可以紧跟着函数原型说明,也可以放在程序中间的位置。例如:

int multiple(int x,int y);//函数说明
void main()
int a=0,b=0;
scanf(“%d%d”,&a,&b);
printf(“%d\\n”,multiple(a,b));//函数调用

int multiple(int x,int y)//函数体
return x*y;

1.9.2函数的调用

在一段程序中引用一个已经定义过的函数称为函数的调用。在调用函数时要给出每个参数的取值。如果函数有返回值,可以定义一个与返回值类型相同的变量,存储函数的返回值。下面是函数调用的例子:

int add(int x,int y)
returnx+y;

void main()
int n1=5,n2=6,n3;
n3=add(n1,n2);
printf(“%d\\n”,n3);

这段程序,调用函数add计算n1加n2的值,并将计算结果存入n3。最后输出n3的值。

这里要注意的是:如果函数的返回值是整型的,则函数调用表达式本身可以被看作是一个整数,它可以出现在任何整数可以出现的地方。其它类型的返回值也是一样。

有返回值的函数调用可以出现在表达式中,比如n3=add(n1,n2)+7;也是合法的语句。

1.9.3参数传递和返回值

函数调用可以看作在程序组件A的执行过程中,跳出A的代码段,转去执行另外一段代码B,等B执行完之后,再回到A中函数调用的位置,继续执行后面的语句。在函数调用的过程中,程序组件A可以通过参数向程序组件B传送信息;程序组件B结束后,可以通过返回值将其执行结果传回程序组件A。

1.9.3.1参数传递

参数作为数值传递给被调用的函数,在函数内部等同于内部变量。下面是一个例子:

int max(int a,int b)
 if(a>=b) return a;
 else return b;

​
void main()
int x=0,y=0,z=0;
x=20;
y=45;
int z=max(x,y);......

在主函数开始执行之前系统为它分配了空间存放变量x,y,z。第一条赋值语句结束后,x的值修改为20;第二条赋值语句结束后,y的值修改为45;执行到第三条赋值语句时,“=”号右边是函数调用,于是装入函数max的代码。max函数所在的程序段,系统为参数a,b分配了空间(注意:参数的名字是独立于调用它的程序的),并将调用时的参数值填入分配的空间。也就是说调用函数时,将数值45和20传给被调用的函数。这时main暂时停止执行,max开始执行,它执行的结果是将参数b的值45通过return语句返回给main。main接收到max返回的45,并且把它赋值给变量z,此时z变量的内容修改为45。程序继续执行。这里需要注意的是:在max函数中对a,b的任何操作不影响x,y的值。

1.9.3.2返回值

函数执行完以后可以向调用它的程序返回一个值,表明函数运行的状况。很多函数的功能就是对参数进行某种运算,之后通过函数返回值给出运算结果。函数的返回值可以有不同的类型,返回值类型在函数定义时说明。下面是一些函数定义的例子:

  • int min(intx,inty);//返回值类型为int,有两个整型参数,函数名为min 。

  • double calculate(int a,double b);//返回值类型为double,有一个整型参数,一个double型参数,函数名为calculate

  • char judge(void);//返回值类型为char,没有参数,函数名为judge。

  • void doit(inttimes);//返回值类型为void,表示不返回任何值,有一个整型参数,函数名为doit

1.9.4库函数和头文件

C/C++语言标准中&

以上是关于C/C++ 算法基础的主要内容,如果未能解决你的问题,请参考以下文章

C/C++ 算法基础

C/C++ 算法基础

C/C++编程基础算法总结

基础算法-c/c++

零基础进阶算法工程师之路:《算法笔记》学习:C/C++快速回顾

零基础进阶算法工程师之路:《算法笔记》学习:C/C++快速回顾