基础语法

这篇笔记我们介绍C++语言中的基础语法,我们将从一个简单的Hello World程序开始,逐步介绍代码的每个部分。

Hello World

前一章节我们已经编写了最基础的C++程序,这里我们再逐行回顾一下。

#include <iostream>

int main() {
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

代码中,第一行使用了#include指令引入iostream,学习C语言时我们已经了解过#include指令用于引入头文件,iostream是C++标准库中的一个头文件,其中包含了基本输入输出相关的函数。main是我们的入口函数,这个入口函数和C语言类似,它返回一个整型值代表程序的运行结果,返回0表示程序正常运行结束,不过和C语言不同的是,C++中不推荐在无参数函数的括号内标注void(尽管这样写也不报错)。

std::cout是C++标准库中提供的输出内容到标准输出(默认是控制台)的对象,确切的说std::cout是一个std::ostream类型的对象,std是标准库中使用的命名空间,C++中作用域解析运算符::用来指定标识符(变量、函数、类等)属于哪个命名空间、类、或者全局作用域。此外,这里的<<并不是左移运算符,它是一个被重载的运算符,用于向输出流对象中写入数据。运算符重载是C++中的一个高级特性,我们将在后续章节学习。至于std::endl,它封装了一个输出操作,用于输出一个换行符并刷新输出流的缓冲区,通常配合std::cout使用。

代码的最后,我们使用了return语句,主函数执行结束并返回了整数0,意味着程序正常结束。

标识符和关键字

标识符是用于命名变量、函数、类等程序实体的字符序列,我们编写代码时,标识符不是随意起名的,它通常需要遵循一定的规则:

  • 由字母、数字、下划线组成,但不能以数字开头
  • 区分大小写
  • 不能使用C++关键字
  • 建议起名具有一定的意义,不要全部用abc等命名

下面例子代码中,我们定义了3个变量,它们在C++代码中就是一种标识符。

int num1 = 1;
int num2 = 2;
int sum = num1 + num2;

C++保留了一些特殊关键字,我们命名标识符时不可以使用这些关键字。

asm           do            if            return        typedef
auto          double        inline        short         typeid
bool          dynamic_cast  int           signed        typename
break         else          long          sizeof        union
case          enum          mutable       static        unsigned
catch         explicit      namespace     static_cast   using
char          export        new           struct        virtual
class         extern        operator      switch        void
const         false         private       template      volatile
const_cast    float         protected     this          wchar_t
continue      for           public        throw         while
default       friend        register      true
delete        goto          reinterpret_cast try

作用域和命名空间

作用域是指变量或函数在程序中可见和可访问的范围。

局部作用域:在函数或代码块(即大括号{})内声明的变量具有局部作用域,局部作用域的变量就是我们平时说的局部变量。下面代码中,变量a具有函数foo()内的局部作用域。

void foo() {
    int a = 1;
}

块作用域:块作用域指的是变量在条件或循环语句包围的代码块(即大括号{})内有效,下面例子中i具有块作用域,在循环语句外i不能被访问。

for (int i = 1; i <= 10; i++) {
    std::cout << i << std::endl;
}

全局作用域:在所有函数外部声明的变量具有全局作用域,只要是在定义全局作用域变量的翻译单元中或者通过extern声明引入了它,在任何位置就都可访问全局变量。

int a = 1;
void foo() {
}

命名空间作用域:命名空间(namespace)是C++中用于防止标识符冲突的一种机制,这在大型项目或使用多个库时非常有用,C++标准库就使用了std命名空间,我们也可以定义自己的命名空间。下面例子代码中,我们定义了自己的命名空间demo,这样引命名空间内部的函数print()时就需要使用demo::print,这样如果在其它命名空间中也有名为print()的函数,由于位于不同的命名空间,它们也不会产生冲突(大型纯C语言项目中标识符冲突是个很常见且让人恼火的问题)。

#include <iostream>

namespace demo {
    void print() {
        std::cout << "Hello, world!" << std::endl;
    }
}

int main() {
    demo::print();
    return 0;
}

如果你多次使用demo::print觉得很烦,可以使用using语句简化这一写法,下面是一个例子。

#include <iostream>

namespace demo {
    void print() {
        std::cout << "Hello, world!" << std::endl;
    }
}

using demo::print;

int main() {
    print();
    return 0;
}

代码中,我们提前声明了demo::print,这意味着我们告诉编译器print这个标识符是demo命名空间下的,我们后续就可以省略的写作print()即可,不必再带着demo::了。

对于命名空间还有一种写法是using namespace,它告知编译器后面代码默认使用某个命名空间,使得我们可以省略该命名空间下的全部::操作符。例如using namespace std意味着我们默认使用std命名空间,这样使用coutendl等时全都可以省略std::了。

#include <iostream>

using namespace std;

int main() {
    cout << "Hello, world!" << endl;
    return 0;
}

大部分人都对using namespace的使用持反对意见,在大型项目中乱用using namespace这种写法很容易造成不易察觉的标识符冲突,个人建议using namespace的使用仅限于using namespace std,或者彻底忘记它,完全不要用这种写法。

注释

C++有单行注释和多行注释两种注释形式。

#include <iostream>

int main() {
    // 单行注释
    /*多行注释
     可以跨域多行*/
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

注意:多行注释不可以嵌套。

编译预处理

编译预处理用于在正式编译前执行的文本处理操作。C++中我们经常能碰到两种编译预处理指令,一种是引入头文件使用的#include

#include <iostream>

另一种是条件编译指令,包括#ifdef#ifndef#endif。条件编译指令有很多用途,例如根据编译参数决定是否开启调试代码、定义头文件时用于避免头文件重复包含等。

#ifndef STACK_H
#define STACK_H

#endif

宏定义

宏定义本质上是一种发生在编译阶段的文本替换,简单的宏定义可以用于定义“常量”,下面是一个例子。

#include <iostream>

#define PI 3.14159265

int main() {
    std::cout << PI << std::endl;
    return 0;
}

不过由于C++本身已经有常量相关的语法,宏定义常量就相对来说比较鸡肋了,宏定义没有类型检查和作用域支持,应谨慎使用。此外宏定义还可以带参数形成宏函数,但C/C++的宏编程非常难而且复杂的宏定义可读性极低,复杂的宏编程是有点类似“炫技”的高级技巧,它很容易导致项目代码可读性急剧下降并趋于不可维护,一般场景应该避免,具体可以参考C语言中相关章节。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap