C/C++ 札记:第一个 C++ 程序
C++ 是一种静态类型,编译型,强类型,通用的编程语言,支持面向过程和面向对象编程。C++ 是 C 的超集,是在 C 语言的基础上进一步扩充和完善,几乎任何合法的 C 程序都是合法的 C++ 程序。
第一个 C++ 程序:
/*
* 第一个 C++ 程序
*/
/* 引入头文件 */
#include <iostream>
/* 引入名字空间 */
using namespace std;
/* 程序的入口函数 */
int main(void)
{
// 定义变量,并赋值
string s = "Hello world!";
// 向标准流输出数据
cout << s << endl;
// 返回一个 int 类型的数据,用于表示程序的退出状态
return 0;
}
可将以上代码保存在 hello.cpp 中,以编译运行程序。C++ 程序文件的后缀名通常用 .cpp
、.cc
或者 .cxx
。
注释
注释是解释性语句,不影响程序的逻辑,但能提高源代码的可读性,注释中的所有字符会被编译器忽略。
C++ 支持单行注释和多行注释。多行注释以 /*
开始,以 */
结束。单行注释以 //
开始,直至行尾结束。多行注释也可以作为单行注释用。
/* 多行注释示例
第二行
第三行
*/
// 单行注释
/* 作为单行注释的多行注释 */
头文件
在编写 C++ 程序时,通常需要引入外部库(包括标准库和第三方库),这些外部库通常都以头文件和动态库或者静态库的新式提供。在 C++ 中,头文件也是一种源代码的形式,但头文件不用被编译。头文件主要提供全局变量、全局函数的声明或公用数据类型的定义,其作用主要是实现分离编译和代码复用。当需要外部的某些声明或者定义时,就可以将相应的头文件引入。引入头文件使用 #include
宏命令,宏会在编译器预处理阶段进行展开,#include
的作用就是把指定的文件的内容插入其所在位置处。
C++ 标准库的头文件通常不带 .h
后缀,这主要是为了与旧版或者 C 语言的头文件做区分,C++ 标准库的头文件通常可以在 /usr/include/c++ 中找到。
如 iostream
便是标准库中的头文件,其申明了 std::cout 和 std::endl 等变量。std::cout 表示输出到标准输出,std:endl 表示换行。
命名空间
命名空间(namespace)
主要用于区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。命名空间的声明使用 namespace
关键字,如:
namespace a {
int c;
}
namespace b {
int c;
}
int c;
使用名字空间需要使用 ::
域解析符,也叫做作用域操作符。如 a::c
,b::c
,对于没有声明名字空间的全局空间,称为无名名字空间,访问时不需要指定域解析符,也可以用类似 ::c
的方式访问。
此外,还可以使用 using
关键字来引入名字空间。使用 using 时,可以引入名字空间中的单个标识符,也可以全部引入。如:
// 仅引入名字空间 std 中的 string, cout, endl
using std::string;
using std::cout;
using std::endl;
// 引入名字空间 std 中的所有内容
using namespace std;
这样声明之后,就如示例程序中一样,string、 cout、 endl 前面的 std:: 就可以省略不写了。但需要注意的是,虽然可以省略不写,但 string 变量的全名仍然是 std::string,如果有一个全局变量也叫 string,其与 std::string 应该是两个不同的变量。所以,为了区分,全局变量的 string 可以写作 ::string(如果 string 是函数,::string 引用的是无名名字空间中的 string,而如果是变量,则有无 :: 都为冲突)。
名字空间还可以嵌套,如:
namespace a {
namespace b {
int c = 1;
}
}
int d = a::b::c;
分号
在 C++ 中,分号(;) 是一个语句结束符。每个语句必须以分号结束。也表明一个逻辑实体的结束。
在有些编程语言中,会以 换行符(\n) 作为结束符的标识。由于 c++ 不以换行符结束,所以可以在一行内放置多个语句,但是为了便于阅读,除非是某些特殊情况,通常都只在一行实现一个语句。
x = y; y = y + 1; // 合法的语句
代码块
代码块是一组使用 大括号({}) 括起来的按物理逻辑连接的语句,也称为复合语句(compound statement)。一个代码块也标记着一个变量作用域。即在一个代码块中定义的变量,只在该代码块中有效。
{
std::string s = "Hello world";
}
cout << s << endl; // 此处的 s 未定义,会报错
程序入口
main()
函数是 C/C++ 程序的入口函数,所谓入口函数,是指程序从这里开始。实际上一个 C/C++ 程序也只执行 main 函数里的逻辑。一个 C/C++ 程序可以包含若干函数,但至少必须包含 main 函数。C/C++ 标准规定 main 函数的返回值类型为 int,返回值用于表示程序的退出状态,返回 0 表示程序正常退出,返回非 0,表示出现异常。C/C++ 标准规定,main 函数原型有两种:
/* 无参数形式 */
int main(void)
{
...
return 0;
}
/* 带参数形式 */
int main(int argc, char *argv[])
{
...
return 0;
}
int
指明了 main() 函数的返回值类型,函数名后面的圆括号一般包含传递给函数的信息。void
表示没有给函数传递参数。
函数
函数是执行某种操作的代码块。函数的定义需提供函数名称、返回值类型、参数列表,以及函数主体。函数定义的一般形式如下:
return_type function_name(parameter list)
{
body of the function
}
- 返回值类型: 定义函数是必须指定返回值类型,如 int、float、string 等。如果函数不需要返回值,则可以将返回值指定为 void,即表明函数不返回任何值
- 函数名称: 为函数指定一个标识符,以便于后续用该名称使用函数。函数名和参数列表一起构成了函数签名
- 参数: 参数就像是占位符。当函数被调用时,向参数传递一个值,这个值被称为实际参数。函数定义时指定的参数列表被称为形式参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可以不包含参数
- 函数主体: 函数主体包含一组定义函数执行任务的语句,即描述函数要完成的功能
一个好的编程习惯是,将不同的逻辑任务划分到不同的函数中,依次来更好的组织,是代码逻辑清晰,更易读。
变量与类型
示例中的 string s = "hello world"; 表示定义 string 类型的变量,变量名为 s,值为 hello world。变量是标记内存区域的一个名称。定义变量 s 时,"hello world" 文件字符串被存放在内存中,s 则标记了这块内存的位置,访问 s 时则可以取出这块内存的内容。变量 s 是一个标识符,用于标记变量。
C++ 标识符 是用于标识变量,函数,类,模块或任何其他用户定义项的名称。标识符以字母 A 到 Z 或 a 到 z 或下划线 _
开头,后跟零个或多个字母,下划线和数字(0到9)。C++ 对大小写敏感,即 A 和 a 是两个不同的标识符。
C++ 是强类型的语言,所以定义变量是必须指定变量的类型,类型决定了变量存储的大小和布局。C++ 中常见的变量类型有 char, bool, int, float, double 等。
int i = 1;
float f = 0.1;
double d = 0.01;
布尔类型(bool)相较于 C 语言是 C++ 中新增的类型,布尔值包括 true 或 false。如果布尔变量用于算术表达式中,将被隐式转换成 int 类型,true 为 1,false 为 0。
bool cond;
cond = true;
cout << cond << endl; // output 1
此外,C++ 还包括 枚举、指针、数组、引用、数据结构、类等类型。
标准流
示例中的 cout << s << endl; 表示将变量 s 的内容输出到标准输出流中。标准流是一种 输入/输出功能,包括标准输入流、标准输出流和标准错误流。标准输入流连接输入设备,如键盘;标准输出流和标准错误流连接输出设备,如显示器、打印机等。
C++ 在 iostream
头文件中定义了 cin、cout、cerr 和 clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。
<<
本来是移位运算符,在这里被 overload 成了输出函数。
示例类似如下一样 C 语言程序:
printf("Hello world!\n");
编译运行
编译 C++ 程序需要使用 g++ 编译器。最简单的编译方式:
g++ hello.cpp
编译时编译器默认将编译结果输出到 a.out 可执行文件中,可以直接运行该文件:
$ ./a.out
Hello world!
也使用 -o 选项指定输出的可执行程序的文件名。如:
g++ hello.cpp -o hello
如果有多个源文件,可以用如下的方式编译:
g++ test1.cpp test2.cpp -o test
在 unix 系统环境下,对于一些简单的测试代码,可以编写一个脚本来实现一键编译运行,如:
echo "========= Compile:"
if [[ "$*" =~ ".cpp" ]]; then
CC="g++ -Wall -std=c++11"
if [[ `uname -s` == "Darwin" ]]; then
CC="${CC} -stdlib=libc++"
fi
else
CC="gcc -Wall"
fi
TMPDIR=$(dirname $(mktemp -u))
OUTFILE="${TMPDIR}/cplus-tmp-out"
if [[ -f $OUTFILE ]]; then
/bin/rm -f $OUTFILE
fi
$CC "$@" -o $OUTFILE
if [[ $? == 0 ]]; then
echo "========= Run:"
$OUTFILE
fi
if [[ -f $OUTFILE ]]; then
/bin/rm -f $OUTFILE
fi
如果程序比较简单,也可以尝试使用 cling,其是一个交互式的 C++ 解析器,基于 LLVM 和 C++ 的前端 clang。可以直接运行 cling 来测试简单的语法逻辑,如:
$ cling
****************** CLING ******************
* Type C++ code and press enter to run it *
* Type .q to exit *
*******************************************
[cling]$ int a = 1;
[cling]$ a
(int) 1
[cling]$ int b = 2;
[cling]$ b
(int) 2
[cling]$ a + b
(int) 3
也可以用 cling 直接运行一个文件:
$ cat hello.cpp | cling --nologo
Hello World
可以编写一个 shell function 来自动的判断是需要直接运行解释器,还是需要运行一个文件:
function kling() {
if [ -f $1 ]; then
cat $1 | cling --nologo
else
cling $*
fi
}
参考资料
本文使用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,转载请注明出处