数据格式详解
输入输出函数详解
字符串处理函数详解
内存函数详解
类详解
数据格式详解
2^8=256( 同样是一个字节,无符号数的最大值是255,而有符号数的最大值是127。)
2^10=1024
2^16=65536(32768,3万)
2^32=4294967296 (大约40亿,4后面10个0)
float最少可表示6位小数
double最少可表示15位小数
long double最少可表示18位小数
C语言变量名与函数名最长不能超过63字节,extern不能超过31字节,每行至多支持到4095字节
C99支持了不定长的数组,你可以用int a[*]这种奇怪的写法,不过近乎没人这么用的。
C语言int的取值范围在32/64位系统中都是32位,范围为-2147483648+2147483647,无符号情况下表示为4294967295。共有10位
左移1位就是x2,不管整数是否有符号,而对于右移,对于有符号的负数,前面添加1,在其他情况下,包括无符号,添加0
编译器可以根据自身硬件来选择合适的大小,但是需要满足约束:short和int型至少为16位,long型至少为32位,并且short型长度不能超过int型,而int型不能超过long型。这即是说各个类型的变量长度是由编译器来决定的,而当前主流的编译器中一般是32位机器和64位机器中int型都是4个字节(例如,GCC)。数据类型占内存的位数实际上与操作系统的位数和编译器(不同编译器支持的位数可能有所不同)都有关 ,具体某种数据类型占字节数得编译器根据操作系统位数两者之间进行协调好后分配内存大小。具体在使用的时候如想知道具体占内存的位数通过sizeof(int)可以得到准确的答案。
为什么在64位机上的指针是4字节?一是编译器为了相互兼容,所有指针都为4字节;二是可能用了32位的编译器。
C标准规定,float类型必须至少能表示6位有效数字,且取值范围至少是10的-37次方~10的+37次方。前一项规定指float类型必须至少精确表示小数点后的6位有效数字,如33.333333。 后一项规定用于方便地表示诸如太阳质量(2.0e30千克)、一个质子的电荷量(1.6e-19库仑)或国家债务之类的数字。通常,系统储存一个浮点数要占用32位。其中8位用于表示指数的值和符号,剩下24位用于表示非指数部分(也叫作尾数或有效数)及其符号。
C语言提供的另一种浮点类型是double(意为双精度)。 double类型和float类型的最小取值范围相同,但至少必须能表示10位有效数字。一般情况下,double占用64位而不是32位。一些系统将多出的 32 位全部用来表示非指数部分,这不仅增加了有效数字的位数(即提高了精度),而且还减少了舍入误差。另一些系统把其中的一些位分配给指数部分,以容纳更大的指数,从而增加了可表示数的范围。无论哪种方法,double类型的值至少有13位有效数字,超过了标准的最低位数规定。C语言的第3种浮点类型是long double,以满足比double类型更高的精度要求。不过,C只保证long double类型至少与double类型的精度相同。
二进制小数说的是它只包括小数部分而不包括整数部分,它的原码也是0表示正,1表示负
两个正数的相减是如何通过反码的相加实现呢?首先是把被减数当作负数取反码,然后相加,如果还进了一位,就移到个位继续相加,最终得到的这个结果。
对于0来说,它的原码和反码都有两种(分别为0000 0000,1000 0000,和0000 0000, 1111 1111),但是补码只有一种(即0000 0000),-0的补码形式等于对应的正数0的原码00000000,取反为11111111,加1是00000000,答案仍然是0,溢出了。整数0,小数0的补码都只有这一种形式。同时也是说,补码没有1000 0000这个值(用来干啥好呢?所以就赋给-128.。。。),其实不是的,-127的原,反,补为:1111 1111, 1000 0000, 1000 0001,因为穷举法,补码 1000 0000 为 -128 是不用怀疑的,所以, 8位有符号的整数取值范围的补码表示 1000 0000 到 0000 0000, 再到 0111 1111 即 -128 到 0, 再到 127 最终 -128 ~ +127,中间没有中断,一直是往上加1的,只不过到0的时候溢出了。-128没有原码,也没有反码,都被-0占了(分别是1000 0000和1111 1111)。
补码本质是用来构成一个环,以实现一个同余运算。二进制算术运算的符号位可以和数值位一样参与运算并能得到正确的结果,但一般来说,符号位参与运算一般都是溢出了。
若采用补码进行运算,同样也会达到相应的效果,但是产生的进位要舍弃。但是采用反码操作时,需要将进位加到后面去,相比,补码比较方便。一个二进制数的补码的补码就是原码!!!
机器字长为8时,有符号整数的范围按照原码/反码都是-127到127,因为最高位为符号位,所以根本就算不到128那里去。。。并非反码规则制定的。但是对于编程语言+硬件性质来讲,一般都会仅仅使用补码,从而把8位字长扩展到-128~127,无符号数没有原码、反码和补码一说。只有带符号数才存在不同的编码方式。
在堆外定义的变量都会初始化为0的,不管是单个变量还是数组
INT_MIN和INT_MAX分别表示有符号基本整型的最小值和最大值
随便写几个输出格式记着:%.3lf,%03d,%6d
优先级:() > . > *
用二维数组作为参数的话,这么写是错的int a[n][],而这么写是对的 int (*a)[n],就是这样,其实,你用a[n][m]也是没关系的,但上面那个就是一个指针,数组返回指针就不行,至于一级也是可变的,根本就是不可以的,除非
如果将二维数组作为参数传递给函数,那么在函数的参数声明中必须指明数组的列数,数组的行数没有太大关系,可以指定也可以不指定。因为函数调用时传递的是一个指针,它指向由行向量够成的一维数组。因此二维数组作为函数参数正确写法如下所示:
void Func(int array[ ][10]);
因为数组的行数无关紧要,所以还可以写成如下形式:
void Func(int (*array)[10]); 因此这个10实际上代表的是后面的那个数字
因此int** 这种类型就永远不算对的那种
技巧:把二维数组当成一维数组来用:a[i/3][i%3]
point *p[3] 现在看的话,因为[]比*优先级要大,因此它首先是一个数组,然后类型为point*型的
point (*p)[3]那么由于()的优先级比[]高,因此它首先是一个指针,然后它指向一个容量为3的point数组,就是这样。
强制转换运算符()优先级极高,因此在给一个公式做强制转换的时候请在外面包上一个括号。
constint* p; //p可变,p指向的内容不可变,int const* p; //p可变,p指向的内容不可变。 int* const p; //p不可变,p指向的内容可变 const int* const p; //p和p指向的内容都不可变。
趋向于运算符也不是一无是处,例如吸收了所有的变量然后n的递减就可以这么做,而且还用上了dowhile,非常棒了。
普通计算机完成一万次循环不到0.1秒
如何比较两个浮点数相等?通过相减后与一个极小值比较就行,一般极小值这么写1E-6,是一个浮点数,整体这么写:fabs(a-b)<1E-6就行。
枚举类型enum的元素长度根据编译器而定。在visual c++下,它和int一样长,是4个字节,在GCC下它会取尽可能短的长度,例如你这个枚举类型只有3种标识,那么它是一个字节
进制转换只有两种情况:十进制转换为其他进制(用这个十进制数除以所求进制取余数即可),其他进制转换为十进制(此进制数拆成数字乘以进制对应的幂次方即可)。
二进制是0b开头,八进制是0什么什么,十六进制是0x什么什么,如果在int前面加上0,那么就不再是十进制的,天
12的平方是int在GCC中的极限平方了,到了13的平方就会溢出,int型数组建立20万个没事,建立100万个就创建不出了,因此在数组建立不出来时,尽量让数组放在函数之外,因为如果数组太大,放在函数内有可能会崩溃,在函数之外则不会有这样的问题。因为在函数外定义属于全局变量,全局变量在静态存储区分配内存,而局部变量是在栈上分配内存空间的,如果数组太大,可能会造成栈溢出。
创建链表的时候,一定要给最后设置NULL,不然是会乱指的
在C++中,如果创建一个结构体指针不设NULL,同样会乱指
c语言不会用&作为函数参数,但C++可以,代表地址,也就是说,可以完全相当于*,但是,如果是直接的常量,写死在内存上,那么就会报错,而*则不会出现这种问题,说白了就是一种自带注释的写法,但关键是,&更方便,代码更易懂
在输入指针参数时,如果只是输入一个参数指针,则也会按照值传递来算,也就是说,只会把指针的值传进去,真恶心
另外,要注意内存泄漏了,多使用del或者free函数,两者适用于不同的场合
编译器隐式执行的任何类型转换都可以由static_cast来完成,比如int与float、double与char、enum与int之间的转换等。当编译器隐式执行类型转换时,大多数的编译器都会给出一个警告,使用static_cast可以明确告诉编译器,这种损失精度的转换是在知情的情况下进行的,也可以让阅读程序的其他程序员明确你转换的目的而不是由于疏忽。
用法:使用static_cast可以找回存放在void指针中的值。一般用于malloc,它的返回值正是void,这叫自带解释。。
double * dptr = static_cast
C11增加了一些新特性,and,or,not 何以取代&& || !真方便!
指针从本质上讲是一个变量,变量的值是另一个变量的地址,指针在逻辑上是独立的,它可以被改变的,包括指针变量的值(所指向的地址)和指针变量的值对应的内存中的数据(所指向地址中所存放的数据)。
引用从本质上讲是一个别名,是另一个变量的同义词,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化(先有这个变量,这个实物,这个实物才能有别名),而且其引用的对象在其整个生命周期中不能被改变,即自始至终只能依附于同一个变量(初始化的时候代表的是谁的别名,就一直是谁的别名,不能变)。引用在定义时需要加上&,而在使用时不用加&,在函数定义参数时要添加&,而不是特别定义一个引用然后使用。
用assert启用断言,一般是对预计可能出现的错误函数参数或者溢出变量进行判断,需要时添加#include
for(expression : struct) 完全也可以用普通数组这个语法糖,但是指针就不行,而且是值传递的,也就是不能修改
在C++中,结构体可以直接相等赋值(里面没有指针),这个叫浅拷贝,如果有指针的话,可以预见,它们将使用同一地址,如果要拷贝,就应该开辟新的地址,放入新的数据,封装在单独的函数中执行即可
struct MyStruct t1 = {1, 2, “hello”};可以这样算
这里有一个值得注意的一点是,如果一个数组是堆分配的,那么它会自动初始化为0,如果是栈分配的,那么它是随机分配,没有初始化这么一个过程,在C语言还是在c++中都是这样
当心使用exit(0),会出现僵尸程序的问题:printf函数就是使用的是缓冲I/O的方式,该函数在遇到“\n“换行符时自动的从缓冲区中将记录读出,并且不会读出\n,即\n仍然保存在缓冲区中。而exit()将缓冲区的数据写完后才能退出来,所以调用exit()函数后程序并不会马上退出,这就是有些出现的僵尸程序,而_exit是直接退出进入到内核中去。_exit();和exit()主要区别是一个退出进程会清理I/O缓冲区,一个直接结束进程进入到内核中。
括号失效:有时你明明以为加了括号可以保证万无一失,但是还是可能跑偏了,例如
int c = ++b * (a+b) 因为有那个自增的运算符,整个表达式异常凶险。。。
C++/CX 是windows运行时的C++语法。它是属于Native C++,不使用CLR也没有垃圾回收机制,与.Net平台下扩展的C++/CLR有本质的区别,虽然两者语法比较类似,但是这两者是完全不同的。C++/CX是基于Windows运行时而诞生的,它运行在Windows运行时上,比在.Net平台上的托管C++具有更高的效率。
c编译器中,仅支持C89规范的编译器,只支持在作用域起始部分(大括号最开始)定义变量。支持C99或者部分支持C99的编译器, 局部变量可以定义在任何位置。基本上绝大多数都支持了,甚至还有一部分支持for(int i),但是并不建议在C语言中用这个。
除树叶外,每个节点都有两个儿子的根树称为完全二叉树,也就是说,不是最矮的,完美二叉树是说的是最矮的然后有2^(k+1)-1的那种树,深度说的是边数,是节点数减1,完全二叉树数据结构上与离散数学上讲的不一样,数据结构讲的是最矮的,完满二叉树才是每个节点都有两个儿子的树
const修饰变量,以下两种定义形式在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的。
TYPE const ValueName = value;
const TYPE ValueName = value;
(1)指针本身是常量不可变 char* const pContent;
(2)指针所指向的内容是常量不可变 const char *pContent;
(3)两者都不可变 const char* const pContent;
#ifndef #define #endif结构放在头文件里防止重复定义,这个是非常必要的,另外,不要在头文件里定义变量,在实现里定义变量,然后在头文件里extern一下,这样做的好处是各模块在编译的时候,就知道“有这么个变量,但它的空间不在我这里”,链接的时候,这个变量虽然出现在所有包含这个头文件的模块里,但只有一个模块是它的真身所在,因为我们期望一个变量只被实例化一次,在多个单元共用一个地址,而不是各有各的实例化
能够往cpp里放的,就往cpp里放,如果是内联函数,记得标上inline,否则会出现multiple definitions,一般而言,头文件中应该只能有public APIs,其他所有的属于implementation detail的东西都应当放在源文件里面。
C允许从void*隐式转换到其它的指针类型,但C++不允许。例如:
int * i = malloc(sizeof(int) * 5);
这表明C++对类型检查更为严格
对变量的赋值一般不能放到函数外,只有在定义全局变量时的初始化才被允许,所以如果一定要在函数外定义变量,要么就不要赋值定义(在函数内定义),要么就在声明后立刻定义
你这么初始化数组 int a[5] = {0},它是会全部初始化0没错,但是如果你这样 int a[5] = {1},那么它只会把第一个初始化为1,其余的初始化为0,在这时编译器只会认为你只是部分初始化,因此它会"贴心的"把剩下的初始化为0,如果你不初始化,那么数组里只是一团乱码
全局的const对象默认是没有extern的声明的,所以它只在当前文件中有效
数据结构
早在C++98标准中就存在了auto关键字,那时的auto用于声明变量为自动变量,自动变量意为拥有自动的生命期,这是多余的,因为就算不使用auto声明,变量依旧拥有自动的生命期,C++98中的auto多余且极少使用,C++11已经删除了这一用法,取而代之的是全新的auto:变量的自动类型推断。auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型
auto默认是by-value方式
有时候不引用string头文件仍然可以使用string,那是因为有可能某一个头文件里包括了它自己的,不同平台可能有不同,在这里不要偷懒。
C++的struct可以放入函数,可以指定默认值,但是C语言的不行
map的值是按照key升序排列的,也就是说,自动排列
通过map对象的方法获取的iterator数据类型是一个std::pair对象。iterator包括两个数据 iterator->first 和 iterator->second 分别代表关键字key和存储的数据value。
宏定义迭代器是一种检查容器内元素并遍历元素的数据类型。C++更趋向于使用迭代器而不是下标操作,因为标准库为每一种标准容器(如vector)定义了一种迭代器类型,而只用少数容器(如vector)支持下标操作访问容器元素。
宏常量没有数据类型,没有类型安全检查,不分配内存,并在字符串替换时可能会产生意想不到的错误,字符串中永远不包含宏,否则该宏名当字符串处理,宏定义末尾不必加分号,否则连分号一并替换,宏定义可以嵌套,可用#undef命令终止宏定义的作用域
2.带参数的宏定义实例:
#define INC(x) x+1
y = INC(5);
反例:
#define SQ® r*r
area = SQ(a+b);//就会变成a+b*a+b,所以最好所有层次都要带上括号
文件包含 #include
条件编译 #ifdef
3.在C语言的宏里,#的功能是将后面的宏参数进行字符串化操作
__LINE__表示是在多少行
__FILE__表示这个是在哪个源文件里的
__DATE__表示源文件被翻译到代码时的日期
__TIME__源代码翻译到目标代码的事件
只包含空格的行,被称为空白行,可能带有注释,C++ 编译器会完全忽略它。
4.此外,我们还可以使用 #if 0 … #endif 来实现注释,且可以实现嵌套,格式为:
#if 0
code
#endif
123
你可以把 #if 0 改成 #if 1 来执行 code 的代码。
这种形式对程序调试也可以帮助,测试时使用 #if 1 来执行测试代码,发布后使用 #if 0来屏蔽测试代码。
输入输出函数详解
对于输入输出,在最后输出回车还是空格的问题上,聪明人都用三目元表达式,但是注意要把表达式全部用括号括起来,为了防止编译器分析不当,而且这种情况时有发生
如何在类似 for(auto var:set){} 这种语句里确定var到底是不是最后一个?或者说set怎么确定其中的值是最后一个?这里有讲究: var == *(--set.end()) 注意是自减而不是-1,--是重载了的,而-没有重载,所以会报错的,而且注意是前面自减才对。
要用
在codeblocks环境中启动程序不会使用在debug文件夹下的文件,若输出文件什么也没有输出,或者没有return 0的,证明程序没有运行完就强行关闭了。
5.scanf 函数的返回值反映的是按照指定的格式符正确读入的数据的个数。也就是说,可以运用while(scanf(“%d”,&x)==1){}来更加简化没有确定数据个数时的代码段,那么,这里只能输入字母来结束输入,在scanf中,回车,空格,tab键是无关紧要的,也就是输入多少也不会管,只有按下空格,再按Ctrl+z,然后再按回车,才算结束输入,这时候scanf接受的是第一个空格之前的字符,在Linux中,按下回车,再按下Ctrl+D即可结束输入,也就是说,scanf的这种特性基本没用,只有在ACM中有用。另外printf的添加空格功能也是非常重要的一个功能,格式为%3d
scanf的返回值用在什么地方呢?用在没有给出有多少数据,一次性输入完就算的那种,这么写:
while(scanf("%d %d",&m,&n)!=EOF){}
感觉还很方便
scanf函数输入字符串的的情况下空白字符(空格,回车,Tab)都只是被视为分隔符,当我们输入字符串jj后按回车的时候就会把jj连同换行符送入缓冲区,然而scanf只会接收字符串jj,并把这个字符串送入到以a为首地址的地址空间中,同时在字符串后面自动加上一个\0,那么如今缓冲区内就只有一个换行符了。如果gets()函数时,因为缓冲区内非空,那么gets直接回读取缓冲区中的换行符复制到str为首地址的内存空间,同时在字符串后面自动加上一个\0,所以输出的str的结果就是一个换行罢了。
gets只接受换行符或EOF作为分割,如果缓冲区不为空,会把换行符读进去,并把换行符改成NULL,如果缓冲区为空,则在换行符之后加上一个NULL ,而不是把换行符改成NULL
getchar()会接收缓冲区中的而第一个字符,如果缓冲区是空的,那么程序便会停在gets()位置处的等待输入。
同样的,在cin中也有类似的问题,cin.getline是能够连着回车一并输入,但是直接cin就会把回车留在缓存里,getline就是读行的意思,回车自然也在行里
无缓冲输入getch()
getchar与getc用法略有不同,都使用返回值,不同的是getc需要指定文件参数,getchar的用法就是当系统发来一段缓存的时候,(因为是回车发来的,因此后面必有回车),然后getchar进行循环读取,(看你是不是while了),然后getchar主要针对这个缓存进行循环
windows中在conio.h
8.输入输出重定向
freopen(“d:\data_in.txt”,“r”,stdin); 将输入定向为文件d盘下的文件data_in.txt文件
freopen(“d:\data_out.txt”,“w”,stdout); 将输出定向到d盘下的data_out.txt目录。如果进行了输入输出重定向的话,那么scanf就应该添加\n了,如果后面没有\n的话,说实在的,它也应该不会出错的,到不是数字的时候自动停止,应该不会出错的。也就是说,即便后面是什么鬼都没关系,就这样,更方便了呢
9.ifstream fin(“wormhole.in”);
fin >> N;
fin.close();
ofstream fout(“wormhole.out”);
fout << solve() << “\n”;
fout.close();
就是这么用,别忘了关闭就行了
cin>>a>>b;