c++ 6.11

要解决具体问题,必须首先要学会用数据类型来描述问题中的具体事物。世界上的问题形形色色,仅用语言内部的数据类型来描述事物是远远不够的,还必须借助于语言所提供的数据类型描述机制来自定义数据类型。要自定义数据类型,就必须先了解语言的内部和基本的数据类型。

两个问题,

1.为什么补码的补码等于原码?

一般来说,补码就是原码取反加一,还原成原码的时候应该先减去一再取反。其实在二进制里,"减一取反"和"取反加一"等价,所以我们可以这么做

为什么等价呢?

假设一个N位的二进制数,可表示的最大值是2^N - 1,例如八位二进制数,最大值是"11111111",就是2^8 - 1,另设一个数n,有一个数x,在这个N位的二进制数里,x = 2^N - 1 - n,即n + x = 2^N - 1,我们不妨称x为n的“补数”,那么对n取反,其实就是算出它对于当前N位数的最大二进制数的补数(因为一个位的数和自身取反的数相加必然是1),例如n是10110100(忽略符号位),取反后为01001011,此为x,而n+x=11111111,即当前N位数的最大二进制数,所以取反就是算补数。

那么对于数n,先取反求出补数,(2^N - 1 - n),再加一得: (2^N - n)

然后再先对n减一,得(n - 1), 再求补数:(2^N - 1 - (n - 1))= (2^N - n)

所以说二进制里,"减一取反"和"取反加一"等价,补码减一取反或者取反加一的值都一样,都能求出原码
————————————————
版权声明:本文为CSDN博主「逍遥笑笑生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40930559/article/details/101393524

2.为什么2进制左移一位相当于乘二运算?

用二进制求和把二进制数展开成10进制数,对两边乘2,发现每个乘方加了1,相当于位码左移了

-------------------------------------------------------------------------------------------------------------

C++编译器目前版本还是32位的,软件相对于硬件总是滞后的

用任何非0整数给bool型变量赋值时,其值都为1.甚至非整数的其他类型,只要非0,其值也是1.因此:

enum bool{True,False}

bool a=3,b=1;

bool d=a-b;(d=1-1=0,为False)

bool型的输出形式可以选择,默认为整数1和0,如果要输出true和false,则可用输出控制符:

cout<<boolalpha<<d<<endl; 

//除2取余法。才发现自己之前一直搞错了。。。。

对被转换的十进制小数乘以2,取其整数部分,作为二进制小数部分,取其小数部分,再乘以2,指导小数部分为0或者已经取到了足够高的位数。

在计算机内部,浮点数是以国际标准IEEE754的形式表示的。该标准将二进制浮点数分成三段,第一段是符号段,它总是占1位,第二段是阶码段,第三段是尾数段。例如,在32位浮点数(float类型),符号段占1位,阶码段占8位,尾数段占23位。在64位浮点数(对应C++中的double类型),阶码段占11位,尾数段占52位;

如果二进制浮点数像十进制浮点数那样规格化,即要求小数点后第一位非0,那么其小数点后的第1位的非0值只能为1,它构成了浮点数尾数的一部分,在计算机内部占去了表示精度的宝贵的1位。由于这样表示的结果使得小数第一位总是为1,何不将该位挪前,增加一位有效位呢?因此,二进制浮点数的规格化不同于十进制浮点数。例如,在32位浮点数表示中

0.6=0.1001(……)

     =1.0011,0011,0011,0011,0011,0011……

它比老老实实表示23位尾数多了一位精度,在具体实现中,抹掉规格化的1,写入二进制浮点数,取出参与运算时,则补上1.因此23位尾数,加上省略的一位,其精度或有效位却是24位

char型字符数组总是多占一个0;

 cin>>的读入方式总是将前导的空格(所谓空格包括空格、回车、水平或垂直制表符等)滤掉,将单词读入,当遇到空格时结束本次输入;

getline总是将行末的回车符滤掉。

isstringstream是输入string流,它在sstream头文件中说明 

vector(矢量,单行矩阵) 是向量类型,它是一种对象实体,具有值,所以可以看做是变量。它可以容纳许多其他类型的相同实体,如若干个整数,所以称其为容器;

C++拥有在运行时获得变量或对象的地址和通过地址操纵数据的能力,这种能力是通过指针来发挥的。由于很多高级操作的内部实现都依赖指针,所以指针不但在过程化程序设计中必不可少,在面向对象程序设计中也必不可少。指针用于数组,用于函数参数,用于内存空间申请和释放等,指针对于成功进行C++编程至关重要。也许指针在其他语言中并不是必要的,但在C++中,显得很必要。指针在提高性能方面,提升c++的产业竞争力上,立下了汗马功劳。指针功能是强大的,但又是最危险的。学习指针的时候,我们始终要强调指针的双刃剑作用。

每个类型都有对应的类型指针 int* ip; char *cp;float *fp; double *dp; "*"可以居左,居右,居中,一个*只能修饰一个指针

一旦引用产生,就确定了它与另一个实体的练习,这种联系是打不破的,直到引用自身的灭亡,

声明变量 [<存储类>]<类型名或类型定义><变量名表>;

auto:属于一次性存储,可被若干变量多次覆盖使用;register:存放在通用寄存器中;extern:在所有函数和程序段中均可用;static: 在内存中是以固定地址存放的,在整个程序运行期间都有效

变量的作用范围:

全局变量:其说明语句不在任何一个类定义、函数定义或复合语句(程序块)中的变量。全局变量所占用的空间在内存的数据区,在程序运行的整个过程中位置保持不变。

局部变量:其说明语句在某一个类定义、函数定义或复合语句(程序块)中的变量。局部变量所占用的空间为程序运行时所设置的临时空间区中,以堆栈的形式允许反复占用和释放;

+

const常量 必须以const开头,类型名为其基本类型及其派生类型,可以省略,常量名为标识符,表达式应于常量类型一致;

宏常量 宏名可以是简单的字符名,也可以是带有参数的函数名

iomanip=io manipulation(操纵)

C++在头文件iomanip 中定义了控制符对象,可以直接将这些控制符嵌入到I/O语句中进行格式控制:

dec :置基数为10

hex:置基数为16

oct:置基数为8

setfill(c):设填充字符为c

setprecision(n):显示小数精度为n位

setw(n):设域宽为n个字符

setiosflags(skipws):忽略前导空白

 定义函数

函数定义的一般形式如下:

 返回类型 函数名(参数列表)

{

……(函数体)

}

函数名:即一个符合c++语法的识符。定义函数名与定义变量名的规则是一样的,但应尽量避免用下划线开头,因为编译器常常定义一些下划线开头的变量或函数。并且函数名应尽可能反映函数的功能,其常常由几个单词组成。如Visual C++中的按下左键的响应函数为OnLButtonDown,这样就较好地反映了函数的功能。此外,在函数名后面必须跟一堆圆括号“()”,用来将函数名与变名区分开来。在括号中可以没有任何信息,也可以包含形式参数表。c++程序通过使用这个函数名和实参表可以调用任何函数。

参数列表:参数也即0个或多个变量,写在函数名后面的一对圆括号内,用于向函数传送数值或从函数带回数值,其不同于变量定义,每一个参数都有自己的类型。当参数列表中的参数多于一个时,其前后两个参数说明项之间必须用逗号分隔开。如果参数表列中参数个数为0,称为无参函数。

返回类型:即指定函数用return返回的函数值的类型,如果函数没有返回值,返回类型应为void。每个函数都有类型,如果在函数定义时没有明确指定类型,则默认类型为int;

除此之外,C++中不允许函数定义嵌套,即在函数定义中再定义一个函数是非法的。一个函数只能定义在别的函数的外部,函数定义之间都是平行的,互相独立的。

实际参数必须与形式参数的个数相同、数据类型相同,而且其对应顺序必须为一一对应。

获得函数的返回值是调用函数的目的。

C++中,如果在使用函数前没有对函数进行定义,则必须对函数进行声明。函数原型声明用来指出函数的名称、类型和参数,其说明语句的一般形式为:

 [<属性说明>]<函数类型><函数名>(参数)

inline(内联函数)、static(静态函数)、virtual(虚函数)、friend(友元函数)

例如:如果有一个函数的定义如下:

double funcl(double a,int b,float c)

{

(函数体)

}

那么正确完整的函数声明为:

double funcl(double x,int y,float z); //末尾加上分号

double funcl(double,int,float);//函数声明中省略了形参名;

double funcl(double a,int b,float c);

main函数是可以带参数的。在main函数中允许带两个参数,一个为argc,为整型数据类型,另一个是指向字符型的指针数组argv [].例如

int mian(int argc,char* argv[])

整型参数argc表示命令行中字符串的个数,指针数组argv []指向命令行中的各个字符串;

函数声明后,在其他程序中即可对函数进行调用了。一般来说,c++程序都是从主函数main开始执行,当执行到函数调用语句时,就会转去执行调用函数,执行后仍然返回到主函数,直至程序结束。当调用一个函数时,整个调用过程分为三步进行:第一步是参数传递,第二步是函数体执行,第三步是返回,即返回到函数调用表达式的位置;

根据参数传递的方式,函数调用可分为两种不同的形式,一种是按值传递,另一种是地址传递或引用传递

传值调用:

① 计算出实参表达式的值,接着给对应的形参变量分配一个存储空间,该空间的大小等于该形参类型的长度;

② 把已求出的实参表达式的值一一存入到为形参分配的存储空间中,成为形参变量的初值,供被调用函数执行时使用;

这种方式调用函数本身不对实参进行操作,也就是说,即使形参的值在函数中发生了变化,实参的值也不会受任何影响,仍为调用前的值;例如 swap()函数使用后对于实参没有任何影响,也就无法发挥它应有的作用;

引用传递:

引用传递过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量; void swap(int &a,int &b),引用传递方式是在函数定义时,在形参前面加上引用运算符“&”。在函数被调用时,参数传递的内容不是实参的值,而是实参的地址,即将实参的地址放到C++为形参分配的内存空间中,因此形参的任何操作都会相应改变实参的值;

C++函数虽然不能嵌套定义,但是可以嵌套调用;

递归调用:是指函数直接或间接的调用自身。递归调用有两种方式:直接递归调用和间接递归调用。直接递归调用即在一个函数中调用自身,间接递归调用即在一个函数中调用了其他函数,而在该其他函数中又调用了本函数

当一个函数既有定义又有声明时,形参的默认值必须在声明中指定,而不能在定义中指定。只有当函数没有声明时,才可以在函数定义中指定形参的默认值。

在函数调用时,实参与形参按从左到右的顺序进行匹配,当实参的数目少于形参时,如果对应位置形参又没有设定默认值,就会产生编译错误;如果设定了默认值,编译器将为那些没有对应实参的形参取默认值。

变量的作用域:

一个程序将操作系统给其运行的内存块分为4个区域:

代码区:存放程序的代码,即程序中各个函数的代码块;

全局数据区:存放程序全局数据和静态数据;

堆区:存放程序的动态数据

栈区:存放程序的局部数据,即各个函数中的数据;

在一个函数内部说明的变量是内部变量,其只在函数范围内有效。

局部变量

形参变量也是内部变量,属于被调用函数;实参变量,则是调用函数的内部变量;

允许在不同的函数中使用相同的变量名,但它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。

在复合语句中也可定义变量,其作用域只在复合语句范围内。

允许在不同的函数中使用相同的变量名,但它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。

全局变量

全局变量称为外部变量,其是在函数外部定义的变量。

全局变量不属于任何一个函数,可被作用域内的所有函数直接引用,其作用域从外部变量定义的位置开始,到本文件结束为止;

外部变量可加强函数模块之间的数据联系,但又使这些函数依赖这些外部变量,从而使得这些函数的独立性降低。

在同一源文件中,允许外部变量和内部变量同名。在内部变量的作用域内,外部变量将被屏蔽而不起作用。

全局变量的作用域是从定义点到本文件结束。如果定义点之前的函数需要引用这些外部变量时,需要在函数内对被引用的外部变量进行说明。

extern 数据类型 外部变量 

goto语句或Switch语句不应使控制在一个声明的作用域之外跳到该声明的作用域内,因为这种跳转越过了变量的声明语句,使变量不能被初始化;

函数重载

函数重载是指同一个函数名可以对应多个函数的实现。每一类实现对应着一个函数体,这些函数的名字相同,但是函数参数的类型不同,这就是函数重载。0000

函数重载又称为函数的多态性,是指同一个函数名对应着多个不同的函数。所谓“不同”,是指这些函数的形参表必须互不相同,或者是形参的个数不同,或者是形参的类型不同,或者是两者都不相同,否则无法实现函数重载。例如

double func1(int,int);

double func1(long);

int func1(int,int);

int func1(long);

重载函数的类型,即返回函数的类型,可以相同,也可以不同,但如果仅仅是返回类型不同而函数名相同,形参表也相同,则是不合法的,编译器会给出语法错误提示。例如: 

除形参名都相同的情况,编译器不认为是重载函数,只认为是对同一个函数原型的多次声明

预处理命令:

简单的说,预处理就是对源文件进行编译前,先对预处理部分进行处理,然后对处理后的代码进行编译。这样做的好处是,经过处理后的代码将会变得很精短。c++提供了丰富的预处理命令,主要包括如下几种:

#define、/#error、#if、#else、#elif、#endif、#ifdef、#ifndef、#undef、#line、#pragma

宏(macro) 是程序设计语言中较为广泛的一个概念,简单地说,宏是一种以相同的源语言执行预定义指令序列的指令。在c++中,通过宏的使用,可以将一个表达式定义成宏,并在C++的源程序中随意调用。

在C++语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为宏的标识符称为宏名,在编译预处理时,对程序中所有出现的宏名,都用宏定义中的字符串去代换,这称为宏代换或宏展开。在定义宏时:

①宏名一般用大写

②使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改,例如:数组大小常用宏定义

③预处理是在编译之前的处理,而编译工作的任务之一是语法检查,预处理不作语法检查。

宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查,。如有错误,只能在编译已被宏展开后的源程序中发现。

宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号一起置换。

宏定义必须写在函数之外,其作用域为从宏定义命令开始到源程序结束。

一般来说,宏名用大写字母表示,以便与变量区别,但也允许用小写字母。

若果要求宏定义只在一个函数中起作用,就可以在函数定义之前定义宏,在函数结束后结束宏,结束宏命令为#undef

被双引号“”括起来后预处理程序并不会对宏进行代换

宏定义允许嵌套, 即在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换(如果交换顺序会怎样?)

 

可以看到,即使调换顺序 也可以嵌套使用宏;

带参数的宏定义:在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。与不带参数的宏不同的是,带参数的宏在调用中,预处理程序不仅要展开宏,进行宏替换,还要用实参去代换形参;#define 宏名(形参表) 字符串

 带参的宏定义:

①带参宏定义中,宏名和形参表之间不能有空格出现。

②在带参宏定义中,形式参数不分配了内存单元,因此不必作类型定义。

函数调用时,要把实参表达式的值求出来再赋予形参。而宏代换中对实参表达式不作计算直接代换,此外,在宏定义中,字符串内的形参通常要通过括号括起来以避免出错。

 这个SQ(y)函数后面的y没有带括号,但是计算结果是正确的;

 按常理来说,SQ既然等于35,那么b=2*SQ就应该等于70,可是2*SQ相当于是2*25+10也就是加号后面的并没有被乘二;

如果把SQ加上括号呢?

 没有用,老老实实给每个y加上括号

 

额,还是出错

给整个表达式括号

成功了!

内联函数也称内嵌函数,当在一个函数的定义或者声明前加上关键字inline就把该函数定义为内联函数,它主要用于解决程序的运行效率。

计算机在执行一般的函数调用时,无论该函数多么简单或复杂,都要经过参数传递、执行函数体和返回等操作,这些操作都需要一定的时间,若把一个函数定义为内联函数后,在程序编译阶段,编译器就会在每次调用该函数的地方都直接替换为该函数体中的代码,由此省去函数的调用、相应地保存现场 、参数传递和返回操作等需要的时间,从而加快整个程序的执行速度;

 带参宏和函数调用的区别如上,函数调用是把形参传递给y后自增1,然后输出函数值,因而会循环五次;宏调用时只做代换。SQ(i++)被代换为((i++)*(i++)),每次循环后i的值会增加2,因此只做3次循环。

#include命令后面的文件名可以用双括号括起来,也可以用尖括号括起来。

#include<iostream或者#include"iostream"都是允许的

#include<math>和#include"math.h"的区别在于,遇到#include<math>时,系统从默认的头文件目录中查找文件;而遇到#include“math.h”时,首先从当前的目录中搜索,如果没有再从默认的头文件中查找文件math.h文件,在使用#include命令时,对系统文件,使用#include<>的形式较好;而对用户自定义文件,则使用#include""形式更快。

条件编译

#ifdef 标识符

    程序段1

#else

    程序段2

#endif

条件编译的功能是,如果标识符已经被#define命令定义过,对程序段1进行编译,否则对程序段2进行编译。

#ifndef=if not define

#if 常量表达式

     程序段1

#else 

    程序段2

#endif

这种形式的条件编译结构功能是,如果常量表达式的值为真,则对程序段1进行编译,否则对程序段2进行编译。

#error用于程序的调试,在编译中遇到#error指令就停止编译。其一般形式为:

#error 出错信息

C++提供#error命令的目的是保证程序是按照用户所设想的那样进行编译的。系统编译程序时,只要遇到#error就会跳出一个编译错误,用户就可以知道程序是否正常执行。 

#line 命令用于控制行号,指令#line可以在出错时显示文件中的行数及希望显示的文件名。

#line number“filename”

//1:09

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值