【C++】菜鸟教程个人C++学习笔记
我自己在菜鸟学习时的记录,可以实现网页运行代码,讲的也很流畅,源网址在这里
检索目录
- 标准化
- 代码一闪而过的问题所在和解决方法。
- **标识符**
- 为什么要使用 **using namespace std;** ?
- 块注释符(/*...*/)是不可以嵌套使用的。
- 查看所有类型字符的信息
- C++ 中的变量声明
- 变量的类型间是可以互相转换的,转换又分为自动转换和强制转换。
- 定义包含了声明,但是声明不包含定义,如
- 字符常量
- 宏定义 #define 和常量 const 的区别
- 静态局部变量
- C++ [运算符](https://www.runoob.com/cplusplus/cpp-operators.html)
- C++ 中的运算符优先级
- 实例
- 关于逻辑运算符 && ,|| 的巧用方式
- ? : 运算符
- C++ 数学运算
- C++ 随机数
- 使用随机数来发红包
- Array 直接初始化
- 实例
- setw控制输出间隔
- **Vector(向量):**
- C++ 中有大量的函数用来操作以 null 结尾的字符串
- 实例
- 字符串
- C++输入
- 1. cin>>
- 2. cin.get()
- 3.cin.getline()
- 4. getline()
- 5. gets()
- 6.getchar()
- C++ 中使用指针
- [C++ 日期 & 时间](https://www.runoob.com/cplusplus/cpp-date-time.html)
- 输入输出流中的函数(模板):
- C++ [动态内存](https://www.runoob.com/cplusplus/cpp-dynamic-memory.html)
- [C++ 实例](https://www.runoob.com/cplusplus/cpp-examples.html)
- 欢迎访问我的个人网站 [https://www.josu.top/](https://www.josu.top/)
C++ 简介 | 菜鸟教程C++ 简介 C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。
C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。
C++ 被认为是一种中级语言,它综合了高级语言和低级语言的特点。
C++ 是由 Bjarne Stroustrup 于 1979 年在新泽西州美利山贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,最初命名为带类的 C,后来在 1983 年更名为 C++。
C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。
**注意:**使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查。
C++ 完全支持面向对象的程序设计,包括面向对象开发的四大特性:
- 封装
- 抽象
- 继承
- 多态
标准的 C++ 由三个重要部分组成:
- 核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。
- C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
- 标准模板库(STL),提供了大量的方法,用于操作数据结构等。
ANSI 标准是为了确保 C++ 的便携性 —— 您所编写的代码在 Mac、UNIX、Windows、Alpha 计算机上都能通过编译。
由于 ANSI 标准已稳定使用了很长的时间,所有主要的 C++ 编译器的制造商都支持 ANSI 标准。
学习 C++,关键是要理解概念,而不应过于深究语言的技术细节。
学习程序设计语言的目的是为了成为一个更好的程序员,也就是说,是为了能更有效率地设计和实现新系统,以及维护旧系统。
C++ 支持多种编程风格。您可以使用 Fortran、C、Smalltalk 等任意一种语言的编程风格来编写代码。每种风格都能有效地保证运行时间效率和空间效率。
基本上每个应用程序领域的程序员都有使用 C++。
C++ 通常用于编写设备驱动程序和其他要求实时性的直接操作硬件的软件。
C++ 广泛用于教学和研究。
任何一个使用苹果电脑或 Windows PC 机的用户都在间接地使用 C++,因为这些系统的主要用户接口是使用 C++ 编写的。
标准化
发布时间 | 通称 | 备注 |
---|---|---|
2017 | C++17 | 第五个 C++ 标准 |
2017 | coroutines TS | 协程库扩展 |
2017 | ranges TS | 提供范围机制 |
2017 | library fundamentals TS | 标准库扩展 |
2016 | concurrency TS | 用于并发计算的扩展 |
2015 | concepts TS | 概念库,用于优化编译期信息 |
2015 | TM TS | 事务性内存操作 |
2015 | parallelism TS | 用于并行计算的扩展 |
2015 | filesystem TS | 文件系统 |
2014 | C++14 | 第四个 C++ 标准 |
2011 | - | 十进制浮点数扩展 |
2011 | C++11 | 第三个 C++ 标准 |
2010 | - | 数学函数扩展 |
2007 | C++TR1 | C++ 技术报告:库扩展 |
2006 | - | C++ 性能技术报告 |
2003 | C++03 | 第二个 C++ 标准 |
1998 | C++98 | 第一个 C++ 标准 |
代码一闪而过的问题所在和解决方法。
一闪而过是因为你的程序没有输入,只有固定的输出。程序会在运行到 return 语句时退出程序。
之前给的三种解决办法我一一讲解:
第一种:
cin.clear(); // 清空缓存
cin.sync(); // 清空缓存
cin.get(); // 接收键盘输入
// From:本帖,"但对于太突然"
这种采用了输入方法来不让程序终止,他会在读入到数据后退出程序(cin.get)。
#include <stdio.h>
int main()
{getchar();return 0;
}
// From:本帖,"xuezy"
这种是采用了输入方法,但不同于上一种的是,这次是使用 getchar 函数获取一个 char 类型,但不将读入的数据存放于任何变量。
#include <stdlib.h>
int main()
{system("pause"); //注意:“system("pause")”;语句会显示“请按任意键继续……”return 0;
}
// From:本帖,"xuezy"
此方法是采用 system() 函数中的 pause 命令进行程序的暂停。
标识符
**定义:**变量、符号常量、函数、数组、类型、文件、标签和其他各种用户定义的对象的名称。
- 第一个字符必须是字母或下划线
- 后续字符可以是字母、数字或下划线
- 标识符的有效长度不超过247字符
- 标识符不能和关键字相同
- 标识符区分大小写
- 最好也不要和系统预定义标识符同名
- 标识符命名要做到“见名知义”
- 应该避免使用可能引起混淆的字母
为什么要使用 using namespace std; ?
有些名字容易冲突,所以会使用命名空间的方式进行区分,具体来说就是加个前缀。比如 C++ 标准库里面定义了 vector 容器,你自己也写了个 vector 类,这样名字就冲突了。于是标准库里的名字都加上 std:: 的前缀,你必须用 std::vector 来引用。同理,你自己的类也可以加个自定义的前缀。
但是经常写全名会很繁琐,所以在没有冲突的情况下你可以偷懒,写一句 using namespace std;,接下去的代码就可以不用写前缀直接写 vector 了。
命名空间(namespace)的意思:
举个例子吧,我们的名字,有很多时候会重名,比如张三。
世界上可能有几十几百个张三。
我们的世界里当然可以用身份证号码来分啦,但在没有身份证的时代怎么分呢?
有办法的,比如,可以在名字前面加上地方名,如
广东的张三
上海的张三
同样的道理,我们在编程时,不同的程序员可能都会喜欢用同样的名字来表示相同的东西,如,大家可能都会用 dog 来表示狗类。好了,现在有好几位程序员都写了 dog 类,可能各自的功能不同或相同。就好比好几家人都养了条狗一样。好了,在程序调用时,编译器傻了,它不知道该拉哪家的狗出来响应呀!
怎么办?哦,让我们在狗前面加上 namespace(名字空间) 以示区分,好了,于是程序中就有了
张三 的 狗
李四 的 狗
啰嗦了一大堆,该说回来了
std::
std 表示是 C++ 的标准命名空间,就是编译系统自带有的,按 C++ 标准定义好了的。
:: 书写格式
比如,在使用输出 std::cout 时,如果它达不到我们想要的效果,我们也可以自己定义一个名字空间。
取名 myspace,再在这个空间里写一个 cout 函数来实现。调用时,就成了 myspace::cout。
块注释符(/…/)是不可以嵌套使用的。
此外,我们还可以使用 #if 0 … #endif 来实现注释,且可以实现嵌套,格式为:
#if 0code
#endif
你可以把 #if 0 改成 #if 1 来执行 code 的代码。
这种形式对程序调试也可以帮助,测试时使用 #if 1 来执行测试代码,发布后使用 #if 0 来屏蔽测试代码。
#if 后可以是任意的条件语句。
下面的代码如果 condition 条件为 true 执行 code1 ,否则执行 code2。
#if conditioncode1
#elsecode2
#endif
查看所有类型字符的信息
下面实例会输出您电脑上各种数据类型的大小。
#include<iostream>
#include <limits>using namespace std; int main()
{ cout << "type: \t\t" << "************size**************"<< endl; cout << "bool: \t\t" << "所占字节数:" << sizeof(bool); cout << "\t最大值:" << (numeric_limits<bool>::max)(); cout << "\t\t最小值:" << (numeric_limits<bool>::min)() << endl; cout << "char: \t\t" << "所占字节数:" << sizeof(char); cout << "\t最大值:" << (numeric_limits<char>::max)(); cout << "\t\t最小值:" << (numeric_limits<char>::min)() << endl; cout << "signed char: \t" << "所占字节数:" << sizeof(signed char); cout << "\t最大值:" << (numeric_limits<signed char>::max)(); cout << "\t\t最小值:" << (numeric_limits<signed char>::min)() << endl; cout << "unsigned char: \t" << "所占字节数:" << sizeof(unsigned char); cout << "\t最大值:" << (numeric_limits<unsigned char>::max)(); cout << "\t\t最小值:" << (numeric_limits<unsigned char>::min)() << endl; cout << "wchar_t: \t" << "所占字节数:" << sizeof(wchar_t); cout << "\t最大值:" << (numeric_limits<wchar_t>::max)(); cout << "\t\t最小值:" << (numeric_limits<wchar_t>::min)() << endl; cout << "short: \t\t" << "所占字节数:" << sizeof(short); cout << "\t最大值:" << (numeric_limits<short>::max)(); cout << "\t\t最小值:" << (numeric_limits<short>::min)() << endl; cout << "int: \t\t" << "所占字节数:" << sizeof(int); cout << "\t最大值:" << (numeric_limits<int>::max)(); cout << "\t最小值:" << (numeric_limits<int>::min)() << endl; cout << "unsigned: \t" << "所占字节数:" << sizeof(unsigned); cout << "\t最大值:" << (numeric_limits<unsigned>::max)(); cout << "\t最小值:" << (numeric_limits<unsigned>::min)() << endl; cout << "long: \t\t" << "所占字节数:" << sizeof(long); cout << "\t最大值:" << (numeric_limits<long>::max)(); cout << "\t最小值:" << (numeric_limits<long>::min)() << endl; cout << "unsigned long: \t" << "所占字节数:" << sizeof(unsigned long); cout << "\t最大值:" << (numeric_limits<unsigned long>::max)(); cout << "\t最小值:" << (numeric_limits<unsigned long>::min)() << endl; cout << "double: \t" << "所占字节数:" << sizeof(double); cout << "\t最大值:" << (numeric_limits<double>::max)(); cout << "\t最小值:" << (numeric_limits<double>::min)() << endl; cout << "long double: \t" << "所占字节数:" << sizeof(long double); cout << "\t最大值:" << (numeric_limits<long double>::max)(); cout << "\t最小值:" << (numeric_limits<long double>::min)() << endl; cout << "float: \t\t" << "所占字节数:" << sizeof(float); cout << "\t最大值:" << (numeric_limits<float>::max)(); cout << "\t最小值:" << (numeric_limits<float>::min)() << endl; cout << "size_t: \t" << "所占字节数:" << sizeof(size_t); cout << "\t最大值:" << (numeric_limits<size_t>::max)(); cout << "\t最小值:" << (numeric_limits<size_t>::min)() << endl; cout << "string: \t" << "所占字节数:" << sizeof(string) << endl; // << "\t最大值:" << (numeric_limits<string>::max)() << "\t最小值:" << (numeric_limits<string>::min)() << endl; cout << "type: \t\t" << "************size**************"<< endl; return 0;
}
本实例使用了 endl,这将在每一行后插入一个换行符,<< 运算符用于向屏幕传多个值。我们也使用 sizeof() 函数来获取各种数据类型的大小。
当上面的代码被编译和执行时,它会产生以下的结果,结果会根据所使用的计算机而有所不同:
type: ************size**************
bool: 所占字节数:1 最大值:1 最小值:0
char: 所占字节数:1 最大值: 最小值:?
signed char: 所占字节数:1 最大值: 最小值:?
unsigned char: 所占字节数:1 最大值:? 最小值:
wchar_t: 所占字节数:4 最大值:2147483647 最小值:-2147483648
short: 所占字节数:2 最大值:32767 最小值:-32768
int: 所占字节数:4 最大值:2147483647 最小值:-2147483648
unsigned: 所占字节数:4 最大值:4294967295 最小值:0
long: 所占字节数:8 最大值:9223372036854775807 最小值:-9223372036854775808
unsigned long: 所占字节数:8 最大值:18446744073709551615 最小值:0
double: 所占字节数:8 最大值:1.79769e+308 最小值:2.22507e-308
long double: 所占字节数:16 最大值:1.18973e+4932 最小值:3.3621e-4932
float: 所占字节数:4 最大值:3.40282e+38 最小值:1.17549e-38
size_t: 所占字节数:8 最大值:18446744073709551615 最小值:0
string: 所占字节数:24
type: ************size**************
C++ 数据类型、typedef 声明和define的区别、枚举的使用例子
C++ 中的变量声明
变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。
当您使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用。您可以使用 extern 关键字在任何地方声明一个变量。虽然您可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。
尝试下面的实例,其中,变量在头部就已经被声明,但它们是在主函数内被定义和初始化的:
#include <iostream>
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 ;f = 70.0/3.0;cout << f << endl ;return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
30
23.3333
同样的,在函数声明时,提供一个函数名,而函数的实际定义则可以在任何地方进行。例如:
// 函数声明
int func();int main()
{// 函数调用int i = func();
}// 函数定义
int func()
{return 0;
}
变量的类型间是可以互相转换的,转换又分为自动转换和强制转换。
自动转换规则:
- 1、若参与运算量的类型不同,则先转换成同一类型,然后进行运算。
- 2、转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。 a、若两种类型的字节数不同,转换成字节数高的类型 b、若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型
- 3、所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
- 4、char型和short型参与运算时,必须先转换成int型。
- 5、在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度:
int a=1;
double b=2.5;
a=b;
cout << a; //输出为 2,丢失小数部分
int a = 1;
double b = 2.1;
cout << "a + b = " << a + b << endl; //输出为a + b = 3.1
强制转换规则:
强制类型转换是通过类型转换运算来实现的。其一般形式为:(类型说明符)(表达式)其功能是把表达式的运算结果强制转换成类型说明符所表示的类型
int a = 1;
double b = 2.1;
cout << "a + b = " << a + (int)b << endl; //输出为a + b = 3
定义包含了声明,但是声明不包含定义,如
int a = 0; //定义并声明了变量 a
extern int a; //只是声明了有一个变量 a 存在,具体 a 在哪定义的,需要编译器编译的时候去找。
函数也是类似,定义的时候同时声明。但如果只是声明,编译器只知道有这么个函数,具体函数怎么定义的要编译器去找。
void fun1(); //函数声明void fun1(){ //函数定义cout<<"fun1"<<endl;
}
C/C++ 编译 cpp 文件是从上往下编译,所以 main 函数里面调用其他函数时,如果其他函数在 main 函数的下面,则要在 main 函数上面先声明这个函数。
或者把 main 函数放在最下面,这个不仅限于 main 函数,其他函数的调用都是如此。被调用的函数要在调用的函数之前声明。
extern 关键字声明在变量和函数之前的说明。
1、作用在变量之前
变量只允许定义一次,但可以在多个文件中声明。
Test.cpp 中:
int s32Val = 0; // 定义一个变量 s32Val,并赋初值为 0
Test1.cpp 中:
extern int s32Val; // 声明变量 s32Val,它在 Test.cpp 中被定义,此处不可赋值
Test2.cpp 中:
extern int s32Val; // 声明变量 s32Val,它在 Test.cpp 中被定义,此处不可赋值
2、作用在函数之前
Test.h:
extern void Fun(); // 函数声明,extern 用于标识次函数为外部可调用函数
Test.cpp:
void Fun(); // 函数定义
C++ 全局变量、局部变量、静态全局变量、静态局部变量的区别
C++ 变量根据定义的位置的不同的生命周期,具有不同的作用域,作用域可分为 6 种:全局作用域,局部作用域,语句作用域,类作用域,命名空间作用域和文件作用域。
从作用域看:
全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern 关键字再次声明这个全局变量。
静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
静态全局变量也具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
从分配内存空间看:
全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间。
全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
- 1)、静态变量会被放在程序的静态数据存储区(数据段)(全局可见)中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
- 2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。
Tips:
- A、若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
- B、若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
- C、设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题,因为他们都放在静态数据存储区,全局可见;
- D、如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带“内部存储器”功能的的函数)
- E、函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
-----------------------------------------------------------------------------------------------------------
static 全局变量:改变作用范围,不改变存储位置
static 局部变量:改变存储位置,不改变作用范围
静态函数 :在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。
如果在一个源文件中定义的函数,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用,这种函数也称为内部函数。定义一个内部函数,只需在函数类型前再加一个“static”关键字即可。
字符常量
字符常量是括在单引号中。如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L’x’),此时它必须存储在 wchar_t 类型的变量中。否则,它就是一个窄字符常量(例如 ‘x’),此时它可以存储在 char 类型的简单变量中。
字符常量可以是一个普通的字符(例如 ‘x’)、一个转义序列(例如 ‘\t’),或一个通用的字符(例如 ‘\u02C0’)。
在 C++ 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码:
转义序列 | 含义 |
---|---|
\ | \ 字符 |
’ | ’ 字符 |
" | " 字符 |
? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数 |
下面的实例显示了一些转义序列字符:
#include <iostream>
using namespace std;int main()
{cout << "Hello\tWorld\n\n";return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Hello World
宏定义 #define 和常量 const 的区别
类型和安全检查不同
宏定义是字符替换,没有数据类型的区别,同时这种替换没有类型安全检查,可能产生边际效应等错误;
const常量是常量的声明,有类型区别,需要在编译阶段进行类型检查
编译器处理不同
宏定义是一个"编译时"概念,在预处理阶段展开,不能对宏定义进行调试,生命周期结束与编译时期;
const常量是一个"运行时"概念,在程序运行使用,类似于一个只读行数据
存储方式不同
宏定义是直接替换,不会分配内存,存储与程序的代码段中;
const常量需要进行内存分配,存储与程序的数据段中
定义域不同
void f1 ()
{#define N 12const int n 12;
}
void f2 ()
{cout<<N <<endl; //正确,N已经定义过,不受定义域限制cout<<n <<endl; //错误,n定义域只在f1函数中
}
定义后能否取消
宏定义可以通过#undef来使之前的宏定义失效
const常量定义后将在定义域内永久有效
void f1()
{#define N 12const int n = 12;#undef N //取消宏定义后,即使在f1函数中,N也无效了#define N 21//取消后可以重新定义
}
是否可以做函数参数
宏定义不能作为参数传递给函数
const常量可以在函数的参数列表中出现
常量实例:已知半径,求圆的周长和面积。
#include<iostream>
using namespace std;#define PI 3.14int main()
{float radius = 10.0;float area = PI * radius * radius;float circumference = 2 * PI * radius;cout << "area is "<<area <<", circumference is "<< circumference<<endl;return 0;
}
修饰指针
修饰指针的情况比较多,主要有以下几种情况:
1、const 修饰 *p,指向的对象只读,指针的指向可变:
int a = 9;
int b = 10;
const int *p = &a;//p是一个指向int类型的const值,与int const *p等价
*p = 11; //编译错误,指向的对象是只读的,不可通过p进行改变
p = &b; //合法,改变了p的指向
这里为了便于理解,可认为const修饰的是 *p,通常使用 ***** 对指针进行解引用来访问对象,因而,该对象是只读的。
2、const 修饰 p,指向的对象可变,指针的指向不可变:
int a = 9;
int b = 10;
int * const p = &a;//p是一个const指针
*p = 11; //合法,
p = &b; //编译错误,p是一个const指针,只读,不可变
3、指针不可改变指向,指向的内容也不可变
int a = 9;
int b = 10;
const int * const p = &a;//p既是一个const指针,同时也指向了int类型的const值
*p = 11; //编译错误,指向的对象是只读的,不可通过p进行改变
p = &b; //编译错误,p是一个const指针,只读,不可变
看完上面几种情况之后是否会觉得混乱,并且难以记忆呢?我们使用一句话总结:
const 放在 * 的左侧任意位置,限定了该指针指向的对象是只读的;const放在 * 的右侧,限定了指针本身是只读的,即不可变的。
如果还不是很好理解,我们可以这样来看,去掉类型说明符,查看 const修饰的内容,上面三种情况去掉类型说明符 int 之后,如下:
const *p; //修饰*p,指针指向的对象不可变
* const p; //修饰p,指针不可变
const * const p; //第一个修饰了*p,第二个修饰了p,两者都不可变
const 右边修饰谁,就说明谁是不可变的。上面的说法仅仅是帮助理解和记忆。借助上面这种理解,就会发现以下几种等价情况:
const int NUM = 10; //与int const NUM等价
int a = 9;
const int *p = &a;//与int const *p等价
const int arr[] = {0,0,2,3,4}; //与int const arr[]等价
对于无符号化为有符号的位数运算,采取 N-2^n 的计算方法,n 取决于定义的数据类型 int、short、char、long int 等等,N 为无符号数的数值,例如文中的 N=50000,short 为 16 位,计算方法为 50000-2^16 得到 -15536。
16 位整数(短整数)的情况下,十进制 50000 就是二进制 11000011 01010000 但在有符号的情况下,二进制最左边的 1,代表这整个数字是负数但是电脑是以补码形式来表示数字的,要获得原本的数字,首先要把整个二进制数 - 11100001101010000 - 1 = 1100001101001111 然后,在把答案取反码 not 1100001101001111 = 0011110010110000 把最终答案变成十进制,就是 15536 所以,一开始的二进制数 11000011 01010000,在有符号的情况下代表的就是 -15536。
来自网友牢记圣光的笔记:
//上边的例子的二进制数为下边的运行结果,看到二进制就很好理解了!#include <iostream>
#include <cstdlib>using namespace std;int main()
{short int i; // 有符号短整数short unsigned int j; // 无符号短整数j = 50000;i = j;cout <<"i:" <<i<<'\n'<< "j:" << j<<endl;char s[40];_itoa_s(i, s, 2);printf("变量i的二进制数为:%s\n", s);_itoa_s(j, s, 2);printf("变量j的二进制数为:%s\n", s);return 0;
}
结果为:
i:-15536
j:50000
变量i的二进制数为:11111111111111111100001101010000
变量j的二进制数为:1100001101010000
std::cout << "Count is " << count << std::endl;
在前面的学习中我们看到的输出没有 std::,而这一节出现了上面的代码。
std 是标准库函数使用的命名空间,是 standard(标准)的缩写。
using namespace std ,它声明了命名空间 std,后续如果有未指定命名空间的符号,那么默认使用 std,这样就可以使用 cin、cout、vector 等。
假设你不使用预处理 using namespace std;,就要加上 std::cin 或者 std::cout。
cin 用于从控制台获取用户输入,cout 用于将数据输出到控制台。
cin 是输入流对象,cout 是输出流对象,它们分别可以用 >> 和 <<,是因为分别在其类中对相应运算符进行了重载。
静态局部变量
在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。 我们先举一个静态局部变量的例子,如下:
#include <iostream>void fn();
int main()
{fn();fn();fn();
}
void fn()
{static int n=10;std::cout<<n<<std::endl;n++;
}
通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。但有时候我们需要在两次调用之间对变量的值进行保存。
通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。
静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。
静态局部变量有以下特点:
- 该变量在全局数据区分配内存;
- 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
- 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
- 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
C++ 运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C++ 内置了丰富的运算符,并提供了以下类型的运算符:
C++ 中的运算符优先级
运算符的优先级确定表达式中项的组合。这会影响到一个表达式如何计算。某些运算符比其他运算符有更高的优先级,例如,乘除运算符具有比加减运算符更高的优先级。
例如 x = 7 + 3 * 2,在这里,x 被赋值为 13,而不是 20,因为运算符 * 具有比 + 更高的优先级,所以首先计算乘法 3*2,然后再加上 7。
下表将按运算符优先级从高到低列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面。在表达式中,较高优先级的运算符会优先被计算。
类别 | 运算符 | 结合性 |
---|---|---|
后缀 | () [] -> . ++ - - | 从左到右 |
一元 | + - ! ~ ++ - - (type)* & sizeof | 从右到左 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> | 从左到右 |
关系 | < <= > >= | 从左到右 |
相等 | == != | 从左到右 |
位与 AND | & | 从左到右 |
位异或 XOR | ^ | 从左到右 |
位或 OR | | | 从左到右 |
逻辑与 AND | && | 从左到右 |
逻辑或 OR | || | 从左到右 |
条件 | ?: | 从右到左 |
赋值 | = += -= *= /= %=>>= <<= &= ^= |= | 从右到左 |
逗号 | , | 从左到右 |
实例
请看下面的实例,了解 C++ 中运算符的优先级。
复制并黏贴下面的 C++ 程序到 test.cpp 文件中,编译并运行程序。
对比有括号和没有括号时的区别,这将产生不同的结果。因为 ()、 /、 * 和 + 有不同的优先级,高优先级的操作符将优先计算。
#include <iostream>
using namespace std;int main()
{int a = 21;int b = 10;int c;c = a + b;cout << "Line 1 - c 的值是 " << c << endl ;c = a - b;cout << "Line 2 - c 的值是 " << c << endl ;c = a * b;cout << "Line 3 - c 的值是 " << c << endl ;c = a / b;cout << "Line 4 - c 的值是 " << c << endl ;c = a % b;cout << "Line 5 - c 的值是 " << c << endl ;int d = 10; // 测试自增、自减c = d++;cout << "Line 6 - c 的值是 " << c << endl ;d = 10; // 重新赋值c = d--;cout << "Line 7 - c 的值是 " << c << endl ;return 0;
}
当上面的代码被编译和执行时,它会产生以下结果:
(a + b) * c / d 的值是 90
((a + b) * c) / d 的值是 90
(a + b) * (c / d) 的值是 90
a + (b * c) / d 的值是 50
关于逻辑运算符 && ,|| 的巧用方式
逻辑与 &&
&& 会先判断左边的值是否为真。
如果为假,那么整个表达式毫无疑问也为假。
如果为真,那就还需要判断右值,才能知道整个式子的值。
这个时候判断右值的过程就起了一个if的作用,可以利用这个过程判断右边表达式是否为真。
下面代码:
/*不用任何循环语句,不用if,来实现1+2+3+...+10的值*/
#include <iostream>
using namespace std;int add(int c)
{int a=0;c&&(a=add(c-1));//递归循环,直到传入c的值为0则结束循环cout<<c+a<<endl;return c+a;
}
int main()
{ add(10);return 0;
}
C++ 中的 --> 操作符?****–> 并不是一个操作符,实际上它是两个独立的操作符:– 和 >。
以下代码中我们设置了一个 x 自减的条件运算符代码,在 x 的进行自减运算前,会先使用比较符号 > 与右边表达式 0 进行比较,然后返回结果再进行自减运算:
while (x --> 0)// 相等于以下代码
while( (x--) > 0 )
// 把两个运算符分开更好理解了while (x-- > 0)
实例 1:输出 0 到 9 的 整数
#include <stdio.h>
int main()
{int x = 10;while (x --> 0) {printf("%d ", x);}
}
输出结果:
9 8 7 6 5 4 3 2 1 0
实例 2: 输出 大于 0 小于 10 的偶数
#include <stdio.h>
int main()
{int x = 10;while( 0 <---- x ){printf("%d ", x);}
}
输出结果:
8 6 4 2
实例 3
#include <stdio.h>
int main()
{int x = 100;while( 0 <-------------------- x ){printf("%d ", x);}
}
输出结果:
90 80 70 60 50 40 30 20 10
for循环使用实例——输出菱形
? : 运算符
我们已经在前面的章节中讲解了 条件运算符 ? :,可以用来替代 if…else 语句。它的一般形式如下:
Exp1 ? Exp2 : Exp3;
其中,Exp1、Exp2 和 Exp3 是表达式。请注意,冒号的使用和位置。
? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ? 表达式的值。
Lambda 函数与表达式
C++ 数学运算
在 C++ 中,除了可以创建各种函数,还包含了各种有用的函数供您使用。这些函数写在标准 C 和 C++ 库中,叫做内置函数。您可以在程序中引用这些函数。
C++ 内置了丰富的数学函数,可对各种数字进行运算。下表列出了 C++ 中一些有用的内置的数学函数。
为了利用这些函数,您需要引用数学头文件 ****。
序号 | 函数 & 描述 |
---|---|
1 | double cos(double); 该函数返回弧度角(double 型)的余弦。 |
2 | double sin(double); 该函数返回弧度角(double 型)的正弦。 |
3 | double tan(double); 该函数返回弧度角(double 型)的正切。 |
4 | double log(double); 该函数返回参数的自然对数。 |
5 | double pow(double, double); 假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。 |
6 | double hypot(double, double); 该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。 |
7 | double sqrt(double); 该函数返回参数的平方根。 |
8 | int abs(int); 该函数返回整数的绝对值。 |
9 | double fabs(double); 该函数返回任意一个浮点数的绝对值。 |
10 | double floor(double); 该函数返回一个小于或等于传入参数的最大整数。 |
下面是一个关于数学运算的简单实例:
#include <iostream>
#include <cmath>
using namespace std;int main ()
{// 数字定义short s = 10;int i = -1000;long l = 100000;float f = 230.47;double d = 200.374;// 数学运算cout << "sin(d) :" << sin(d) << endl;cout << "abs(i) :" << abs(i) << endl;cout << "floor(d) :" << floor(d) << endl;cout << "sqrt(f) :" << sqrt(f) << endl;cout << "pow( d, 2) :" << pow(d, 2) << endl;return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
sin(d) :-0.634939
abs(i) :1000
floor(d) :200
sqrt(f) :15.1812
pow( d, 2 ) :40149.7
C++ 随机数
在许多情况下,需要生成随机数。关于随机数生成器,有两个相关的函数。一个是 rand(),该函数只返回一个伪随机数。生成随机数之前必须先调用 srand() 函数。
下面是一个关于生成随机数的简单实例。实例中使用了 time() 函数来获取系统时间的秒数,通过调用 rand() 函数来生成随机数:
srand函数是随机数发生器的初始化函数。
原型: void srand(unsigned seed);
**用法:**它需要提供一个种子,这个种子会对应一个随机数,如果使用相同的种子后面的rand()函数会出现一样的随机数。如: srand(1); 直接使用 1 来初始化种子。不过为了防止随机数每次重复,常常使用系统时间来初始化,即使用 time 函数来获得系统时间,它的返回值为从 00:00:00 GMT, January 1, 1970 到现在所持续的秒数,然后将 time_t 型数据转化为(unsigned)型再传给 srand 函数,即: srand((unsigned) time(&t)); 还有一个经常用法,不需要定义time_t型t变量,即: srand((unsigned) time(NULL)); 直接传入一个空指针,因为你的程序中往往并不需要经过参数获得的t数据。
#include <iostream>
#include <ctime>
#include <cstdlib>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;
}
当上面的代码被编译和执行时,它会产生下列结果:
随机数: 1748144778
随机数: 630873888
随机数: 2134540646
随机数: 219404170
随机数: 902129458
随机数: 920445370
随机数: 1319072661
随机数: 257938873
随机数: 1256201101
随机数: 580322989
使用随机数来发红包
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <iomanip>
#include <math.h>using namespace std;int main()
{int i, number;int best;//手气最佳float total;cout << "请输入红包金额:";cin >> total;cout << "请输入抢红包人数:";cin >> number;/* 生成随机数 */// 设置种子srand((unsigned)time(NULL));float a[1024];//保存每个人的随机数。最多支持1024个人抢红包。float b[1024];//保存每个人获得的红包金额。float suma = 0;//随机数总和。float sumb = 0;//红包总和。int max = 0;for (i = 0; i < number; i++){// 生成实际的随机数a[i] = rand() % 100;if (a[i] > max){max = a[i];best = i;//获取手气最佳}suma += a[i];}for (i = 0; i < number - 1; i++){b[i] = a[i] / suma * total;//按照随机数计算每个人实际获得的金额sumb += round(b[i] * 100) / 100.0;//将红包金额保留两位小数//输出信息cout << "第" << setiosflags(ios::right)<< setw(3) << i + 1 << "个人的红包是:" << setiosflags(ios::right) << setw(6) << setiosflags(ios::fixed) << setprecision(2) << round(b[i] * 100) / 100.0 ;if (best == i){cout << "(手气最佳)" << endl;}else {cout << endl;}}//最后一人的红包金额等于总金额减去前面的金额。cout << "第" << setiosflags(ios::right)<<setw(3) << number << "个人的红包是:" <<setiosflags(ios::right) << setw(6) << setiosflags(ios::fixed) <<setprecision(2) << round((total - sumb) * 100) / 100.0;if (best == number - 1){cout << "(手气最佳)" << endl;}else {cout << endl;}return 0;
}
Array 直接初始化
char 数组是特殊的,这种初始化要记得字符是以一个 null 结尾的。
实例
char a1[] = {'C', '+', '+'}; // 初始化,没有 nullchar a2[] = {'C', '+', '+', '\0'}; // 初始化,明确有 nullchar a3[] = "C++"; // null 终止符自动添加
const char a4[6] = "runoob"; // 报错,没有 null 的位置
a4 是错误的,虽然 a4 包括 6 个直接字符,但是 array 大小是 7:6个字符 + 一个null。正确的是:
const char a4[7] = "runoob";
setw控制输出间隔
在C++中,setw(int n)用来控制输出间隔,(n-1个空格)。
setw()默认填充的内容为空格,可以setfill()配合使用设置其他字符填充。
cout<<setfill('*')<<setw(5)<<'a'<<endl;
则输出:
****a //4个*和字符a共占5个位置。
Vector(向量):
C++ 中的一种数据结构,确切的说是一个类。它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的。
用法:
1.文件包含:
首先在程序开头处加上 #include 以包含所需要的类文件 vector。
还有一定要加上 using namespace std;
2.变量声明:
2.1 例: 声明一个 int 向量以替代一维的数组: vector a; (等于声明了一个 int 数组 a[],大小没有指定,可以动态的向里面添加删除)。
2.2 例: 用 vector 代替二维数组.其实只要声明一个一维数组向量即可,而一个数组的名字其实代表的是它的首地址,所以只要声明一个地址的向量即可,即: vector a 。同理想用向量代替三维数组也是一样,vector a; 再往上面依此类推。
3.具体的用法以及函数调用:
3.1 得到向量中的元素和数组一样,例如:
vector <int *> a
int b = 5;
a.push_back(b);//该函数下面有详解
cout<<a[0]; //输出结果为5
使用数组给向量赋值:
vector<int> v( a, a+sizeof(a)/sizeof(a[0]) );
或者:
int a[]={1,2,3,4,5,6,7,8,9};
typedef vector<int> vec_int;
vec_int vecArray(a,a+9);
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;如果 s1<s2 则返回值小于 0;如果 s1>s2 则返回值大于 0。 |
5 | strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
下面的实例使用了上述的一些函数:
实例
#include <iostream>
#include <cstring>using namespace std;int main ()
{char str1[11] = "Hello";char str2[11] = "World";char str3[11];int len ;// 复制 str1 到 str3strcpy( str3, str1);cout << "strcpy( str3, str1) : " << str3 << endl;// 连接 str1 和 str2strcat( str1, str2);cout << "strcat( str1, str2): " << str1 << endl;// 连接后,str1 的总长度len = strlen(str1);cout << "strlen(str1) : " << len << endl;return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
strcpy( str3, str1) : Hello
strcat( str1, str2): HelloWorld
strlen(str1) : 10
字符串
- \1. append() – 在字符串的末尾添加字符
- \2. find() – 在字符串中查找字符串
- \4. insert() – 插入字符
- \5. length() – 返回字符串的长度
- \6. replace() – 替换字符串
- \7. substr() – 返回某个子字符串
- \8. …
#include <iostream>
#include <string>
using namespace std;int main()
{//定义一个string类对象string http = "www.runoob.com";//打印字符串长度cout<<http.length()<<endl;//拼接http.append("/C++");cout<<http<<endl; //打印结果为:www.runoob.com/C++//删除int pos = http.find("/C++"); //查找"C++"在字符串中的位置cout<<pos<<endl;http.replace(pos, 4, ""); //从位置pos开始,之后的4个字符替换为空,即删除cout<<http<<endl;//找子串runoobint first = http.find_first_of("."); //从头开始寻找字符'.'的位置int last = http.find_last_of("."); //从尾开始寻找字符'.'的位置cout<<http.substr(first+1, last-first-1)<<endl; //提取"runoob"子串并打印return 0;
}
关于字符数组为什么可以以数组名来用cout输出数组内容,而普通数组不行。
int a[10] = {1,2,3,6,7};
char b[6] = {'h','a','p','p','y','\0'};
char c[] = "happy";
cout<<a<<endl;
cout<<b<<endl;
cout<<c<<endl;
0x22fe6c
happy
happy
从以上范例可以看出,普通数组中以数组名用cout来输出,只会得到一串地址;用字符数组则会输出数组中的内容。
C++输入
C++ 中常见的几种输入字符串的方法如下:
cin、cin.get()、cin.getline()、getline()、gets()、getchar()
1. cin>>
用法一:最常用、最基本的用法,输入一个数字:
#include <iostream>
using namespace std;
int main ()
{int a,b;cin>>a>>b;cout<<a+b<<endl;
}//输入:2[回车]3[回车]
//输出:5
用法二:接受一个字符串,遇“空格”、“Tab”、“回车”都结束
#include <iostream>
using namespace std;
int main ()
{char a[20];cin>>a;cout<<a<<endl;
}//输入:jkljkljkl
//输出:jkljkljkl//输入:jkljkl jkljkl //遇空格结束,所以不能输入多个单词
//输出:jkljkl
2. cin.get()
用法一:cin.get(字符变量名)可以用来接收字符
#include <iostream>
using namespace std;
int main ()
{
char ch;
ch=cin.get(); //或者cin.get(ch);只能获取一个字符
cout<<ch<<endl;
}//输入:jljkljkl
//输出:j
用法二:cin.get(字符数组名,接收字符数)用来接收一行字符串,可以接收空格
#include <iostream>
using namespace std;
int main ()
{
char a[20];
cin.get(a,20); //有些类似getline。可以输入多个单词,中间空格隔开。
cout<<a<<endl;
}//输入:jkl jkl jkl
//输出:jkl jkl jkl//输入:abcdeabcdeabcdeabcdeabcde (输入25个字符)
//输出:abcdeabcdeabcdeabcd (接收19个字符+1个'\0')
用法三:cin.get(无参数)没有参数主要是用于舍弃输入流中的不需要的字符, 或者舍弃回车, 弥补cin.get(字符数组名,接收字符数目)的不足.
#include <iostream>
using namespace std;int main(void)
{char arr[10];cin.get(arr,10);cin.get();//用于吃掉回车,相当于getchar();cout<<arr<<endl;cin.get(arr,5);cout<<arr<<endl;system("pause");return 0;
}//输入abcdefghi
//输出abcdefghi
//输入abcde
//输出abcd
//请按任意键继续
#include <iostream>
using namespace std;int main(void)
{char arr[10];cin.get(arr,10);//cin.get();//用于吃掉回车,相当于getchar(); 现在把这行注释掉试试看cout<<arr<<endl;cin.get(arr,5);cout<<arr<<endl;system("pause");return 0;
}//输入abcdefghi
//输出abcdefghi
//请按任意键继续
3.cin.getline()
cin.getline(): 接受一个字符串,可以接收空格并输出
#include <iostream>
using namespace std;
int main ()
{
char m[20];
cin.getline(m,5); //与上面基本相同。
cout<<m<<endl;
}//输入:jkljkljkl
//输出:jklj
接受5个字符到m中,其中最后一个为’\0’,所以只看到4个字符输出;
如果把5改成20:
输入:jkljkljkl
输出:jkljkljkl输入:jklf fjlsjf fjsdklf
输出:jklf fjlsjf fjsdklf
延伸:
cin.getline()实际上有三个参数,cin.getline(接受字符串到m,接受个数5,结束字符)
当第三个参数省略时,系统默认为’\0’ 是‘/n’吧。
如果将例子中cin.getline()改为cin.getline(m,5,‘a’);当输入jlkjkljkl时输出jklj,输入jkaljkljkl时,输出jk
当用在多维数组中的时候,也可以用cin.getline(m[i],20)之类的用法:
#include<iostream>
#include<string>
using namespace std;int main ()
{
char m[3][20];
for(int i=0;i<3;i++)
{
cout<<"\n请输入第"<<i+1<<"个字符串:"<<endl;
cin.getline(m[i],20);
}cout<<endl;
for(int j=0;j<3;j++)
cout<<"输出m["<<j<<"]的值:"<<m[j]<<endl;}
测试:
请输入第1个字符串:
kskr1请输入第2个字符串:
kskr2请输入第3个字符串:
kskr3输出m[0]的值:kskr1
输出m[1]的值:kskr2
输出m[2]的值:kskr3
4. getline()
getline() :接受一个字符串,可以接收空格并输出,需包含 #include。
#include<iostream>
#include<string>
using namespace std;
int main ()
{string str;getline(cin,str);cout<<str<<endl;
}
测试:
输入:jkljkljkl //VC6中有个bug,需要输入两次回车。
输出:jkljkljkl输入:jkl jfksldfj jklsjfl
输出:jkl jfksldfj jklsjfl
和cin.getline()类似,但是cin.getline()属于istream流,而getline()属于string流,是不一样的两个函数
5. gets()
gets(): 接受一个字符串,可以接收空格并输出,需包含 #include。
#include<iostream>
#include<string>
using namespace std;
int main ()
{char m[20];gets(m); //不能写成m=gets();cout<<m<<endl;
}
测试:
输入:jkljkljkl
输出:jkljkljkl输入:jkl jkl jkl
输出:jkl jkl jkl
类似cin.getline()里面的一个例子,gets()同样可以用在多维数组里面:
#include<iostream>
#include<string>
using namespace std;int main ()
{char m[3][20];for(int i=0;i<3;i++){cout<<"\n请输入第"<<i+1<<"个字符串:"<<endl;gets(m[i]);}cout<<endl;for(int j=0;j<3;j++)cout<<"输出m["<<j<<"]的值:"<<m[j]<<endl;
}
测试:
请输入第1个字符串:
kskr1请输入第2个字符串:
kskr2请输入第3个字符串:
kskr3输出m[0]的值:kskr1
输出m[1]的值:kskr2
输出m[2]的值:kskr3
自我感觉gets()和cin.getline()的用法很类似,只不过cin.getline()多一个参数罢了;
这里顺带说明一下,对于本文中的这个kskr1,kskr2,kskr3的例子,对于cin>>也可以适用,原因是这里输入的没有空格,如果输入了空格,比如“ks kr jkl[回车]”那么cin就会已经接收到3个字符串,“ks,kr,jkl”;再如“kskr 1[回车]kskr 2[回车]”,那么则接收“kskr,1,kskr”;这不是我们所要的结果!而cin.getline()和gets()因为可以接收空格,所以不会产生这个错误;
6.getchar()
getchar() :接受一个字符,需包含 #include。
#include<iostream>
using namespace std;
int main ()
{char ch;ch=getchar(); //不能写成getchar(ch);cout<<ch<<endl;
}
测试:
输入:jkljkljkl
输出:j
getchar()是C语言的函数,C++也可以兼容,但是尽量不用或少用;
C++ 中使用指针
使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 ***** 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:
#include <iostream>using namespace std;int main ()
{int var = 20; // 实际变量的声明int *ip; // 指针变量的声明ip = &var; // 在指针变量中存储 var 的地址cout << "Value of var variable: ";cout << var << endl;// 输出在指针变量中存储的地址cout << "Address stored in ip variable: ";cout << ip << endl;// 访问指针中地址的值cout << "Value of *ip variable: ";cout << *ip << endl;return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Value of var variable: 20
Address stored in ip variable: 0xbfc601ac
Value of *ip variable: 20
& 符号的意思是取地址,也就是返回一个对象在内存中的地址。***** 符号的意思是取得一个指针所指向的对象。 也就是如果一个指针保存着一个内存地址,那么它就返回在那个地址的对象。简单点就是:&:取址。***** :取值。
在这里也只是想着验证一下 引用是否可以改变这个原来的被引用变量里存的值,最后也确实是可以改变。
用火影忍者里的分身法作比喻:
- 指针其实更像是普通的分身法,即使是分身破灭,对自身也没有影响;
- 引用更像是多重影分身,多重影分身发生了变化,本身也会受到影响。
C++ 日期 & 时间
C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 头文件。
有四个与时间相关的类型:clock_t、time_t、size_t 和 tm。类型 clock_t、size_t 和 time_t 能够把系统时间和日期表示为某种整数。
struct tm {int tm_sec; // 秒,正常范围从 0 到 59,但允许至 61int tm_min; // 分,范围从 0 到 59int tm_hour; // 小时,范围从 0 到 23int tm_mday; // 一月中的第几天,范围从 1 到 31int tm_mon; // 月,范围从 0 到 11int tm_year; // 自 1900 年起的年数int tm_wday; // 一周中的第几天,范围从 0 到 6,从星期日算起int tm_yday; // 一年中的第几天,范围从 0 到 365,从 1 月 1 日算起int tm_isdst; // 夏令时
}
输入输出流中的函数(模板):
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{cout<<setiosflags(ios::left|ios::showpoint); // 设左对齐,以一般实数方式显示cout.precision(5); // 设置除小数点外有五位有效数字 cout<<123.456789<<endl;cout.width(10); // 设置显示域宽10 cout.fill('*'); // 在显示区域空白处用*填充cout<<resetiosflags(ios::left); // 清除状态左对齐cout<<setiosflags(ios::right); // 设置右对齐cout<<123.456789<<endl;cout<<setiosflags(ios::left|ios::fixed); // 设左对齐,以固定小数位显示cout.precision(3); // 设置实数显示三位小数cout<<999.123456<<endl; cout<<resetiosflags(ios::left|ios::fixed); //清除状态左对齐和定点格式cout<<setiosflags(ios::left|ios::scientific); //设置左对齐,以科学技术法显示 cout.precision(3); //设置保留三位小数cout<<123.45678<<endl;return 0;
}
测试输出结果:
123.46
****123.46
999.123
1.235e+02
其中 cout.setf 跟 setiosflags 一样,cout.precision 跟 setprecision 一样,cout.unsetf 跟 resetiosflags 一样。
setiosflags(ios::fixed) 固定的浮点显示
setiosflags(ios::scientific) 指数表示
setiosflags(ios::left) 左对齐
setiosflags(ios::right) 右对齐
setiosflags(ios::skipws 忽略前导空白
setiosflags(ios::uppercase) 16进制数大写输出
setiosflags(ios::lowercase) 16进制小写输出
setiosflags(ios::showpoint) 强制显示小数点
setiosflags(ios::showpos) 强制显示符号
cout.setf 常见的标志:
标志 | 功能 |
---|---|
boolalpha | 可以使用单词”true”和”false”进行输入/输出的布尔值. |
oct | 用八进制格式显示数值. |
dec | 用十进制格式显示数值. |
hex | 用十六进制格式显示数值. |
left | 输出调整为左对齐. |
right | 输出调整为右对齐. |
scientific | 用科学记数法显示浮点数. |
fixed | 用正常的记数方法显示浮点数(与科学计数法相对应). |
showbase | 输出时显示所有数值的基数. |
showpoint | 显示小数点和额外的零,即使不需要. |
showpos | 在非负数值前面显示”+(正号)”. |
skipws | 当从一个流进行读取时,跳过空白字符(spaces, tabs, newlines). |
unitbuf | 在每次插入以后,清空缓冲区. |
internal | 将填充字符回到符号和数值之间. |
uppercase | 以大写的形式显示科学记数法中的”e”和十六进制格式的”x”. |
iostream 中定义的操作符:
操作符 | 描述 | 输入 | 输出 |
---|---|---|---|
boolalpha | 启用boolalpha标志 | √ | √ |
dec | 启用dec标志 | √ | √ |
endl | 输出换行标示,并清空缓冲区 | √ | |
ends | 输出空字符 | √ | |
fixed | 启用fixed标志 | √ | |
flush | 清空流 | √ | |
hex | 启用 hex 标志 | √ | √ |
internal | 启用 internal 标志 | √ | |
left | 启用 left 标志 | √ | |
noboolalpha | 关闭boolalpha 标志 | √ | √ |
noshowbase | 关闭showbase 标志 | √ | |
noshowpoint | 关闭showpoint 标志 | √ | |
noshowpos | 关闭showpos 标志 | √ | |
noskipws | 关闭skipws 标志 | √ | |
nounitbuf | 关闭unitbuf 标志 | √ | |
nouppercase | 关闭uppercase 标志 | √ | |
oct | 启用 oct 标志 | √ | √ |
right | 启用 right 标志 | √ | |
scientific | 启用 scientific 标志 | √ | |
showbase | 启用 showbase 标志 | √ | |
showpoint | 启用 showpoint 标志 | √ | |
showpos | 启用 showpos 标志 | √ | |
skipws | 启用 skipws 标志 | √ | |
unitbuf | 启用 unitbuf 标志 | √ | |
uppercase | 启用 uppercase 标志 | √ | |
ws | 跳过所有前导空白字符 | √ |
iomanip 中定义的操作符:
操作符 | 描述 | 输入 | 输出 |
---|---|---|---|
resetiosflags(long f) | 关闭被指定为f的标志 | √ | √ |
setbase(int base) | 设置数值的基本数为base | √ | |
setfill(int ch) | 设置填充字符为ch | √ | |
setiosflags(long f) | 启用指定为f的标志 | √ | √ |
setprecision(int p) | 设置数值的精度(四舍五入) | √ | |
setw(int w) | 设置域宽度为w | √ |
结构体struct
下面是一个实例,抛出一个除以零的异常,并在 catch 块中捕获该异常。
#include <iostream>
using namespace std;double division(int a, int b)
{if( b == 0 ){throw "Division by zero condition!";}return (a/b);
}int main ()
{int x = 50;int y = 0;double z = 0;try {z = division(x, y);cout << z << endl;}catch (const char* msg) {cerr << msg << endl;}return 0;
}
由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。当上面的代码被编译和执行时,它会产生下列结果:
Division by zero condition!
C++ 标准的异常
下表是对上面层次结构中出现的每个异常的说明:
异常 | 描述 |
---|---|
std::exception | 该异常是所有标准 C++ 异常的父类。 |
std::bad_alloc | 该异常可以通过 new 抛出。 |
std::bad_cast | 该异常可以通过 dynamic_cast 抛出。 |
std::bad_exception | 这在处理 C++ 程序中无法预期的异常时非常有用。 |
std::bad_typeid | 该异常可以通过 typeid 抛出。 |
std::logic_error | 理论上可以通过读取代码来检测到的异常。 |
std::domain_error | 当使用了一个无效的数学域时,会抛出该异常。 |
std::invalid_argument | 当使用了无效的参数时,会抛出该异常。 |
std::length_error | 当创建了太长的 std::string 时,会抛出该异常。 |
std::out_of_range | 该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator。 |
std::runtime_error | 理论上不可以通过读取代码来检测到的异常。 |
std::overflow_error | 当发生数学上溢时,会抛出该异常。 |
std::range_error | 当尝试存储超出范围的值时,会抛出该异常。 |
std::underflow_error | 当发生数学下溢时,会抛出该异常。 |
C++ 动态内存
# 字符串化的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串。
当用作字符串化操作时,# 的主要作用是将宏参数不经扩展地转换成字符串常量。
C++ 实例
欢迎访问我的个人网站 https://www.josu.top/
【C++】菜鸟教程个人C++学习笔记相关推荐
- 菜鸟教程C语言学习笔记1
C语言环境-C语言常量部分已写在菜鸟教程下方 (7).C存储类 auto存储类 所有局部变量默认的存储类 只能用在函数内,即只能修饰局部变量 {int mount;auto int month; } ...
- 菜鸟教程C语言学习笔记2
(9)判断 判断语句 if if-else 嵌套if switch 嵌套switch 运算符 三元运算符?: (10)循环 循环类型 while do while for循坏 嵌套循环 循环控制语句 ...
- react render没更新_web前端教程分享React学习笔记(一)
web前端教程分享React学习笔记(一),React的起源和发展:React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写 ...
- 好程序员教程分析Vue学习笔记五
好程序员教程分析Vue学习笔记五,上次我们学习了Vue的组件,这次我们来学习一下路由的使用.在Vue中,所谓的路由其实跟其他的框架中的路由的概念差不多,即指跳转的路径. 注意:在Vue中,要使用路由, ...
- 【台大郭彦甫】Matlab入门教程超详细学习笔记二:基本操作与矩阵运算(附PPT链接)
Matlab入门教程超详细学习笔记二:基本操作与矩阵运算 前言 一.基本操作 1.把matlab当作计算器使用 2.变量 3.控制格式输出 二.矩阵运算 1.矩阵 2.矩阵索引 3.使用:创建向量 4 ...
- lua菜鸟教程_Lua语言学习
根据百科描述:Lua是一个小巧的脚本语言,是巴西里约热内卢天主教大学里的一个研究小组,由Roberto Ierusalimschy.Waldemar Celes 和 Luiz Henrique de ...
- Kaggle教程 机器学习入门学习笔记
机器学习入门学习笔记 [跳转]<Kaggle教程 机器学习入门>系列课程目录 >> 决策树 简介:是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零 ...
- 一个非常棒的Unity入门教程(附上学习笔记)
非常适合入门的unity教程,知识点全面,声音清晰,基本没有废话,时间控制得当,UP主Michael还在更很多其他的unity相关教程 视频教程地址:https://www.bilibili.com/ ...
- html5教程 w3cschool,W3Cschool学习笔记——HTML5基础教程
HTML5 建立的一些规则:新特性应该基于 HTML.CSS.DOM 以及 JavaScript. 减少对外部插件的需求(比如 Flash) 更优秀的错误处理 更多取代脚本的标记 HTML5 应该独立 ...
最新文章
- 【Android 逆向】函数拦截 ( CPU 高速缓存机制 | CPU 高速缓存机制 导致 函数拦截失败 )
- Git--团队开发必备神器
- 强化学习2——有模型强化学习MDP(搬砖马尔科夫,贝尔曼等式)
- 安卓微软雅黑字体ttf_618巨献丨精致的悦黑5字重小字体
- USB peripherals can turn against their users
- Dart最新消息:Angular 2 Dart及Flutter发布
- java设计模式之美_《设计模式之美》-笔记
- 创建数据库链接(dblink)步骤
- HDU5701 中位数计数【中位数+水题】
- iOS应用架构谈 本地持久化方案及动态部署
- ArcGIS 计算椭球面积
- 新浪导航栏下拉菜单案例展示
- 有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。(java)
- 手把手教学linux上扩容和缩减swap分区。
- 为何晶振并联一个1MΩ电阻?晶振低温不起振如何解决?
- 自制Arduino便携式超声波测距仪
- 最全软件测试面试题(经典)
- 参数传递之传名,传地址,得结果,传值
- Mysql报错2003 解决办法 Can‘t connect to MySQL server on ‘localhost‘ (10061)
- 案例:百度的评论系统是怎么设计的?你想象不到