C/C++的30个冷知识

数据格式详解

输入输出函数详解

字符串处理函数详解

内存函数详解

类详解

数据格式详解
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<double*>(vptr);

C11增加了一些新特性,and,or,not 何以取代&& || !真方便!

指针从本质上讲是一个变量,变量的值是另一个变量的地址,指针在逻辑上是独立的,它可以被改变的,包括指针变量的值(所指向的地址)和指针变量的值对应的内存中的数据(所指向地址中所存放的数据)。

引用从本质上讲是一个别名,是另一个变量的同义词,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化(先有这个变量,这个实物,这个实物才能有别名),而且其引用的对象在其整个生命周期中不能被改变,即自始至终只能依附于同一个变量(初始化的时候代表的是谁的别名,就一直是谁的别名,不能变)。引用在定义时需要加上&,而在使用时不用加&,在函数定义参数时要添加&,而不是特别定义一个引用然后使用。

用assert启用断言,一般是对预计可能出现的错误函数参数或者溢出变量进行判断,需要时添加#include <assert.h> 不用时添加#define NDEBUG

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,–是重载了的,而-没有重载,所以会报错的,而且注意是前面自减才对。

要用而不要用<iostream.h>,后者只是仅仅支持字符流,而前者包含了一系列模板化的I/O类,二者在接口和执行上都是不同的。

在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;

count<<a<<b<<endl;

cout也是支持格式化输出的,只不过是特别恶心,例如让浮点数保留两位小数输出

cout << setprecision(2) << 3.1415926…注意会四舍五入,还必须要加上#include “iomanip”

printf不支持string,因此只能这么干:printf("%s",str.c_str()); 所以不推荐,因此cout和printf混用就好了

其中,另外,gets在C ++中会产生bug,而且在C11标准中 被废除,因此不建议使用,getline(cin, str)函数只能读取string类型,不能读取字符数组类型,cin.get可以读取字符数组类型,并且只会遇到回车而结束。用法为cin.get(ch,长度),另外需要注意的是,这个函数会将换行符留在输入队列中,如果连续两次调用,第二次将无法读入,应加上一个不带参数的cin.get吃掉换行符,推荐用cin.ignore(),因为看起来自带注释的感觉,另外cin>>noskipws也有读取空格字符串的功能

一般的,常会有使用一次cin之后连续多次使用getline,但是,由于cin不读入空格的特性,getline总会少输入一行,所以正确姿势是用cin.get()或者cin.ingnore(),感觉用后者更自带注释一些

8.1."\n" 表示内容为一个回车符的字符串。std::endl 是流操作子,输出的作用和输出 “\n” 类似,但可能略有区别。

std::endl 输出一个换行符,并立即刷新缓冲区,由于流操作符 << 的重载,对于 ‘\n’ 和 “\n”,输出效果相同。对于有输出缓冲的流(例如cout、clog),如果不手动进行缓冲区刷新操作,将在缓冲区满后自动刷新输出。不过对于 cout 来说(相对于文件输出流等),缓冲一般体现得并不明显。但是必要情况下使用 endl 代替 ‘\n’ 一般是个好习惯。

对于无缓冲的流(例如标准错误输出流cerr),刷新是不必要的,可以直接使用 ‘\n’。

9.printf,sprintf,fprintf是三个亲兄弟,分别是输出到标准输出,字符串,文件。

11.fgetc(file)为从文件中读取一个字符,返回int型,而getchar()是从标准输入输出中获取一个字符,它等价于fgetc(stdin)

12.fgets(buff,MAXN,fin)将读取完整的一行存放到buff字符数组中,而且往往是以\n结尾(除了在文件结束前没有遇到\n这种特殊情况)。当一个字符也没有读到,函数返回null。同样有一个标准输入板的gets(s)函数,里面只有一个数组参数,风险较大,不建议使用。而在scanf中,是不包括\n的,但是也不能在接受字符串中打上\n,回车是一个输入完成键,在scanf与fgets混用时(我为什么要混用?可能以后再也不会混用了)要注意这个点。

13.在用fgets读取整段函数时,每一个段后面会有一个回车符需要减一,而最后一个段没有回车符,用一个0代替,好像也会录入,注意fgets与fscanf的不同,因为fscanf不吸收\n\r,因此交替使用时,要用scanf("…\n\r",…)吸收掉段尾标志才行。因为在文件里,没有敲回车代表完成输入的。

14.fgetc(pFile)从pFIle文件读取一个字符,返回值就是此字符,fputc(ch,pFile)也是这样,若失败了,返回EOF,为-1,而fegts(str,n,pFile)倘若失败,则返回NULL即0

15.fgets,fput,fprintf,fscanf,fread,fwrite(地址,要读写的字节数,每个数据项长度,文件指针)此为二进制方式

16.rewind(文件指针)使文件位置标记指向文件开头,fseek(文件类型指针,位移量,起始点0,1,2)

17.在C++中,getch已经变为_getch,我也不知道这会有什么意义

对于上下左右和别的一些扩展键使用getch会先返回一个224,再使用一次getch()这时返回的才是扫描码

fopen_s函数在安全性上有所提高,通过输入一个错误指针达到安全目的

printf返回字符串中字符个数

gettext函数是tc的函数,在vs里自然是没有的

输出带空格的方式有新方法了,可以printf("%d%s", a[i], (i-n+1?" “:”\n"));,虽然判断还是需要判断,但是真的很简洁了,非常不错,就是这样,很棒

字符串处理函数详解
知道如何把字符数组赋给字符串类吗?只能while循环,然后+=粘合!否则没有别的办法。

6.数组批量复制函数memcpy,数组清零函数memset函数,类似于a[100] = {0}只能在定义时使用,它们都在string.h中,善于利用这些函数,可以不用写for循环了。

10.头文件ctype.h中定义的isalpha(判断是否为大小写字母),isdigit(是否为十进制字符),isprint(是否为可打印字符)等工具可以用来判断字符属性,而toupper(返回字符的大写形式),tolower(返回字符的小写形式)等工具可以用来转换大小写。

14.c语言中的确有字符串转int型函数,为atoi函数,在stdlib里

strlen返回字符串长度的

内存函数详解
关于memset,只用它来初始化0就行了,初始化其他的,全错!相信我,memset函数也是以字节为单位进行赋值的,对于int型,是四个字节,也就是将这四个字节设置成0x01010101, 转换成十进制就是16843009。memset的作用是来将一段内存按自己进行初始化, 并非用来进行变量初始化。

C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小.下面是 realloc() 函数的声明。

void *realloc(void *ptr, size_t size)

ptr – 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。另外,ptr必须是经过malloc创建的ptr,如果ptr参数是某个数组的首地址,或者用一个指针指向这个数组的首地址,然后传进去,也是不行的,必须是malloc得来的地址

size – 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。

该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。

sizeof测量字符串指针时,只会返回四字节,因为测量的是指针,但用sizeof测量字符串数组头节点的时候,返回的是包括0的所有长度,而用strlen的话,不包括后面的0,可以同时测量字符串数组和字符串指针

关于realloc与字符串之间的关系,因为所有的指针都是一个四字节的整型

在C++中,可以用new完全替代malloc,现在用malloc主要就是用来开辟一个结构或者一个结构数组,通过 MyStruct *ms = new MyStruct; 或者MyStruct *ms = new MyStruct[n];来完成,删除的话请用delete ms;完成,如果删除的是个数组,必须加方括号delete[] ms;来完成,因此从现在开始,由于new可以动态创建数组,而且可以方便替换,不需要加库函数,就用new创建数组就好了

new一个东西,是不会初始化的,要初始化,也可以

new一个数组同时给它初始化怎么办?没有正规的方法,这个C、C++没办法实现的,只能先定义然后赋值。但可以这么来:

typedef int arrT[10]; //arrT表示10个int的数组类型

int *pi1 = new arrT; //10个未初始化的int

int *pi2 = new art10; //10个初始化为0的int

new[](new的数组版)要求元素对象的类型必须具有默认构造函数(内建类型的“默认构造函数”是什么也不做),否则将不能使用new[]

类详解
类和struct都是按照by var方式传递的,但类会使用拷贝构造函数。

值得注意的是,c++的结构体是可以有构造函数的,这也可以说,如果构造一个链表结构体的话,那么就非常有用了是不是,在销毁的时候顺便释放空间什么的,结构体中可以包含函数;也可以定义public、private、protected数据成员,结构体定义中默认情况下的成员是public,而类定义中的默认情况下的成员是private的。类中的非static成员函数有this指针,(而struct中没有是错误的,一直被误导啊,经过测试struct的成员函数一样具有this指针),类的关键字class能作为template模板的关键字 即template class A{}; 而struct不可以。

C++中定义结构体变量时可以不加struct关键字,也就是说,typedef可以在c++中省掉了。

如果手工定义了任何构造函数,那么就不会再提供默认无参构造函数

如果有无参数构造函数或者全部默认参数的构造函数,则不应该加括号,这个与代表声明函数语法矛盾。

Private是缺省的默认值,如果放在最前面,会自动获得缺省访问控制权限。

操作符重载方法:friend Class operater+(const

Class& a, const Class& b){

Class t;

   t.x= a.x + b.x;

   returnt;  // ok完成

}//注意一定要加friend,否则不会访问到别的类的隐私属性

Struct允许在初始化的时候用这种方式初始化:Struct s = {1, 2};但前提是有这个变量才行。

函数式创建一个类Class c(1,2,3),但是要求相应的构造函数才行

创建对象如果不给出对象名,则会产生一个无名对象,在使用一次后消失,一般来说是有它自己的作用的

如果类中有全部默认参数的函数和无参数的函数,那么编译器会报错,实际上只要是出现矛盾的地方,编译器都会报错。

以初始化列表的形式进行成员初始化,在构造函数后加冒号实现,看起来不伦不类,但是它能解决引用,常量,对象属性的初始化,超级实用,因为在构造函数体中是不能完成对常量成员和引用成员的初始化的,但它的初始化顺序是按照定义参数顺序进行的。

用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。不然子类的析构函数是不会使用的,那么就会造成资源不被释放。虚析构函数使得在删除指向子类对象的基类指针时可以调用子类的析构函数达到释放子类中堆内存的目的,而防止内存泄露的.当父类的析构函数声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,先调动子类的析构函数,再调动父类的析构函数。

不建议使用全局对象,因为debug只能从main处进入,而类的初始化在main开始之前,所以根本没办法调试。另外,由于全局变量创建顺序完全不可控,更不要让全局变量之间相互依赖。

对象赋值不进行内存分配的是浅赋值,浅赋值只是进行属性的分配,不会调用构造函数,这个虽然不能说是相当危险吧,如果不进行内存分配就使用它默认存在的拷贝函数,但是涉及到内存分配,指针什么的,就必须显式使用构造函数,析构函数,拷贝函数。自定义拷贝构造函数在对象本体与对象实体不一致的时候,便是需要的,否则无此必要。

友元是针对类函数而言,能够在这个函数里直接使用别的实例的私有变量

静态成员是默认初始化为0的。如果想初始化为别的,那么可以在类实现部分用int什么的初始化,不一样,全局变量式的定义方法加一个类空间。不能重复static关键字。静态成员访问可以通过对象,也可以直接通过类名。不能把静态成员的初始化放置在类的定义中。静态成员变量在类中仅仅是声明,没有定义,所以要在类的外面定义,实际上是给静态成员变量分配内存。如果不加定义就会报错,初始化是赋一个初始值,而定义是分配内存。也就是说,如果没有赋初值,是不能用的。

静态成员可以用来创建有限实例,例如可以将构造函数私有化,那么它就不能被实例化,然后在静态函数里创建有限个实例,那么就可以了。

静态成员函数仍然有访问私有数据的权力。

友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下:

   friend 类型 函数名(形式参数);

友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。一个函数可以是多个类的友元函数,只需要在各个类中分别声明。

友元函数的调用与一般函数的调用方式和原理一致。普通函数可以作为友元函数,其实一个类中的函数也是可以作为另一个类的友元函数,但是要加上空间限定符,例如一个普通的函数,它定义为一个类的友元函数后,它就可以使用那个类的私有变量,也就是说,友元函数定义到哪里,哪里就要奉献是这个意思。但是如果说类的函数作为友元函数,那么它怎么调用呢?在风险类中,它只是被声明了一下而已,实际上,它的this指针仍然指向其母类,就是这样,没什么好说的。关键是这里有一点,如果一个类函数是另一个类的友元函数,那么它必然会有那个类的一个参数,但是如果把那个类放到下面,那么参数就会定义失效,但是如果把这个贡献私有成员的类放到上面,那么它里面定义的friend函数就没有声明过,也会错,因此应该这么做的,把贡献私有成员的类放到下面,然后在上面放上一个它的声明就可以了。你也可以这么做,把贡献私有变量的类放到前面,然后声明后面的那个 提供友元函数的类的定义,其实都是一样的。

关于友元类,如果,例如friend class A在B类的声明里面,那么B的成员就可以被共享,而A的不能被共享,也就是说:类A成员函数都是类B友元函数,能直接存取私有成员和保护成员。

使用友元类时注意:(1) 友元关系不能被继承。 (2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。 (3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

const可用于重载函数区分,但是它会放在函数的最末尾,可以调用的,但是不安全,不可移植。遗憾的是,标准C++不允许这么做。C++标准定义,除了const对象之外,有const的成员函数重载的都将被忽略。

默认参数在声明的时候可以直接省略掉参数名称,但是在函数定义的时候要有参数名称,并且不允许出现默认值,说白了,没卵用。

函数的声明和定义中,都可以省略形参名,但是就不能调用它了,同样的,没卵用。。。

在设计函数的时候,若参数是类,则多用引用传递,若参数是内部数据类型,则多用值传递

关于后自增的说明,它会返回一个临时变量,而不是它自己,在前自增中,要求函数的返回应该是它的引用类型,而后自增就是一个简单的变量就好了,要自己创建一个变量,然后送出去。。,记得在参数表后面加上一个int,唉,真是难,它们都要求参数为引用类型

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值