C++简明教程(一)

本文内容来自菜鸟教程, C++教程,该篇内容仅作为笔记使用

静态类型编程语言

  • 编译时执行类型检查,而不是运行时执行类型检查(Java)
  • 大小写敏感

面向对象

  • 封装
  • 抽象
  • 继承
  • 多态

标准库

  • 核心语言,提供所有构建块,包括变量、数据类型和常量等
  • C++标准库,提供大量函数,用于操作文件、字符串等
  • 标准模板库(STL), 提供大量方法, 用于操作数据结构

环境配置

  • Linux
// 检查系统上是否安装了GCC
$g++ -v
  • Windows
1. 安装MinGW
2. 添加MinGW的 bin 子目录到PATH 环境变量中
3. 使用Visual Studio编译
4. gcc 编译链接C++程序
$ gcc main.cpp -lstdc++ -o main
  • Mac OS

数据类型

  • 基本类型

    • int
  • typedef 声明

typedef type newname; // 定义一个已有类型的别名
typedef int feet; // feet是int的另一个名称
feet distance; // 声明一个int类型变量distance
  • 枚举类型

    枚举类型(enumeration)是C++中的一种派生数据类型

enmu 枚举名 {
    标志符[=整形常数],
    标志符[=整形常数],
    ...
    标志符[=整形常数]
} 枚举变量;

例如:
enmu color { red, green, blue} c; // red = 0; green = 1; blue = 2;
c = blue;

enmu color {red, green = 5, blue}; // blue = 6, 但red = 0

变量

  • 变量定义
type varible_list; // 变量定义
type variable_name = value; // 定义时初始化
例:
int i, j, k;
extern int d = 3, f = 5; // d 和 f的声明
int d = 3, f = 5; // 定义并初始化 d 和 f
  • 变量声明

    • 变量声明向编译器保证变量以给定的类型和名称存在, 这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。
    • 变量声明只在编译时有它的意义, 在程序链接时编译器需要实际的变量声明。
    • 当使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的), 变量声明就显得非常有用。
    • 可以使用 extern 关键字在任何地方声明一个变量。
    • 可以多次声明一个变量, 但变量只能被定义一次。
#include 
using namespace std;
 
// 变量声明, 变量在头部被声明, 但是在主函数中被定义和初始化的
extern int a, b;
extern int c;
extern float f;
  
int main ()
{
  // 变量定义
  int a, b;
  int c;
  float f;
 
  // 实际初始化
  a = 10;
  b = 20;
  c = a + b;
 
  cout << c << endl ; // 输出c到屏幕显示器, 回车换行
 
  f = 70.0/3.0;
  cout << f << endl ;
 
  return 0;
}
  • 函数声明
// 函数声明, 提供一个函数名, 函数的实际定义可以在任何地方进行
int func();
 
int main()
{
    int i = func(); // 函数调用
}
 
// 函数定义
int func()
{
    return 0;
}
  • 左值(Lvalues)和右值(Rvalues)
    • 左值(lvalue):指向内存位置的表达式。可出现在赋值号(=)的左边或右边
    • 右值(rvalue):存储在内存中某些地址的数值。 不能出现在赋值号=的左边

变量作用域

  • 局部变量: 在函数或代码块内部定义的变量, 仅在函数或块内有效
  • 全局变量: 在函数外部定义的变量, 在程序的整个声明周期内都有效
#include 
using namespace std;
 
// 全局变量定义, 系统默认初始化
int g;
 
int main ()
{
  // 局部变量定义, 系统不会对局部变量初始化, 必须手动初始化
  int a, b;
 
  // 实际初始化
  a = 10;
  b = 20;
  g = a + b;
 
  cout << g;
 
  return 0;
}

常量

  • 整数常量
85         // 十进制
0213       // 八进制 前缀 0 表示八进制
0x4b       // 十六进制 前缀 0x或0X 表示十六进制
    
30         // 整数 
30u        // 无符号整数 后缀 U/u 表示无符号(unsigned)
30l        // 长整数 后缀 L/l 表示长整数(long)
30ul       // 无符号长整数, 顺序无所谓
  • 浮点常量

    • 浮点常量由整数部分、小数点、小数部分和指数部分组成

    • 也可以使用小数形式或者指数形式来表示浮点常量

    • 小数形式表示时,必须包含整数部分、小数部分,或同时包含两者

    • 当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者

    • 带符号的指数是用 e 或 E 引入的

3.14159       // 合法的 
314159E-5L    // 合法的 
    
510E          // 非法的:不完整的指数
210f          // 非法的:没有小数或指数
.e55          // 非法的:缺少整数或分数
  • 布尔常量

    • true
    • false
  • 字符常量

    • 字符常量是括在单引号中
    • 如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L'x'),此时它必须存储在 wchar_t 类型的变量中。否则,它就是一个窄字符常量(例如 'x'),此时它可以存储在 char 类型的简单变量中。
    • 字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')
  • 字符串常量

定义常量

  • #define 预处理器
#define identifier value // 使用 #define 预处理器定义常量的形式

#define LENGTH 10   
#define WIDTH  5
#define NEWLINE '\n'
  • const 关键字
const type variable = value; // 使用 const 前缀声明指定类型的常量
const int  LENGTH = 10;

修饰符类型

C++ 允许在 char、int 和 double 数据类型前放置修饰符。修饰符用于改变基本类型的含义,所以它更能满足各种情境的需求。

  • signed
  • unsigned
  • long
  • short

类型限定符

类型限定符提供了变量的额外信息

限定符 含义
const const 类型的对象在程序执行期间不能被修改改变。
volatile 修饰符 volatile 告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率。
restrict restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。

C++ 存储类

存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。

下面列出 C++ 程序中可用的存储类:

- auto
- register
- static
- extern
- mutable
- thread_local (C++11)

从 C++ 11 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。

auto 存储类

自 C++ 11 以来,auto 关键字用于两种情况:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。

C++98标准中auto关键字用于自动变量的声明,但由于使用极少且多余,在C++11中已删除这一用法。

根据初始化表达式自动推断被声明的变量的类型,如:

auto f=3.14;      //double
auto s("hello");  //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型

register 存储类

register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。

{
   register int  miles;
}

寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 'register' 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

static 存储类

static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,

static 修饰局部变量可以在函数调用之间保持局部变量的值。

tatic 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。

#include 
 
// 函数声明 
void func(void);
 
static int count = 10; /* 全局变量 */
 
int main()
{
    while(count--)
    {
       func();
    }
    return 0;
}
// 函数定义
void func( void )
{
    static int i = 5; // 局部静态变量
    i++;
    std::cout << "变量 i 为 " << i ;
    std::cout << " , 变量 count 为 " << count << std::endl;
}

extern 存储类

extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 'extern' 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。

extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下所示:

// main.cpp
#include 
 
int count ;
extern void write_extern(); // 
 
int main()
{
   count = 5;
   write_extern();
}
// support.cpp
#include 
 
extern int count; // 
 
void write_extern(void)
{
   std::cout << "Count is " << count << std::endl;
}

mutable 存储类

mutable 说明符仅适用于类的对象,这将在本教程的最后进行讲解。它允许对象的成员替代常量。也就是说,mutable 成员可以通过 const 成员函数修改。

thread_local 存储类

使用 thread_local 声明的变量仅可在创建该变量的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。

thread_local 说明符可以与 static 或 extern 合并。

可以将 thread_local 仅应用于数据声明和定义,thread_local 不能用于函数声明或定义。

以下演示了可以被声明为 thread_local 的变量:

thread_local int x;  // 命名空间下的全局变量
class X
{
    static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string X::s;  // X::s 是需要定义的
 
void foo()
{
    thread_local std::vector v;  // 本地变量
}

运算符

算数运算符

逻辑元算符

位运算符

假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:

A = 0011 1100
B = 0000 1101

A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A  = 1100 0011
运算符 描述 实例
& 与(AND) (A & B) 将得到 12,即为 0000 1100
| 或(OR) (A | B) 将得到 61,即为 0011 1101
^ 异或() (A ^ B) 将得到 49,即为 0011 0001
~ 补码 (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
<< 二进制左移运算符。 A << 2 将得到 240,即为 1111 0000
>> 二进制右移运算符。 A >> 2 将得到 15,即为 0000 1111

赋值运算符

其他运算符

运算符\\\\\\\\\\\\\\\\\\\\\\ 描述
sizeof sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。
Condition ? X : Y 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
, 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
.-> 成员运算符用于引用类、结构和共用体的成员。
Cast 强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。
& 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
* 指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。

循环

循环类型

  • while
  • for
  • do...while
  • 嵌套循环

循环控制语句

  • break
  • continue
  • goto

无线循环

for( ; ; )

函数

定义函数

return_type function_name( parameter list ) // 返回类型 函数名 参数
{
   body of the function // 函数体
}

函数声明

函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。

return_type function_name( parameter list );
// 例:
int max(int num1, int num2);
// 在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:
int max(int, int);

当在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,应该在调用函数的文件顶部声明函数。

函数参数

调用类型 描述
传值调用 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。
指针调用 该方法把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
引用调用 该方法把参数的引用复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

默认情况下,C++ 使用传值调用来传递参数。一般来说,这意味着函数内的代码不能改变用于调用函数的参数。之前提到的实例,调用 max() 函数时,使用了相同的方法。

参数默认值

int sum(int a, int b=20) // 参数默认值
{
  int result;
  result = a + b;
  return (result);
}

Lambda函数与表达式

C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。

Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。

Lambda 表达式本质上与函数声明非常类似。Lambda 表达式具体形式如下:

[capture](parameters)->return-type{body}
// 例如:
[](int x, int y){ return x < y ; }

如果没有返回值可以表示为:

[capture](parameters){body}
// 例如
[]{ ++global_x; } 

在一个更为复杂的例子中,返回类型可以被明确的指定如下:

[](int x, int y) -> int { int z = x + y; return z + x; }

本例中,一个临时的参数 z 被创建用来存储中间结果。如同一般的函数,z 的值不会保留到下一次该不具名函数再次被调用时。

如果 lambda 函数没有传回值(例如 void),其返回类型可被完全忽略。

在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:

[]      // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&]     // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=]     // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x]  // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

另外有一点需要注意。对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入:

[this]() { this->someFunc(); }();

C++数字

数学运算

double cos(double); // 返回弧度角(double 型)的余弦。
double sin(double); // 返回弧度角(double 型)的正弦。
double log(double); // 返回参数的自然对数。
double pow(double, double); // 返回 x 的 y 次方。
double hypot(double, double); // 返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。
double sqrt(double); // 返回参数的平方根。
int abs(int); // 返回整数的绝对值。
double fabs(double); // 该函数返回任意一个浮点数的绝对值。
double floor(double); // 返回一个小于或等于传入参数的最大整数。

随机数

关于随机数生成器,有两个相关的函数。一个是 rand(),该函数只返回一个伪随机数。

生成随机数之前必须先调用 srand() 函数。

下面是一个关于生成随机数的简单实例。实例中使用了 time() 函数来获取系统时间的秒数,通过调用 rand() 函数来生成随机数:

#include 
#include 
#include 
 
using namespace std;
 
int main ()
{
   int i,j;
 
   // 设置种子
   srand( (unsigned)time( NULL ) );
 
   /* 生成 10 个随机数 */
   for( i = 0; i < 10; i++ )
   {
      // 生成实际的随机数
      j= rand();
      cout <<"随机数: " << j << endl;
   }
 
   return 0;
}

数组

声明数组

type arrayName [ arraySize ]; // arraySize 必须是一个大于零的整数常量,type 可以是任意有效的 C++ 数据类型。
// 例:
double balance[10];

初始化数组

double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};

二维数组

多维数组最简单的形式是二维数组。一个二维数组,在本质上,是一个一维数组的列表。

声明一个 x 行 y 列的二维整型数组,形式如下:

type arrayName [ x ][ y ];

type 可以是任意有效的 C++ 数据类型,arrayName 是一个有效的 C++ 标识符。

指向数组的指针

数组名是一个指向数组中第一个元素的常量指针。因此,在下面的声明中:

double balance[50];

balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。因此,下面的程序片段把 p 赋值为 balance 的第一个元素的地址:

double *p;
double balance[10];

p = balance;

使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。

一旦您把第一个元素的地址存储在 p 中,您就可以使用 *p、*(p+1)、*(p+2) 等来访问数组元素。

传递数组给函数

C++ 中您可以通过指定不带索引的数组名来传递一个指向数组的指针。

C++ 传数组给一个函数,数组类型自动转换为指针类型,因而传的实际是地址。

如果您想要在函数中传递一个一维数组作为参数,您必须以下面三种方式来声明函数形式参数,这三种声明方式的结果是一样的,因为每种方式都会告诉编译器将要接收一个整型指针。同样地,您也可以传递一个多维数组作为形式参数。

  • 形式参数是一个指针:

    void myFunction(int *param)
    {
    }
    
  • 形式参数是一个已定义大小的数组:

    void myFunction(int param[10])
    {
    }
    
  • 形式参数是一个未定义大小的数组:

    void myFunction(int param[])
    {
    }
    

从函数返回数组

C++ 不允许返回一个完整的数组作为函数的参数。但是,可以通过指定不带索引的数组名来返回一个指向数组的指针。

如果您想要从函数返回一个一维数组,您必须声明一个返回指针的函数,如下:

int * myFunction()
{
}

另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。

字符串

C 风格字符串

C 风格的字符串起源于 C 语言,并在 C++ 中继续得到支持。字符串实际上是使用 null 字符 '\0' 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。

下面的声明和初始化创建了一个 "Hello" 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 "Hello" 的字符数多一个。

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

C++ 中有大量的函数用来操作以 null 结尾的字符串:supports a wide range of functions that manipulate null-terminated strings:

序号 函数 & 目的
1 strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
2 strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。
3 strlen(s1); 返回字符串 s1 的长度。
4 strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1s2 则返回值大于 0。
5 strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
6 strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。

C++ 引入的 string 类类型

C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。我们将学习 C++ 标准库中的这个类,现在让我们先来看看下面这个实例:

#include 
#include 
 
using namespace std;
 
int main ()
{
   string str1 = "Hello";
   string str2 = "World";
   string str3;
   int  len ;
 
   // 复制 str1 到 str3
   str3 = str1;
   cout << "str3 : " << str3 << endl;
 
   // 连接 str1 和 str2
   str3 = str1 + str2;
   cout << "str1 + str2 : " << str3 << endl;
 
   // 连接后,str3 的总长度
   len = str3.size();
   cout << "str3.size() :  " << len << endl;
 
   return 0;
}

指针

指针基础

学习 C++ 的指针既简单又有趣。通过指针,可以简化一些 C++ 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C++ 程序员,学习指针是很有必要的。

正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。

int  var1;
 
cout << &var1 << endl; // 访问内存地址

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var-name;

在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */

所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。

不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

int  var = 20;   // 实际变量的声明
int  *ip;        // 指针变量的声明
 
ip = &var;       // 在指针变量中存储 var 的地址

指针详解

概念 描述
C++ Null 指针 C++ 支持空指针。NULL 指针是一个定义在标准库中的值为零的常量。
C++ 指针的算术运算 可以对指针进行四种算术运算:++、--、+、-
C++ 指针 vs 数组 指针和数组之间有着密切的关系。
C++ 指针数组 可以定义用来存储指针的数组。
C++ 指向指针的指针 C++ 允许指向指针的指针。
C++ 传递指针给函数 通过引用或地址传递参数,使传递的参数在调用函数中被改变。
C++ 从函数返回指针 C++ 允许函数返回指针到局部变量、静态变量和动态内存分配。

Null指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:

#include 

using namespace std;

int main ()
{
   int  *ptr = NULL;
   cout << "ptr 的值是 " << ptr ;  // 0
   return 0;
}

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

如需检查一个空指针,您可以使用 if 语句,如下所示:

if(ptr)     /* 如果 ptr 非空,则完成 */
if(!ptr)    /* 如果 ptr 为空,则完成 */

因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。

指针的算数运算

指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

  • 指针的递增

假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。

这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。

如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

  • 指针的递减
ptr--

同样地,对指针进行递减运算,即把值减去其数据类型的字节数

  • 指针的比较
#include 
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指针中第一个元素的地址
   ptr = var;
   int i = 0;
   while ( ptr <= &var[MAX - 1] ) // 指针的比较
   {
      cout << "Address of var[" << i << "] = ";
      cout << ptr << endl;
 
      cout << "Value of var[" << i << "] = ";
      cout << *ptr << endl;
 
      // 指向上一个位置
      ptr++;
      i++;
   }
   return 0;
}

指针和数组

指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的。例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。请看下面的程序:

#include 
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指针中的数组地址
   ptr = var;
   for (int i = 0; i < MAX; i++)
   {
      ptr++;
   }
   return 0;
}

然而,指针和数组并不是完全互换的。例如,请看下面的程序:

#include 
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
 
   for (int i = 0; i < MAX; i++)
   {
      *var = i;    // 这是正确的语法
      var++;       // 这是不正确的
   }
   return 0;
}

把指针运算符 * 应用到 var 上是完全可以的,但修改 var 的值是非法的。这是因为 var 是一个指向数组开头的常量,不能作为左值。

指向指针的指针

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。

当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

image

一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:

int **var;

传递指针给函数

// 函数声明
double getAverage(int *arr, int size);
double getAverage(int *arr, int size)
{
    
}

从函数返回指针

int * myFunction()
{

}

你可能感兴趣的:(C++简明教程(一))