3

C/C++ 札记:第一个 C++ 程序

 1 year ago
source link: http://kuanghy.github.io/2022/01/02/cpp-hello
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

C/C++ 札记:第一个 C++ 程序

C/C++ | Jan 2, 2022 | 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::cb::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
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK