三、The C in C++
第三章主要讲解了 C++ 中继承自 C 语言的核心元素,包括函数创建、执行控制、操作符、数据类型、作用域、存储指示、复合类型创建等。
3.1 创建函数(Creating Functions)
- C++允许函数重载,同名的函数可以根据参数类型和数量区分:
int add(int x, int y, int z);
int add(float,float,float);
void add();
- 函数可放入自己的函数库,构成.h 和 .lib 文件,方便重复使用和分布。
3.2 控制程序的执行流程(Controlling execution)
- 分支:if-else,switch-case
- 循环类型:while,do-while,for
- 特殊操作:break 中断循环,continue 跳过本轮继续
- 递归:函数自己调用自己
3.3操作符
- 一元操作符:++,–,,,&
- 二元操作符:+ - * / % = == != && || << >>
- 其他:?: 条件操作符,sizeof 等
- 进行计算时应使用括号明确进体顺序,避免误解
3.4 数据类型简介
基本类型:bool, char, int, float, double, void
指针:用于存放地址,需指明指向的类型
#include <iostream>
using namespace std;
int dog,cat,bird;
void f(int pet) {cout << "pet id number:" << pey << endl;}
void main(){int i,j;cout << "f(): " << &f << endl;//produce the addresscout << "dog: " << &dog << endl;cout << "cat: " << &cat << endl;cout << "bird: " << &bird << endl;cout << "i: " << &i << endl;cout << "j: " << &j << endl;int *p = new int;cout << "p: " << &p << endl;cout << "&(*p) :" << &(*p) << endl;//cout << "&(*p)" << p << endl delet p;
}
output:
由此可见
f()的地址在代码段/文本段,属于静态分配
dog,cat,bird,全局变量地址,地址相邻,分配在静态区(BSS段)
i、j, 局部变量,地址较低,且和全局变量区分明显,属于栈区,且高地址向低地址增长
&p , 局部变量int * p本身的地址,也是栈区
&(_p) , 动态分配的int 类型变量地址(通过*_new int),属于堆区。
引用:变量的别名,必须初始化,不可重新指向
int a=3,b=5;
int& m = a;//OK
int& m; // error
int& m = b;//ok
int&k = m;
int n = m;
int*p = &m;
int*& ref = p;
m = m+5

引用仅仅只是一个别名,就像m,k它们和a指向同一个位置,占用同一个内存
参数传递:分为值传递、地址传递、引用传递
int i = 10;
int *p = &i;
void* vp = p;//OK
int *ip = vp;//error
其他指针赋值给void*,但是不可以void*赋值给其他型
比较Argument Passing(Value,pointer,reference)
Pass by value
//C03:PassByValue.cpp
#include <iostream>
using namespace std;
void f(int a){cout << "a=" << a << endl;a = 5;cout << "a=" << a << endl;
}
void main(){int x = 47;cout << "x=" << x << endl;f(x);cout << "x=" << x << endl;
}
输出:
x = 47
a = 47
a = 5
x = 47
Pass by address
//C03:PassAddress.cpp
#include <iostream>
using namespace std;
void f(int *p){cout << "p=" << p << endl;cout << "*p=" << *p << endl;*p = 5;cout << "p=" << p << endl;
}
void main(){int x = 47;cout << "x=" << x << endl;cout << "&x=" << &x << endl;f(x);cout << "x=" << x << endl;
}

Pass reference
//C03:PassReference.cpp
#include <iostream>
using namespace std;
void f(int& a){cout << "a=" << a << endl;a = 5;cout << "a=" << a << endl;
}
void main(){int x = 47;cout << "x=" << x << endl;f(x);cout << "x=" << x << endl;
}
比较swap1(Value),swap2(address),swap3(reference)
Passing by Value
#include <iostream>
using namespace std;
void swap1(int x,int y){int temp;temp = x;x = y;y = temp;cout << "x=" << x << "," << "y=" << y << endl;
}
void main(){int a(5),b(9);swap1(a,b)//swap1(5,9);ok?cout << "a:" << a << "," << "b:" << b << endl;
}
问题swap1(5,9);ok?
OK。在 C++ 中,函数参数如果是值传递(pass by value),你完全可以传入常量字面值(如 5
, 9
),因为:
int x
,int y
是 值传递:函数内会把参数值拷贝一份到局部变量中;swap1(5, 9)
等价于:x = 5; y = 9;
—— 合法;- 虽然
x
、y
在函数中交换了,但这些交换并不会影响外部实参。
Passing by address
#include <iostream>
using namespace std;
void swap2(int* x,int* y){int temp;temp = *x;*x = *y;*y = temp;cout << "X = " << *x << "," << "y=" << *y << endl;
}
int main(){int a(5),b(9);swap2(&a,&b);//swap2(&5,&9);?cout << "a=" << a << "," << "b:" << b << endl;return 0;
}
output:
问题swap2(5,9);ok?
不OK。 函数参数是两个 int
,也就是指向整型的指针,因此必须传入 **地址(address)。 然而 **5
和 9
是整型字面值(int),不是地址。编译器无法把 int
自动转换成 int
(除非你非常暴力地强制类型转换)
Passing by reference
#include<iostream>
using namespace std;
void swap3(int& x,int& y){int temp;temp = x;x = y;y = temp;cout << "x=" << x << "," << "y=" << y << endl;
}
int main(){int a(5),b(9);swap3(a,b);//swap3(5,9);?cout << "a=" << a << "," << "a=" << a << endl;
}
output
问题swap3(5,9);ok?
不OK。这是引用传递(pass by reference),也就是说:
- 必须传入变量,因为函数参数会变成这些变量的别名(reference);
5
和9
是常量值,不是变量,不能作为引用参数;- 引用必须引用一个 具名对象(L-value),但
5
、9
是 R-value(右值),没有地址可引用;
Summary of Argument Passing

3.5 作用域(Scoping)
3.5.1变量只能在其归属作用域内进行操作
void main(){int scp1;//scp1 visible here{ //scp1 still visible hereint scp2;//scp2 visible here{//scp1 & scp2 still visible hereint scp3;//scp1,scp2&scp3 visible here}//<<--scp3 destoryed here//scp3 not available here//scp1 & scp2 still visible here}//<<--scp2 destoryed here//scp3 & scp3 not available here
}//<<--scp1 destoryed here
- C++ allows you to define variable anywhere in a scope,so you can define a variable right before you use it
- It reduces the errors you get from being forced to jump back and forth within a scope
- 大括号定义一个新作用域
- C++ 允许在任意作用域内定义变量,更加灵活
3.6存储分配指示(Specifying storage allocation)
3.6.1 全局变量(Global variables)
- Global variables are defined outside all function bodies and are available to all parts of program.
- Global variables are unaffected by scopes and are always available.
- If the existence of a global variable in one file is declared using the extern keyword in another file,the data is available for use by the second file.
//C03:Global.cpp
//Demonstration of global variables
#include <iostream>
using namespace std;
int globe = 0;
void func();
void main(){globe = 12;cout << globe << endl;func();//Modify globecout << globe << endl;
}//C03:Global2.cpp
//Accessing external global variables
extern int globe;
//The linker resolves th reference
void func(){globe = 47;
}
output:
//C03:Global.cpp
//Demonstration of global variables
#include <iostream>
using namespace std;
int globe = 0;
void func();
void main(){int globe = 12;cout << globe << endl;func();//Modify globecout << globe << endl;
}//C03:Global2.cpp
//Accessing external global variables
extern int globe;
//The linker resolves th reference
void func(){globe = 47;
}
output:
原因解释
- 在
main()
中定义的int globe = 12;
屏蔽了全局的globe func()
修改的是全局的globe
,但main()
中打印的是局部的- 因此即使func修改了全局变量,main中的输出仍为局部值
12
3.6.2局部变量(Local variables)
- Local variables are often called automatic variables because they automatically come into being when the scope is entered and automatically go away when the scope closes.
void func(){
int globe = 47;
}
3.6.3Static variables
- If you want a value to be persistent(持久的) throughout the life of a program,you can define a function’s local variable to be static and give it an initial value.
- The initialization is performed only the first time when the function is called,and the data retains(保留) its value between function calls.
- A static is unavailable outside the scope of the function
//C03:Static.cpp
//Using a static variable in a function
#include <iostream>
using namespace std;
void func(){static int i = 0;//int the functioncout << ++i << ",";
}
void main(){for (int x = 0;x<5;x++){func();}//cout i;//error
}
output:
//C03:FileStatic.cpp
//File scope demonstration.Compiling and
//lingking this file with FileStatic2.cpp
//will cause a linker error
//File scope means only available in this file;
static int fs = 0;
void main(){fs = 1;
}//C03:FileStatic2.cpp
//Trying to references fs
extern int fs;//linker error
void func(){fs = 100;//error
}
3.6.4Extern & linkage
Extern
- The extern keyword tells the compiler that a variable or a function exists,even if the compiler hasn’t yet seen it in the file currently being compiled.
- This variable or function may be defined in another file or further down in the current file.
linkage
- In an executing program,and identifier is represented by storagefont> in memory that holds a variable or a compiled function body.
internal linkage
- internal linkage means that storage is created to represent the identifier only for the file being compiled.
storage is created to represent the identifier意思是标识符所代表的数据在内存中的位置
- internal linkage在C++中通过keyword static 实现
external linkage
- external linkage means that a single piece of storage is created to represent the identifier for all files being compiled.(即非
static
的全局变量,全局函数) - 在所有函数之外定义的变量,以及函数定义,默认具有external linkage。你可以同过
static
关键字强制将其转为internal linkage,也可以通过extern
关键字显示声明其为external linkage. - 局部变量只在函数调用期间暂时存在于stack上。linker(链接器)无法识别自动变量(即局部变量),因此它们没有连接属性(no linker)。
//C03:FileStatic.cpp
//internal linkage and external linkage
//linking this file with FileStatic2.cpp
static int fs = 0;//in the file
static void f(){};
int fe = 0;
extern void func(){};
void main(){fs = 1;func();
}//FileStatic.cpp
extern int fs;//linker error
extern int fe;
extern void f();//linker error
void func(){//fs = 100;//errorfe = 100;//f();//error
}
3.6.5常量(Constants)
-
#define PI 3.14159//这是一个replacement 并非Constants
//Its scope is: from #define to #undef
-
const double PI = 3.14159; //const
-
const
是一个实际变量,但值不可变 -
const
必须有一个初始化的值
void f(const int a){};
int main(){f(5); // 调用 f 函数,传入字面值 5
}
这是可行的
3.6.6 volatile
- Whereas(尽管) the qualifier(修饰符)
const
tells the compiler “This never changes”(Which allows the compiler to perform extra optimizations). - 修饰符
volatile
则告诉编译器“你永远不知道这个值什么时候会改变” ,并且禁止编译器基于该变量的稳定性进行任何优化
3.7操作符与用法(Operators and their use)
3.7.1赋值(Assignment)
A = 4;
- A:an lvalue(左值),a distinct,named variable
- 4:an rvalue(右值),a constant,variable,or expression that can produce a value
3.7.2 数学的操作符(Mathematical operators)
- addition(+)
- subtraction(-)
- division(/)
- multiplication(*)
- modulus(%):this produces the remainder(余数) from integer division.
3.7.3 关系运算符(Relational operators)
- <,>,<=,>=,==,!=
- 这些运算符产生bool值,就像C语言一样
- 1 for true,0 for false.
3.7.4 逻辑操作符(Logical operators)
- &&,||,|,!
- The result is true if it has a non-zero value,and false if it has a value of zero.
3.7.5按位操作符(Bitwise operators)
- bitwise and (&)
- bitwise or (|)
- bitwise not /complement (~)//补码
- bitwise exclusive or / xor (^)//异或
- &=;|=;^=
3.7.6移位操作符(Shift operators)
- left-shift (<<)
- right-shift (>>)
- <<= ; >>=
3.7.7一元操作符(Unary operators)
- Bitwise not (~)
- logical not (!)
- unary minus (-) ; unary plus (+)
- increment (++); decrement (–)
- address-of (&)
- dereference (& and ->)//间接引用
- cast
- new;delelte
3.7.8三元操作符(The ternary operator)
-
c=a>b ? a : b
if (a> b ) c = a;
else c = b;
3.7.9 逗号运算符(The comma operators)
//C03:CommaOperator.cpp
#include <iostream>
using namespace std;
void main(){int a =0,b=1,c=2,d=3,e=4;a = (b,c,d,e);cout << "a=" << a << endl;//The parentheses(圆括号) are critical here.Without//them,the statement will evaluete to.(a = b),c,d,e;cout << "a=" << a << endl;
}
output:
原因解释:
a = (b,c,d,e);
- 逗号表达式
(x, y)
会先执行 x,再执行 y,并返回 y 的值。 - 所以
a = (b, c, d, e);
实际含义是:a = e。 - 也就是逗号表达式(b,c,d,e) 会,顺序执行
b,c,d,e
这些表达式,最终只返回最后一个表达式e的值 - 所以
a
只赋值了一次:a = e;
(a = b),c,d,e;
- 它不是赋值语句(它不是
a = (……)
) - 而是一个逗号表达式序列,主作用是执行它里面的每个子表达式,并返回最后一个表达式的值。
- 其中
(a = b)
起到赋值操作,将b的值赋给a - 然后剩下的
c,d,e
都是无副作用的值表达式,被执行但不改变任何东西 - 所以最后a = 1
3.7.10 表达式里常出的错误(Common pitfalls when using operators)
= ==
&& &
|| |
//C03:Pitfall.cpp
void main(){int a = 1,b = 1;while (a = b)//error{……}
}
3.7.11类型转换操作符(Casting operators)
-
Casting allows you to make this type conversation explicit
-
(type name) value
-
type name (argument)
int a = 100;
float b = float(a)
float c = (float) a
int d = int (5.5);
3.7.12 C++ explicit casts
-
static_cast<Type> (e): 用于在相关类型之间进行转换。
static_cast<int\>(5.5);
-
reinter_cast<Type>(e): 处理不相关类型之间的转换,比如指针类型之间的“强制解释”。
int *p = reinterpret_cast<int*\>(0x12345678);
🚨 非常危险,常用于底层编程。
-
const_cast<Type>(e): 去掉变量的
const
限定。用于把const
对象转换为非常量对象(⚠谨慎使用)const int*p = ……;
int *q = const_cast<int*\>(p);
-
dynamic_cast<Type>(e): 在运行时进行类型检查的转换。通常用于多态类之间的类型安全向下转换。
Base* bp = new Derived();
Derived* dp = dynamic_cast<Derived*>(bp); // 如果 bp 不是 Derived*,则返回 nullptr
3.7.13 sizeof操作符
#include <iostream>
using namespace std;
void main(){cout << "bool:" << sizeof(bool) << endl;cout << "char:" << sizeof(char) << endl;cout << "int:" << sizeof(int) << endl;cout << "float:" << sizeof(float) << endl;cout << "double:" << sizeof(double) << endl;cout << "long double:" << sizeof(long double) << endl;
}
output:
结果可能会因为不一样的编译系统而不一样
3.7.14 asm关键字
- 你可以在C++程序中编写针对硬件的汇编代码
- 编写汇编语言时需要使用的具体语法依赖于(编译器)。
3.7.15显示操作符(Explicit operators)
-
These are keywords for bitwise and logical operators.(Not all complier(编译器) can support them )
-
and : && (logical and)
or : || (logical or)
not : ! (logical NOT)
not_eq: != (logical not-equivalne)
bitand: & (bitwise and)
and_eq: &= (bitwise and-assignment)
bitor: | (bitwise or)
or_eq: |= (bitwise or-assignment)
xor: ^ (bitwise exclusive-or)
xpr_eq: ^= (bitwise exclusive-or-assignment)
compl:~ (ones complement)
3.8复合类型创建(Composite type creation)
简述
- C++ 提供工具,允许你组合(compose)基础数据类型,从而创建更复杂的数据类型。
- 其中最重要的是结构体(struct),它是C++中**类(class)**的基础
3.8.1使用typedef
创建别名(Aliasing names with typedef
)
-
typedef existing-type alias-name
typedef int* IntPtr;
IntPtr x,y;
3.8.2使用struct
组合变量(Combining variables with struct)
- A struct is a way to collect a group of variables into a structure.
- The struct is the foundation for the class in the C++.
//C03:SimpleStruct.cpp
struct Structure1{char c;double b;
};
int main(){Structure1 s1,s2;Structure1* p = &s2;s1.c = 'a';//Select an element using a '.'s1.d = 0.00093;p->c = 'b';//Select an element using a '->'p->d = 10.5;return 0;
}
3.8.3使用enum
澄清程序(Clarifying programs with enum)
-
An enumeration is a type that can hold a set of values specified by the user.
enum name{ASM,AUTO,BREAK}
-
By default(默认) ,the values of enumerators(枚举值) are initialized increasing from 0
ASM = 0,AUTO = 1,BREAK = 2
-
An enumerator(枚举值) can be initialized by a constant-expression of integral type(整型常量表达式)
ASM = 1,AUTO = 5;
这里这样写完后,BREAK就会默认为AUTO+1,所以BREAK = 6 -
It is used less in C++.(在C++中使用较少)
3.8.4使用union
节省内存(Saving memory with union)
- Sometimes a program will handle different types of data using the same variable.
- A struct contains all the possible different types you might need to store.但是使用
struct
时,每个成员都有独立的内存空间。 - An union is a special type of a class,where every member has the same address(每个成员共用同一个地址)
union A{int a;double b;
};
A x;
3.8.5 数组(Arrays)
概述
-
T[size]
表示“由size
个T
类型元素组成的数组”。 -
The elements are indexed from 0 to size-1(元素索引范围)
-
There is no array assignment. (数组不能直接赋值)
意思就是说在C++中,不能把一个数组整体赋值给另一个数组
int a[3] = {1,2,3}; int b[3]; b = a;//错误!不能直接赋值数组
原因:这是因为在C++中,数组名其实代表的时数组的首地址(在下文会提到),不是一个可以整体赋值的“对象”。并非是平常所理解的变量
-
The name of an array is the starting address of the array.
-
Initialization:
int v1[4] = {1,2,3,4}; char v2[] = {'a','b','c','\0'}; char v3[2] = {'a','b','\0'};//error 不可以超出数组size char v4[3] = {'a','b','c','\0'};//ok int v5[8] = {1,2,3,4};//部分初始化,未初始化的会系统默认初始化为0
注意事项:
char v[3] = {'a','b','c'};
是字符数组,这样编写不会报错,但它由于缺少’\0’,所以并不是一个字符串,如果执行cout << v;
,程序将会崩溃,因为cout
认为它是一个字符串,但它没有\0
来标记结束。
Pointer and arrays
-
The name of an array can be used as a pointer to its initial elements.
-
Access a array can be achieved either through a pointer to an array plus an index or through a pointer to an element.
-
注意:大多数C++实现不会对数组访问进行范围检查(即你越界访问不会报错,但会导致未定义行为)。
int v[] = {1,2,3,4}; int* p1 = v;//pointer to initial element int* p2 = &v[2];
v[2],*(v+2),*(p1+2),*p2
都是3
3.9 调式提示(Debugging hints)
3.9.1预处理器调试标志(Pre-processor debugging flags)
这是用#define
来控制是否输出调试信息的方式,编译时决定
示例:
#include <iostream>#define DEBUG // 注释掉这一行可以关闭调试输出int main() {int a = 5, b = 10;#ifdef DEBUGstd::cout << "Debug: a = " << a << ", b = " << b << std::endl;#endifstd::cout << "Sum = " << (a + b) << std::endl;return 0;
}
DEBUG只是一个宏的名字,可以用DEBUG1等等代替
用法说明:
#ifdef DEBUG
:只有在DEBUG
被定义时,才会编译输出的调试代码。- 可以用来临时打开/关闭调试信息,不影响主逻辑
3.9.2运行时调试标志(Runtime debugging flags)
这是一种运行时控制调试信息的方式,通常用变量或命令行参数来控制。
示例:
#include <iostream>int main() {bool debugMode = true; // 运行时控制开关int a = 3, b = 7;if (debugMode) {std::cout << "[DEBUG] a = " << a << ", b = " << b << std::endl;}std::cout << "Product = " << (a * b) << std::endl;return 0;
}
用法说明:
- 可以通过配置文件、命令行参数等方式改变
debugMode
。 - 比预处理器方式灵活,不需要重新编译
3.9.3C语言的assert()
宏
这是一个运行时断言,如果**表达式为假(false)**,程序会打印错误信息并终止
示例:
#include <iostream>
#include <cassert> // 引入 assertint divide(int a, int b) {assert(b != 0); // 如果 b 为 0,会终止程序return a / b;
}int main() {int result = divide(10, 2); // 正常std::cout << "Result = " << result << std::endl;result = divide(10, 0); // 触发断言失败return 0;
}
用法说明:
assert()
在Debug模式(开放时用的编译配置)下启用,在Release 模式(发布程序时用的优化配置)下会被编译器忽略(你可以用-DNDEBUG
来关闭它)。- 非常适合用于验证前提条件,尤其是在开发测试时。
3.10函数地址(Function address)
概述
- Once(一旦) a function is compiled and loaded into the computer to be executed,it occupies a chunk(区块) of memory,and has an address.
- You can use function address with pointers just as you can use variable address.
3.10.1定义函数指针(Defining a function pointer)
void (*funcPtr)();
表示:funcPtr
是一个指向无参数、无返回值函数的指针funcPtr
是一个指向函数的指针,该函数没有参数,也没有返回值。
void* funcPtr();
表示:funcPtr
是一个函数,它的返回类型时void*
(即返回一个void指针)funcPtr
是一个返回void*
类型的函数(而不是指针)
3.10.2复杂的声明与定义(Complicated declarations & definitions)
- You will rarely(很少) need very complicated declarations and definitions.
- 逐步分析每一部分,并使用从右往左的规则来理解它。
float(*(*fp2)(int,int,float))(int)
fp2
是一个指向函数的指针,这个函数接受三个参数(int
,int
和float
),并返回一个指向另一个函数的指针。- 那个被返回的函数接受一个
int
参数,返回一个float
值。
3.10.3使用函数指针(Using a function pointer)
- Once you define a pointer to a function,you must assign it to a function address before you can use it.
- The function name func denotes(表示) the address of a function
func()
.
//C03.PointerToFunction.cpp
//Defining and using a poinnter to a function
#include <iostream>
using namespace std;
void func(){cout << "func() called" << endl;
}int main(){void (*fp)();//Delare a function pointerfp = func; //Point to a function(*)fp(); //Call the function,func();void (*fp2)() = func; //Define and initialize(*fp2)(); //Call the functionreturn 0;
}
3.11动态存储分配(Dynamic storage allocation)
-
**new/new[]😗*creates dynamic objects.
new type(initialize);
// individual objectnew type[size];
//array
-
new/new[]
returns a pointer that pointer to type. -
new[]
dose not initialize the memory returned.使用
new[]
创建数组时,不会自动初始化数组里的元素。 -
**delete/delete[]😗*destroy dynamic object.
delete pointername;
// individualdelete[] pointername;
// array
-
An object created by new must be destroyed by delete.
-
A pointer can be destroyed by delete/delete[] only once.
#include <iostream>
using namespace std;
int main(){int *p;p = new int(5); //individual obejctcout << *p << endl;delete p; //destory individual objectp = new int[5]; //arrayfor (int i = 0 ; i < 5 ; i++)*(p+i)=i;for (int i = 0 ; i < 5 ; i++)cout << *(p+i) << "\t";delete[] p; //destroy arrayreturn 0;
}
output:
3.12 Summary
- Pointers
- References
- Operators : Casting operators
- Scope
- Const
- Static,extern
- New,delete