c++琐碎存疑知识点梳理

1、关于break的使用:

使用场景:用于跳出选择语句或者是循环语句

  • 在switch语句中,跳出当前的case;
  • 在循环语句中,跳出当前的循环语句。(注意,对于嵌套循环,跳出的是最内层的循环语句)

2、关于continue语句的使用:

作用:终止最近的循环中的当前迭代,开始下一次迭代 ;

使用场景:for 、while和do while 循环内部

注意:对于while和do while循环语句,继续进行下一次循环的条件判断;对于传统的for循环,会执行当前循环的循环更新表达式;

3、关于Double类型数据大小比较

遇到的问题简化示例:

#include<iostream>
using namespace std;
int main(){
	for(double i=0.1;i<1;i+=0.1)
	cout<<i<<" ";
	// 输出结果为 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
}

因为精度问题,这样使用和日常使用的int类型相同的方法是不行的,需要这样比较: 

#include <math.h>    //头文件要记得加

const double EPS = 1e-6;  //一般这样子就够,但有时具体题目要考虑是否要更小的 

if(fabs(a-b) < EPS)  //判断是否相等 

if(a > b+EPS)   // 判断a是否大于b,因为大的肯定大,所以即使你小的加上,还是会更大

修改如下:

#include<iostream>
#include<cmath> 
const double eps = 1e-6;//一般负六次就够了
using namespace std;
int main(){
	for(double i=0.1;i+eps<1;i+=0.1)
	cout<<i<<" ";
	// 输出结果为 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 
}

4、关于文件输入输出

对于C语言输入输出流的底层知识学习:

参考文章:一文带你读懂C/C++语言输入输出流与缓存区 - 知乎 (zhihu.com)

缓冲区——内存空间的一部分,也就是说在内存空间中预留了一定大小的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,根据其对应的是输入设备还是输出设备,分为输入缓冲区输出缓冲区

缓冲区的刷新:

  • 缓冲区满时;
  • 执行flush语句,即使用特定函数刷新缓冲区;
  • 执行endl语句,即行缓冲区遇到回车时;
  • 关闭文件。

可见,缓冲区满或关闭文件时都会刷新缓冲区,进行真正的I/O操作。

另外,在C++中,我们可以使用flush函数来刷新缓冲区(执行I/O操作并清空缓冲区) 如:

cout << flush; //将显存的内容立即输出到显示器上进行显示

所以在程序中向文件中输出内容(不含换行)的时候, 如果在程序结束之前断点调试的时候打开文件查看,则是看不到任何东西的!不要以为是出现问题了,实际上只是因为输出的内容还在缓冲区中,等程序执行完之后就会在文件中显示了。

 此外:(歪一下楼,这篇文章中对于getchar()函数的使用讲解非常好,这里摘录一下:

int getchar(void) ;

说明:当程序调用getchar()函数时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar()函数才开始从键盘缓冲区中每次读入一个字符。也就是说,后续的getchar()函数调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才重新等待用户按键。)

 5、按位逻辑运算

1、按位与 &

参加运算的两个数据,按二进制位进行“与”运算。

例如:

int AndOperator(int &a, int &b){
    return a & b;
}
int main(){
	// 3&5 即 0000 0011 & 0000 0101 = 0000 0001  因此,3&5的值得1
    int a = 3, b = 5;
    cout<< AndOperator(a, b) <<endl;
    return 0;
}

注意:负数按补码形式参加按位与运算。

例如: -3 & 5

   即 1111 1101 & 0000 0101 = 0000 0101

   因此,-3 & 5的值得5

按位与运算的应用:

1、清零。若想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与, 结果为零。 例:3 & 0 = 0

2、取一个数的指定位。指定位置为1,其余为0。

例:设 X = 10101110,

  取X的低4位: X & 0000 1111 = 0000 1110

  取X的第5位: X & 0000 1000 = 0000 1000

3、判断奇偶 根据未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if (a & 1 == 0)代替if (a % 2 == 0)来判断a是不是偶数。         

2、按位或运算 |

应用:常用来将数据的 指定位 置为1。
例:将X=10100000的低4位置1 ,用 X | 0000 1111 = 1010 1111即可得到。

3、异或运算 ^

应用:
(1)使特定位翻转 找一个数,对应X要翻转的各位,该数的对应位为1,其余位为零,此数与X对应位异或即可。
例:X=10101110,使X低4位翻转,用X ^ 0000 1111 = 1010 0001即可得到。

(2)与0相异或,保留原值 ,X ^ 0000 0000 = 1010 1110。


4、按位取反运算符 ~

注意: ~1=-2; ~0=-1;      是按照补码来取反的!!!

如:(~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。

原理分析:

二进制数在内存中以补码的形式存储。

~9的计算步骤:
        转二进制:0 1001
        计算补码:0 1001
        按位取反:1 0110
_____
        转为原码:
        按位取反:1 1001   
        末位加一:1 1010
        符号位为1是负数,即-10

~-9的计算步骤:
        转二进制:1 1001
        计算补码:1 0111
        按位取反:0 1000
        _____
        转为原码:
        正数的补码和原码相同,仍为:0 1000 ,即8

6、关于生成随机数

  1、 C 库函数 rand()

在 C++ 程序中,在新标准出现之前,C 和 C++ 都依赖一个简单的 库函数 rand 来生成随机数

这个函数生成的是均匀分布的伪随机数,每个随机数的范围在 0 和一个系统相关的最大值(至少为 32767)之间。

int rand(void);

rand()函数 返回一个 0 ~ RAND_MAX 之间的整数。RAND_MAX 是一个定义在 <cstdlib> 的常数。 

该函数返回的数字实际上是用算法生成的,实际上并不是随机的。它是根据种子生成的,根据不同的种子产生不同的随机序列。系统默认的种子是1,所以说每次使用的随机序列都是固定的。

所以需要配合随机种子生成函数来使用 ——

要想使每次运行时变量 x 的值都不同,就必须使它的种子随机,这时就需要用到srand函数。

void srand(unsigned int seed);

srand()函数 就是用来设置rand()函数的种子的。根据不同的输入参数可以产生不同的种子。通常使用time函数作为srand函数的输入参数。

time函数会返回1970年1月1日至今所经历的时间(以秒为单位)。

在使用 rand() 函数之前,srand() 函数要先被调用,并且在整个程序中只需被调用一次

具体使用示例:

#include <cstdlib>
#include <ctime>
using namespace std;
int main() 
{
    srand(time(nullptr)); // 用当前时间作为种子
    int min = 5, max = 10;
    int randomValue = (rand() % (max - min)) + min;//范围[min,max)
    randomValue = (rand() % (max - min + 1)) + min;//范围[min,max]
    randomValue = (rand() % (max - min)) + min + 1;//范围(min,max]
}

缺点:

1、不能产生随机浮点数

2、均匀分布:有很多程序需要不通范围的随机数。一些程序需要非均匀分布的随机数。而在编写程序为了解决这些通常会转换 rand 生成的随机数的范围、类型或者是分布时,常常会引入非随机性。

3、随机数:多次循环产生的随机数序列是完全一样的

如:

#include<iostream>
#include<ctime>
using namespace std;
int main() {

    for (int j = 0; j < 5; j++) {
        srand(time(0));  //产生不同的随机数种子 
        for (int i = 0; i < 10; i++)
            cout << rand() % 10 << " "; //rand函数 ;
        cout << endl;
    }
}

运行结果:

但是,如果将程序修改如下就不会出现这样的重复序列

#include<iostream>
#include<ctime>
using namespace std;
int main() {
    srand(time(0));  //产生不同的随机数种子 

    for (int j = 0; j < 5; j++) {
        //srand(time(0));  //产生不同的随机数种子 
        for (int i = 0; i < 10; i++)
            cout << rand() % 10 << " "; //rand函数 ;
        cout << endl;
    }
}

 运行结果:

一种特性应用场景:

结合 rand() 函数的均匀分布以及有限范围的特点,可以在遗传算法的实现中实现“随机性”的要求

double p = (rand()/RAND_MAX)*Len;//将随机数均匀投射到[0,Len]的区间范围内
if(p<X){
    //情况1
}
else //……


 详见我之前关于遗传算法实现的文章:
GA遗传算法实现记录_努力的耿耿的博客-CSDN博客

 

2、C++11函数random()

头文件 random

运用示例代码:

#include<random>//头文件
#include<iostream>
#include<ctime>
using namespace std;
 
int main()
{
	default_random_engine e(time(0));//引擎生成随机序列,设置种子
	uniform_real_distribution<double> u(-1.2,3.5);//设置产生的随机数的类型以及范围
	for(int i = 0; i < 10; ++i)
		cout << u(e) << endl;
	return 0;
}

详细原理见这篇博客:
 【C++】c++ 11中的随机数 ——random_S大幕的博客-CSDN博客_c++11 随机数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值