c++速记1

c++速记1

三月 05, 2023

c++速记1

参考资料

  1. https://note.youdao.com/ynoteshare/index.html?id=ca06d234563957d61d3bdf281afa2233
  2. https://blog.csdn.net/bsmmaoshenbo/article/details/50778068
  3. https://note.youdao.com/ynoteshare/index.html?id=12f60d70f1170c9b3d6a40ee9f25a63f
  4. http://c.biancheng.net/view/1323.html
  5. https://www.bilibili.com/video/BV1et411b73Z
  6. https://www.runoob.com/w3cnote/cpp-header.html

输入输出流:cin、cout、cerr、clog

  1. cin输入流,标准输入
  2. cout 可以重定向(比如输出到文件),通过缓冲区。
  3. cerr不经过缓冲而直接输出,一般用于迅速输出出错信息,是标准错误,默认情况下被关联到标准输出流,但它不被缓冲,也就说错误消息可以直接发送到显示器,而无需等到缓冲区或者新的换行符时,才被显示。一般情况下不被重定向
  4. clog流也是标准错误流,作用和cerr一样,区别在于cerr不经过缓冲区,直接向显示器输出信息,而clog中的信息存放在缓冲区,缓冲区满或者遇到endl时才输出
  5. endl 可以刷新缓冲。在添加打印语句时,应保证一直刷新流,以防程序崩溃,输出还留在缓冲区内。
  6. 标准库定义了4个IO对象,处理输入时使用命名为cin的istream类型对象,这个对象也成为标准输入。处理输出时使用命名为cout的ostream类型对象,这个对象也称为标准输出。标准库还定义了另外两个ostream对象,分别命名为cerr和clog。cerr对象又叫标准错误,通常用来输出警告和错误信息给程序的使用者,而clog对象用于产生程序执行的一般信息。一般情况下,系统将这些对象与执行窗口联系起来,这样,当我们从cin读入时,数据从执行程序的窗口读入,当写到cout、cerr、clog时,输出写至同一窗口。运行程序时,大部分操作系统都提供了重定向输入或者输出流的方法。利用重定向可以将这些流与所选择的文件联系起来

类型

int、short、long 都是带符号类型。char 是否有符号由编译器决定。

因为 char 是否有符号并不确定,因此可以使用 signed char 或 unsigned char 来指定是否有符号。

C++ 提供了几种字符类型:

  1. char:该类型的变量只能容纳一个字符。一个 char 的空间应确保可以存放机器基本字符集中任意字符对于的数字值,即一个 char 的大小和一个机器字节一样。
    1. ''声明字符,""声明字符串
  2. wchar_t:宽字符,用于扩展字符集,wchar_t 确保可以存放机器最大扩展字符集中的任意一个字符。
  3. char16_t 和 char32_t:为 Unicode 字符集服务。

类型选择

明确知晓数值不可能为负时,选用无符号类型。

整数运算用 int,数值太大时用 long long,不用 short 和 long

浮点数运算用 double。float 和 double 的计算代价相差无几

类型转换

  1. 把浮点数赋给整型时,结果仅保留小数点前的部分
  2. 赋给无符号类型超出范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。比如 -1 赋给 8 位 unsigned char 的结果是 255(-1=256*(-1)+255)
  3. 赋给带符号类型超出范围的值时,结果是未定义的。程序可能工作,可能崩溃。

程序尽量避免依赖于实现环境的行为。比如 int 的尺寸在不同环境可能不同。

含有无符号类型的表达式

一个表达式中既有无符号数又有int值时,int会被转换成无符号数

无符号减无符号数,结果还是无符号数,如果是负值就等于该符数加上无符号数的模

变量

对于c++而言,”变量“和”对象“一般可以互换使用。

c++中,对象通常指一块能存储数据并具有某种类型的内存

变量声明和定义的关系

声明定义是严格区分的。

要声明一个变量加 extern,声明变量不能赋值。

任何包含了显式初始化的声明即成为定义。

1
2
3
extern int i;     // 声明 i
int i; // 定义i;
extern int i = 1; // 定义 i,初始化抵消了 extern 的作用。

变量只能被定义一次,但是可以多次声明。

声明和定义的区分很重要

c++是静态类型语言,其含义是在编译阶段检查类型

引用

引用是给对象起的别名。初始化引用时,是将引用和对象绑定在一起。引用无法重定向,只能一直指向初始值。

引用必须初始化。引用的初始值必须是一个对象,不能是字面值。

对引用的所有操作都是对与之绑定的对象的操作。

引用非对象。

不能定义对引用的引用,因为引用非对象。

1
int &r = i;

引用只能绑定在对象上,不能与字面值或表达式绑定。

引用只能绑定同类型对象。

指针

在块作用域内,指针如果没有被初始化,值将不确定。

指针必须指向指定的类型,不能指向其他类型。

1
2
3
4
int i = 0;
double *dp = &i; // 错误
long *lp = &i; // 错误
int *ip = i; // 这个也是错误的,但 int *ip = 0; 是正确的

指针与引用的不同:

  1. 指针是一个对象而引用不是;
  2. 指针可以重定向引用不可以;
  3. 有指向指针的指针无引用的引用;
  4. 指针不需要在定义时赋初值而引用需要。

不能定义指向引用的指针。可以定义指向指针的引用。

1
2
int *p; 
int* &r = p; // r是对指针p的引用

面对如上 *&r 这样比较复杂的指针或引用的声明语句时,从右向左读比较易于弄清。

利用解引用符(*)可以访问指针指向的对象。

空指针

1
2
3
int *p = nullptr; // 三种定义空指针的方式。最好用第一种
int *p = 0;
int *p = NULL; // NULL 是在头文件 cstdlib 中定义的预处理变量,值为 0。

建议初始化所有指针。

非零指针对应的条件值是 ture,零指针对应的条件值是 false。

void*指针

void* 指针和空指针不是一回事。

void* 指针是特殊的指针类型,可以存放任意对象的地址。它的用处比较有限。

野指针

1
2
3
4
5
6
7
8
9
10
11
12
int main() {

//指针变量p指向内存地址编号为0x1100的空间
int * p = (int *)0x1100;

//访问野指针报错
cout << *p << endl;

system("pause");

return 0;
}

const修饰指针

const修饰指针有三种情况

  1. const修饰指针 —- 常量指针
  2. const修饰常量 —- 指针常量
  3. const即修饰指针,又修饰常量

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int main() {

int a = 10;
int b = 10;

//const修饰的是指针,指针指向可以改,指针指向的值不可以更改
const int * p1 = &a;
p1 = &b; //正确
//*p1 = 100; 报错


//const修饰的是常量,指针指向不可以改,指针指向的值可以更改
int * const p2 = &a;
//p2 = &b; //错误
*p2 = 100; //正确

//const既修饰指针又修饰常量
const int * const p3 = &a;
//p3 = &b; //错误
//*p3 = 100; //错误

system("pause");

return 0;
}

看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量

头文件机制

通常,在一个 C++ 程序中,只包含两类文件—— .cpp 文件和 .h 文件。其中,.cpp 文件被称作 C++ 源文件,里面放的都是 C++ 的源代码;而 .h 文件则被称作 C++ 头文件,里面放的也是 C++ 的源代码。

C++ 语言支持”分别编译”(separatecompilation)。也就是说,一个程序所有的内容,可以分成不同的部分分别放在不同的 .cpp 文件里。.cpp 文件里的东西都是相对独立的,在编译(compile)时不需要与其他文件互通,只需要在编译成目标文件后再与其他的目标文件做一次链接(link)就行了。比如,在文件 a.cpp 中定义了一个全局函数 “void a(){}”,而在文件 b.cpp 中需要调用这个函数。即使这样,文件 a.cpp 和文件 b.cpp 并不需要相互知道对方的存在,而是可以分别地对它们进行编译,编译成目标文件之后再链接,整个程序就可以运行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* math.cpp */
double f1()
{
//do something here....
return;
}
double f2(double a)
{
//do something here...
return a * a;
}
/* end of math.cpp */
/* 并把"这些"函数的声明放在一个头文件 math.h 中:*/

/* math.h */
double f1();
double f2(double);
/* end of math.h */
/* 在另一个文件main.cpp中,我要调用这两个函数,那么就只需要把头文件包含进来:*/

/* main.cpp */
#include "math.h"
main()
{
int number1 = f1();
int number2 = f2(number1);
}
/* end of main.cpp */

指针和函数

作用:利用指针作函数参数,可以修改实参的值

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//值传递
void swap1(int a ,int b)
{
int temp = a;
a = b;
b = temp;
}
//地址传递
void swap2(int * p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}

int main() {

int a = 10;
int b = 20;
swap1(a, b); // 值传递不会改变实参

swap2(&a, &b); //地址传递会改变实参

cout << "a = " << a << endl;

cout << "b = " << b << endl;

system("pause");

return 0;
}

总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递