目录
一. 前言
本文为个人学习笔记,学习资料为c++菜鸟教程,仅供参考~
二. c++ 变量类型
- (1) float – 单精度浮点值。单精度是这样的格式,1位符号,8位指数,23位小数。
(2) double – 双精度浮点值。双精度是1位符号,11位指数,52位小数。
- c++中的变量声明:
(1) 变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。
(2) 当使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用。
(3) 可以使用 extern 关键字在任何地方声明一个变量。虽然您可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。 - 获取变量的类型
cout << typeid(c).name() << endl; // c为变量
三. c++ 变量作用域
局部变量:在函数或一个代码块内部声明的变量。
全局变量:在所有函数外部声明的变量。
初始化:
定义局部变量时,系统不会对其初始化,必须自行对其初始化。
定义全局变量时,系统会自动初始化为下列值:
int 0
char ‘\0’
float 0
double 0
pointer NULL
四. c++ 常量
- 定义常量
在 C++ 中,有两种简单的定义常量的方式:
(1) 使用 #define 预处理器。
(2) 使用 const 关键字。
五. c++ 修饰符类型
- c++中的类型限定符
类型限定符提供了变量的额外信息。
const - - 类型的对象在程序执行期间不能被修改改变。
volatile(后期留意) - - 告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率。
restrict (后期留意) - - 由 restrict 修饰的指针是唯一 一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。
六. c++ 存储类
- 存储类的作用:定义程序中变量/函数的范围(可见性)和生命周期。
- 存储类的位置:放置在它们所修饰的类型之前。
- 可用的存储类:static(重点), extern(重点), thread_local(用到再说), mutable(用到再说), auto(用到再说), register(用到再说)。
- 存储类的分别介绍:
(1) static
① 使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
② 当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。
#include <iostream>
// 函数声明
void func(void);
static int count = 10; // 全局变量,注意 因为存在std::count,所以代码开始处不用using namespace std
int main()
{
while(count--)
{
func();
}
return 0;
}
// 函数定义
void func( void )
{
static int i = 5; // 局部静态变量,如果无static,则打印出的i一直为6
i++;
std::cout << "变量 i 为 " << i ;
std::cout << " , 变量 count 为 " << count << std::endl;
}
(2) extern
通常用于当有两个或多个文件共享相同的全局变量或函数。
第一个文件:main.cpp
#include<iostream>
int count;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
第二个文件:support.cpp
#include<iostream>
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
编译这两个文件,如下所示:
$ g++ main.cpp support.cpp -o write
这会产生 write 可执行程序,尝试执行 write,它会产生下列结果:
$ ./write
Count is 5
(3) 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<int> v; // 本地变量
}
(4) mutable
mutable 说明符仅适用于类的对象,这将在本教程的最后进行讲解。它允许对象的成员替代常量。也就是说,mutable 成员可以通过 const 成员函数修改。
(5) auto
从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符。
自 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';//错误,必须是初始化为同一类型
(6) register
从 C++ 17 开始,register 关键字被弃用。
register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。
这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)。
{
register int miles;
}
寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。
七. c++ 运算符
- 逻辑运算符
&& – 逻辑与;|| – 逻辑或;! – 逻辑非。 - 位运算符(对二进制操作)
& - - 按位与;
| - - 按位或;
^ - - 按位异或;
~ - - 二进制补码运算符是一元运算符,具有"翻转"位效果,即0变成1,1变成0。 (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
‘<<’ 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。
‘> >’ 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 - 杂项运算符
sizeof - - 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。
Condition ? X : Y - - 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。
, 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
#include <iostream>
using namespace std;
int main()
{
int i, j;
j = 10;
i = (j++, j+100, 999+j);
cout << i; // 1010, 当i = (j++,j+=100, 999+j); // i = 10+1+100+999 = 1110
return 0;
}
.(点)和 ->(箭头) - - 成员运算符用于引用类、结构和共用体的成员。
Cast - - 强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。
& - - 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。
‘*’ - - 指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。
八. c++ 循环
- 基于范围的for循环
#include <iostream>
using namespace std;
//基于范围的for循环
int my_array[5] = { 1,2,3,4,5 };
int main() {
for (int &x : my_array) // &的作用: 会改变my_array中元素的值,∵ 指针运算符 & 返回变量的地址
{
x *= 2;
cout << x << endl;
}
cout << my_array[1] << endl;
return 0;
}
涉及字符串的操作 示例
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
// string str("some string");
string str= "some string";
for (auto &c : str) // auto类型也是 C++11 新标准中的,用来自动获取变量的类型
{
c = toupper(c); // toupper -- 将字符改为大写
}
cout << str << endl;
return 0;
}
- break - - 只停止执行它所对应的最内层的循环 。
- goto语句是将控制转移到被标记的语句。不建议在程序中使用 goto 语句。
九. c++ 判断
-
switch语句
(1) 示例代码(比后面的语法更好理解)#include <iostream> using namespace std; int main() { //局部变量声明 char grade = 'B'; switch (grade) { case 'A': cout << "很棒!" << endl; break; case 'B': case 'C': cout << "做得好!" << endl; break; case 'D': cout << "您通过了" << endl; break; case 'F': cout << "最好再试一下" << endl; break; default: cout << "无效的成绩" << endl; } cout << "您的成绩是 " << grade << endl; return 0; }
(2) 语法
switch(expression){ case constant-expression : statement(s); break; // 可选的 case constant-expression : statement(s); break; // 可选的 // 您可以有任意数量的 case 语句 default : // 可选的 statement(s); }
解释: switch 语句必须遵循下面的规则: 1. switch 语句中的 expression 必须是一个整型或枚举类型,或者是一个 class 类型,其中 class 有一个单一的转换函数将其转换为整型或枚举类型。 2. 在一个 switch 中可以有任意数量的 case 语句。每个 case 后跟一个要比较的值和一个冒号。 3. case 的 constant-expression 必须与 switch 中的变量具有相同的数据类型,且必须是一个常量或字面量。 4. 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。 5. 当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。 6. 不是每一个 case 都需要包含 break。如果 case 语句不包含 break,控制流将会 继续 后续的 case,直到遇到 break 为止。 7. 一个 switch 语句可以有一个可选的 default case,出现在 switch 的结尾。default case 可用于在上面所有 case 都不为真时执行一个任务。default case 中的 break 语句不是必需的。
十. c++ 函数
-
传值调用
该方法把参数的实际值赋值给函数的形式参数。
修改函数内的形式参数对实际参数没有影响。示例代码
#include <iostream> using namespace std; // 传值调用: // 函数声明 // void swap(int x, int y); int swap(int x, int y); int main() { int a = 100; int b = 200; cout << "交换前,a的值:" << a << endl; //100 cout << "交换前,b的值:" << b << endl; //200 // 调用函数来交换值 a, b = swap(a, b); cout << "交换后,a的值:" << a << endl; //100 cout << "交换后,b的值:" << b << endl; //200 return 0; } //函数定义 void swap(int x, int y) { int temp; temp = x; x = y; y = temp; return; }
-
指针调用
该方法把参数的地址赋值给形式参数。
在函数内,该地址用于访问调用中要用到的实际参数。
修改形式参数会影响实际参数。#include <iostream> using namespace std; // 指针调用 // 函数声明 void swap(int *x, int *y); int main() { int a = 100; int b = 200; cout << "交换前,a的值:" << a << endl; //100 cout << "交换前,b的值:" << b << endl; //200 // 调用函数来交换值 swap(&a, &b); cout << "交换后,a的值:" << a << endl; //200 cout << "交换后,b的值:" << b << endl; //100 return 0; } void swap(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; return; }
-
引用调用
后期再说,容易与指针调用混淆~ -
lambda 函数与表达式 (后期可再回头熟练~)
十一. c++ 数字
- c++ 数学运算
(1) double pow(double, double)
假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。
(2) double hypot(double, double)
该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。
(3) double log(double)
该函数返回参数的自然对数。 - c++ 随机数
关于随机数生成器,有两个相关的函数。一个是 rand(),该函数只返回一个伪随机数。生成随机数之前必须先调用 srand() 函数。
示例代码:
#include<iostream>
#include<ctime>
// #include<cstdlib>
using namespace std;
int main()
{
int i, j;
// 设置种子
srand((unsigned)time(NULL));
// srand(5); //固定随机种子
/*生成10个随机数*/
for (i = 0; i < 10; i++)
{
//生成实际的随机数
j = rand();
cout << "随机数: " << j << endl;
}
return 0;
}
十二. c++ 数组
- 形式:大括号{}(不同于python,不是[])。
- 指向数组的指针(学完指针再学该部分)
- trick: setw()的使用(设置字段宽度)
示例代码
#include<iostream>
#include<iomanip>
using namespace std;
// using std::setw;
int main()
{
int n[10]; //n是一个包含10个整数的数组;
//初始化数组元素
for (int i = 0; i < 10; i++)
{
n[i] = i + 100;
}
// cout << setw(14) << "Element" << setw(14) << "Value" << endl;
cout << "Element" << setw(7) << "Value" << endl;
// 输出数组中每个元素的值
for (int j = 0; j < 10; j++)
{
cout << setw(7) << j << setw(7) << n[j] << endl;
}
cout << n << endl;
return 0;
}
- 从函数返回数组
(1) C++ 不允许返回一个完整的数组作为函数的参数,但是,可以通过指定不带索引的数组名来返回一个指向数组的指针;
(2) 如果想要从函数返回一个一维数组,必须声明一个返回指针的函数;
(3) C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
示例代码:
#include<iostream>
using namespace std;
// 要生成和返回随机数的函数
int * getRandom()
{
static int r[10];
// 设置种子
srand(5);
for (int i = 0; i < 10; i++)
{
r[i] = rand();
cout << r[i] << endl;
}
return r;
}
//要调用上面定义函数的主函数
int main()
{
//一个指向整数的指针
int * p;
p = getRandom();
for (int i = 0; i < 10; i++)
{
cout << "*(p + " << i << "):";
cout << *(p + i) << endl;
}
return 0;
}
- 获取数组长度
(1)在C/C++中并没有提供直接获取数组长度的函数;
(2)方法:len = (sizeof(array) / sizeof(array[0]))
相比较之下,字符串可以直接通过str.size()获取字符串长度。
十三. c++ 指针
- 指针的作用(部分):
(1)简化一些 C++ 编程任务;
(2)一些任务,如动态内存分配,没有指针无法执行。 - 指针的数据类型
所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。
不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。 - 在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
注: 很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。 - 我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。
- 如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
- 一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。
int **var;
十四. c++ 引用
- 引用与指针有三个主要的不同:
(1)不存在空引用。引用必须连接到一块合法的内存。
(2)一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
(3)引用必须在创建时被初始化。指针可以在任何时间被初始化。 - 试想变量名称是变量附属在内存位置中的标签,可以把引用当成是变量附属在内存位置中的第二个标签。 因此,您可以通过原始变量名称或引用来访问/改变变量的内容。
十五. c++ 时间日期
- 需要在 C++ 程序中引用ctime头文件。
- 常用代码:
#include <iostream>
#include <ctime>
#pragma warning(disable : 4996)
using namespace std;
int main( )
{
// 基于当前系统的当前日期/时间
time_t now = time(0);
// 把 now 转换为字符串形式
char* dt = ctime(&now);
cout << "本地日期和时间:" << dt << endl;
// 把 now 转换为 tm 结构
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "UTC 日期和时间:"<< dt << endl;
tm *ltm = localtime(&now);
// 输出tm结构的各个组成部分
cout << "年:" << 1900 + ltm->tm_year << endl;
cout << "月:" << 1 + ltm->tm_mon << endl;
cout << "日:" << ltm->tm_mday << endl;
cout << "时间:" << ltm->tm_hour << ":";
cout << ltm->tm_min << endl;
cout << ltm->tm_sec << endl;
}