C++ Primer学习笔记之第六章--函数
来源:互联网 发布:淘宝店招怎么设置全屏 编辑:程序博客网 时间:2024/06/14 03:27
6.1 函数基础
1、一个典型函数的定义包括以下部分
(1)返回类型(return type)
(2)函数的名字
(3)由0个或多个形参(parameter)组成的列表
(4)函数体
2、函数的调用完成两项工作
(1)一:用实参初始化函数对应的形参
(2)二:将控制权转移给被调用函数
(3)此时,主调函数(calling function)执行被暂时中断,被调函数(called function)开始执行。
3、return语句完成两项工作
(1)一:返回return语句中的值(如果有则返回)
(2)二:将控制权从被调函数转回主调函数
6.1 节练习
练习6.4:编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘,在main函数中调用该函数。
#include<iostream>#include<stdlib.h>#include<string>#include<vector>#include<stdexcept>using std::invalid_argument;using std::endl;using std::string;using std::cout;using std::cin;using std::vector;void factorial();int main(void){ //6.1test -->6.4 factorial(); return 0;}void factorial(){ long int fac=1; long int n; cout<<"Enter the number: "; while(cin>>n) { try{ if(n<=0) throw invalid_argument("Number must greater than 0!"); }catch (invalid_argument){ char c; cout<<"Enter the number again?(y/n): "; cin>>c; if(cin||c=='y') { factorial(); } else if(!cin||c=='n') break; } for(;n>0;n--) fac=fac*n; cout<<"n!="<<fac<<endl; fac=1; cout<<"Enter the number: "; }}
练习6.5:编写一个函数输出其实参的绝对值:
#include<iostream>#include<stdlib.h>#include<string>#include<vector>#include<stdexcept>using std::invalid_argument;using std::endl;using std::string;using std::cout;using std::cin;using std::vector;void factorial();void absolute();int main(void){ //6.1test -->6.5 absolute(); return 0;}void absolute(){ int n; cout<<"Enter a number,and output the absolute value: "; while(cin>>n) { if(n>0) cout<<"The absolute number is: "<<n<<endl; else cout<<"The absolute number is: "<<-n<<endl; cout<<"Enter a number,and output the absolute value: "; } cout<<"quit!"<<endl;}
6.1.1 局部对象
1、在C++中,名字有作用于,对象有生命周期(life)。
(1)名字的作用于是程序文本的一部分,名字在其中可见。
(2)对象的生命周期是程序执行过程中该对象存在的一段时间。
2、函数体是一个语句块,块构成一个新的作用域,我们可以在其中定义变量。
(1)形参和函数体内部定义的变量,统称局部变量(local value)
(2)局部变量可隐藏(hide)外层作用域中同名的其他所有声明。
3、自动对象(auto object):只存在于块执行期间的对象。
4、局部静态对象(local static object):在程序的执行路径第一次经过对象定义语句时初始化,并且指导程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。
(1)用关键字static可定义局部静态对象。
6.1.1练习
练习6.7:编写一个函数,当它第一次被调用时返回0,以后每次调用返回值加1
#include<iostream>#include<stdlib.h>#include<string>#include<vector>using std::endl;using std::string;using std::cout;using std::cin;using std::vector;int func_calledtimes();int main(void){ //6.1.1test -->6.7 for(int i=0;i<10;++i) func_calledtimes(); return 0;}int func_calledtimes(){ static int times=0; if(times==0) { cout<<"The function has been called "<<times<<" times."<<endl; times++; return 0; } else { cout<<"The function has been called "<<times<<" times."<<endl; return times++; }}
6.1.2 函数声明
1、函数的三要素:返回类型、函数名、形参类型描述了函数的接口,说明了该函数所需的全部信息。
(1)函数声明也称作为函数原型(function prototype)
2、含有函声明的头文件应该被包含到定义函数的源文件中。
6.1.3 分离式编译
1、大多数编译器提供了分离式编译每个文件的机制,这一过程通常会产生一个后缀名是.obj(Windows)或.o(Unix)的文件。
6.2 参数传递
1、形参初始化机制与变量初始化一样。
2、形参是引用类型时,我们说它对应的实参被引用传递(pass by reference)或函数被传引用调用(called by reference)。
3、当实参的值被拷贝给形参时,形参和实参是两个相互独立的对象。
6.2.1 传值参数
1、指针形参可改变实参的值。
2、使用引用避免拷贝
(1)拷贝较大类类型对象或容器比较低效
(2)甚至有的类类型(包括IO类型在内根本不支持拷贝操作)
3、如函数无须改变引用形参的值,最好将其声明为常量引用(const &)
6.2.1 节练习
练习6.10:编写一个函数,使用指针形参交换两个整数的值。
#include<iostream>#include<stdlib.h>#include<string>#include<vector>using std::endl;using std::string;using std::cout;using std::cin;using std::vector;void change(int *,int *);int main(void){ //6.2.1test -->6.10 int a=10,b=13; cout<<"a="<<a<<endl <<"b="<<b<<endl; change(&a,&b); cout<<"a="<<a<<endl <<"b="<<b<<endl; return 0;}void change(int *a1,int *a2){ int tmp=*a1; *a1=*a2; *a2=tmp;}
6.2.4 数组形参
1、数组的两个特殊性质对我们定义和使用作用在数组上的函数有影响,这两个性质分别是:
(1)不允许拷贝数组
(2)使用数组时(通常)会将其转换为指针
2、管理数组指针形参的三种方法
(1)使用标记指定数组长度
void print(const char *cp){ if(cp) //cp不是一个空指针 while(*cp) //只要指针所指的字符不是空字符 cout<<*cp<<endl;}
(2)使用标准库规范
void print(const int *beg,const int * end){ while(beg!=end) cout<<*beg++<<endl;}
(3)显示传递一个表示数组大小的形参
void print(const int ia[],size_t size){ for(size_t i=0;i!=t.size;++i) cout<<ia[i]<<endl;}
(4)传递多维数组
void print(int (*matrix)[10],int rowSize){ /*..........*/}
6.2.5 main:处理命令行选项
1、main函数可以定义为:
int main(int argc,char *argv[]){}//等价定义int main(int argc,char **argv){}(1)argc是指命令行输入参数的个数(2)argv存在所有的命令行参数
6.2.6 含有可变形参的函数
1、initializer_list形参
(1)如果函数的实参数量未知但全部实参的类型都相同,我们可以使用initializer_list类型的形参。
(2)initializer_list是一个标准库类型(定义在同名的头文件中),用于表示某种特定类型的值的数组。
2、initializer_list提供的操作
(1)initializer_list<T> lst; //默认初始化,T类型元素的空列表(2)initializer_list<T>{a,b,c,d...};//lst的元素数量和初始值一样名,lst的元素是对应初始值的副本,列表的元素是常量。(3)lst2(lst); lst2=lst; //拷贝赋值一个initializer_list对象不会拷贝列表中的元素,原始列表和副本列表共享元素(4)lst.size(); //列表中的元素数量(5)lst.begin(); //返回指向lst中首元素的指针(6)lst.end(); //返回指向lst中尾元素下一位置的指针
3、initializer_list和vector是模版类型。
(1)与vector不同,initializer_list对象中的元素永远是常量,无法改变initalizer_list对象中元素的值。
4、例:
initializer_list<string> ls;void error_msg(initialize_list<string> ls){ for(auto beg=ls.begin();beg!=ls.end();++beg) cout<<*beg<<""; cout<<endl;}
6.2.6 节练习
练习6.27:编写一个函数,它的参数是initializer_list类型的对象,函数的功能是计算列表中所有元素的和
#include<iostream>#include<stdlib.h>#include<string>#include<vector>//#include "fuc.c"#include<initializer_list>#include<stdexcept>using std::initializer_list;using std::invalid_argument;using std::endl;using std::string;using std::cout;using std::cin;using std::vector;void sum(initializer_list<int>); int main(void){ initializer_list<int> li{1,2,3,4,5,6,7,8,9,10}; sum(li); return 0;}void sum(initializer_list<int> a){ int sum=0; for(auto il=a.begin();il!=a.end();++il) sum+=*il; cout<<"sum="<<sum<<endl;}
6.3 返回类型和return语句
1、return语句形式:
(1)return;(2)return expression;
2、返回一个值的方式和初始化变量和形参的方式完全一样;
返回的值用于初始化调用点的临时量,该临时量是函数调用的结果
3、不要返回局部对象的引用或指针。
4、返回类类型的函数和调用运算符
(1)如果函数返回指针、引用或类的对象,我们就能使用函数调用的结果访问结果对象的成员。
例:auto sz=shorterString(s1,s2).size();
5、引用返回左值
(1)调用一个返回引用的函数得到左值,其它返回类型得到右值。
(2)例:
char & get_val(string &str,string::size_type ix){ return str[ix];}int main(){ string s("a value"); get_value(s,0)='A';}
6、列表初始化
(1)C++11规定,函数可以返回{}包围的值的列表
(2)例:
vector<string> process(){ return {"abc","def"};}
6.3.3 返回数组指针
1、返回数组指针的函数形式:
(1)
Type (*function(parameter_list))[dimetion]1、Type表示数组元素的类型2、dimension表示数组的大小3、(*function(parameter_list))最外层两端的括号必须存在。
(2)例: int (*func(int i))[10];
<1>逐层理解其含义:
①func(int i):表示调用func函数时需要一个int类型的实参。
②(*func(int i)):意味着我们可以对函数调用结果执行解引用操作
③(*func(int i))[10]:表示解引用func的调用将得到一个大小是10的数组
④int (*func(int i))[10]:表示数组中的元素是int类型
2、使用尾置返回类型
(1)C++11中有一种建华上述声明的方法,就是使用尾置返回类型(trailing return type)
(2)任何函数的定义都能使用尾置返回,但这种形式对于返回类型比较复杂的函数最有效。
如:返回类型是数组的指针或数组的引用,函数指针等。
(3)尾置返回类型跟在形参列表后并以一个->符号开头
(4)例:func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组
auto func(int i)->int(*)[10];
3、使用decltype
(1)例:
int odd[]={1,3,5,7,9};int even[]={0,2,4,6,8};decltype(odd) *arrPtr(int i){ return (i%2)?&odd:&even; //返回一个指向数组的指针。}
(2)arrPtr使用关键字decltype表示它返回一个指针,并且该指针所指的对象与odd类型一致,类型含有5个整型的数组。
注意:decltype并不负责把数组类型转换为指向数组的指针,所以必须在函数声明时加一个符号*
6.3.3 节练习
练习6.36:编写一个程序,使其返回数组的引用并且该数组包含10个string对象。不要使用尾置返回类型,decltype或者类型别名。
#include<iostream>#include<stdlib.h>#include<string>#include<vector>#include<initializer_list>#include<stdexcept>using std::initializer_list;using std::invalid_argument;using std::endl;using std::string;using std::cout;using std::cin;using std::vector;string (& return_str_arr(string (* str)[10] ) ) [10];int main(void){ //6.3.3test -->6.36 string st[10]={"Hello","Arch","CPrimer"}; string * stp=st; stp=return_str_arr(&st); cout<<"st type is string [10]:"<<endl; for(size_t j=0;j<10;j++) cout<<st[j]<<endl; cout<<"stp type is also string [10]:"<<endl; for(size_t j=0;j<10;j++) cout<<stp[j]<<endl; return 0;}string (& return_str_arr(string (*str)[10]) ) [10]{ for(size_t i=0;i<10;++i) (*str)[i]="Hey."; return *str;}
练习6.37:为6.36的函数再写三个声明,一个使用类型别名,一个使用尾置返回类型,最后一个使用decltype关键字
//(1)使用类型别名版本:using str_arr_ref=string(&)[10];//等价定义于typedef string(&str_arr_ref)[10] ;str_arr_ref return_str_arr(string (*str)[10]){ for(size_t i=0;i<10;++i) (*str)[i]="Hey."; return *str;}//(2)使用尾置返回类型//声明部分为:auto return_str_arr(string (*str)[10])->string(&)[10];//定义部分:auto return_str_arr(string (*str)[10])->string(&)[10]{ for(size_t i=0;i<10;++i) (*str)[i]="Hey."; return *str;}//(3)decltype关键字声明及定义string st[10]={"Hello","Arch","CPrimer"}; decltype(st) & return_str_arr( string (*str)[10] ) { for(size_t i=0;i<10;++i) (*str)[i]="Hey."; return *str; }
6.4 函数重载
1、如果同一作用域内几个函数的名字相同,但形参列表不同,我们称之为重载(overloaded)函数。
2、main函数不能重载
3、重载和const形参
(1)顶层const不影响传入函数的对象,一个拥有顶层const的形参无法和另外一个没有顶层const的形参区分开来。
(2)例:
Record lookup(Phone);Record lookup(const Phone);//重复声明Record lookup(Phone)Record lookup(Phone *);Record lookup(Phone * const);//重复声明Record lookup(Phone *);
(3)如果形参是某种类型的指针或引用,可通过区分其指向的是常量对象还是非常量对象可以实现函数重载。此时的const是底层的。
例:
Record lookup(Account &); //函数作用于Account的引用Record lookup(const Account &); //作用于常量引用(重载函数)Record lookup(Account *);Record lookup(const Account *);//重载函数,作用于指向常量的指针。
4、const_cast和重载
(1)有一个选出最短字符串的函数,该版本为:
const string & shorterString(const string &s1,const string &s2){ return s1.size()<=s2.size()?s1:s2;}//该函数版本即使实参不是常量,但返回仍是const string的引用。
(2)const_cast版本的,当它的实参不是常量时候,得到的结果是一个普通的引用。
string & shorterString(string &s1,string &s2){ auto &r = shorterString(const_cast<const string &>(s1),const_cast<const string &>(s2)); return const_cast<string &>(r);}//能得到普通引用的原因:const_cast转换可以去掉底层const,并且这一过程安全。
(3)const_cast版本函数执行流程:将实参强制转换为对const的引用(事实上这个实参强制仍然是绑定这一个普通的引用,而非常量引用),然后调用shorterString函数的const版本。const版本返回const string的引用(事实上该引用为普通引用)。然后被强制转换为普通引用。
5、调用重载函数
(1)函数匹配(function match):把函数调用与一组重载函数中的某一个关联起来的过程。
函数匹配也叫做重载确定(overload resolution)
(2)调用重载函数时有三种可能的结果
<1>编译器找到一个实参最佳匹配(best match)的函数,并生成调用该函数的代码。
<2>找不到任何一个函数与调用的实参匹配,此时编译器发出无匹配(no match)的错误。
<3>有多余一个函数可以匹配,但每一个都不是最佳选择。此时也将发生错误,称为二义性调用(ambiguous call)。
6.4.1 重载与作用域
1、重载对作用于的一般性质并没有什么影响。但在内层作用于中声明名字,它将隐藏外层作用于中同名的实体。
2、在不同作用域中无法重载函数名。
3、在C++中,名字查找发生在类型检查之前。
6.5 特殊用途语言特性
6.5.1 默认实参
1、在函数的很多调用它们都被赋予一个相同的值,我们把这个反复出现的值称为函数的默认实参(default argument)
2、例:
typedef string::size_type sz;string screen(sz ht=24,sz=wid=80,char backgrnd=' ');
6.5.2 内联函数和constexpr函数
1、内敛函数可比main函数调用的花销
(1)将函数指定为内敛函数(inline),通常是将它在每个调用点“内联地”展开。
(2)例:
inline const string & shorterString(const string &s1,const string &s2) { return s1.size()<=s2.size()?s1:s2; }
2、内联说明只是想编译器发出一个请求,编译器可以选择忽略这个请求。
3、一般来说,内联几只用于优化规模较小,流程直接,调用频繁的函数。
4、constexpr函数
(1)constexpr函数是指能用于常量表达式的函数。
5、constexpr函数遵循几项约定:
(1)函数返回类型及所有形参的类型都得是字面值类型。
(2)并且函数体中必须有且只有一条return语句。
(3)例:
constexpr int new_sz(){return 42;};constexpr int foo=new_sz(); //正确,foo是一个常量表达式
6、constexpr函数不一定返回常量表达式。
7、把内联函数放在头文件(头文件最后加#ifndef)中
(1)和其它函数一样,内联函数和constexpr函数可以在程序中多次定义。
原因:编译器要想展开函数声明和定义。
6.5.3 调试帮助
1、assert 预处理宏
(1)assert是一种预处理宏(preprocessor marco)。
所谓预处理宏其实是一个预处理变量。assert红使用一个条件表达式作为它的条件:
(2)格式
assert(expr);
(3)首先对expr求值,如果表达式为假(0),assert输出信息并终止程序的执行。
若expr为真(非0),assert什么都不做。
(4)assert定义在cassert头文件中。
2、assert宏常用于检查“不能发生的条件”。
例:
assert(word.size()>threshold);
3、NDEBUG预处理变量
(1)定义NDEBUG能不执行运行时检查
4、C++编译器定义的_ _func_ _之外,预处理器还定义了4个对于程序调试很有用的名字:
1、 __FILE__ :存放文件名的字符串字面值2、 __LINE__ :存放当前行号的整型字面值3、 __TIME__ :存放文件编译时间的字符串字面值4、 __DATE__ :存放文件编译日期的字符串字面值
6.5.3 节练习
练习6.47:改写一下程序,使其有条件地输出与执行过程有关的条件。例如,每次调用时输出数组元素的大小。分别在打开和关闭调试器的情况下执行该程序:
修改的程序:int &get(int *array,int index){ return arry[index];}int main(){int ia[10];for(int i=0;i!=10;++i) get(ia,i)=i;}//启动assert调试:include<iostream>#include"stdlib.h"#include<string>#include<vector>#include<cassert>using namespace std;using std::string;using std::endl;using std::cout;using std::cin;int &get(int *array,int index){ return array[index];}int main(){ int ia[10]; for(int i=0;i!=10;++i) { get(ia,i)=i; assert(ia[i]<5); cout<<ia[i]<<endl; } return 0;}//关闭assert预处理宏,在头文件#include<cassert>前定义NDEBUG预处理变量即可#include<iostream>#include"stdlib.h"#define NDEBUG#include<string>#include<vector>#include<cassert>using namespace std;using std::string;using std::endl;using std::cout;using std::cin;int &get(int *array,int index){ return array[index];}int main(){ int ia[10]; for(int i=0;i!=10;++i) { get(ia,i)=i; assert(ia[i]<5); cout<<ia[i]<<endl; } return 0;}
6.6 函数匹配
1、候选函数(candidate function)有两个特征
(1)与被调用的函数同名
(2)声明在调用点可用
2、从候选函数中选出能被这组实参调用的函数称为可行函数(viable function)
6.6.1 实参类型转换
一、实参类型和形参类型的转换划分为几个等级:
1、精准匹配,包括三种情况:
(1)实参类型和形参类型相同
(2)实参从数组类型或函数类型转换成对应的指针类型
(3)向实参添加顶层const或者从实参中删除顶层const
2、通过const转换实现的匹配
3、通过类型提升实现的匹配(如整型提升)
4、通过算数类型转换或指针转换实现的匹配
5、通过类类型转换实现的匹配
6.7 函数指针
1、函数指针指向的是函数而非对象
函数指针指向某种特定类型
函数的类型由它的返回类型和形参类型共同决定,与函数名无关。
(1)例:
bool lengthCompare(const string &,const string &);要想声明一个可以指向该函数的指针,只需用 指针替换函数名 即可bool (*pf)(const string &,const string &);
2、使用函数指针
(1)将leengthCompare的地址赋给pf
1、 pf=lengthCompare; //pf指向名为lengthCompare的函数,函数名自动转换为指针2、 pf=&lengthCompare; //等价的赋值语句,取地址符可选
(2)直接使用指向函数的指针调用该函数,无须提前解引用指针:
1、 bool b1=pf("hello","goodbye"); //调用lengthCompare函数2、 bool b2=(*pf)("hello","goodbye"); //一个等价调用
3、在指向不同函数类型的指针不存在转换规则。
4、函数指针形参
(1)函数虽然不能定义函数类型的形参,但形参可以是指向函数的指针。
例:
void userBigger(const string & s1,const string &s2, bool pf(const string &,const string &));//另一个等价声明void userBigger(const string & s1,const string &s2, bool (*pf)(const string &,const string &));
(2)我们可以把函数名作为实参使用,此时它会自动转换成指针:
useBigger(s1,s2,lengthCompare);1、用typedef定义类型别名和decltype能简化以上使用typedef bool Func(const string &,const string &);typedef decltype(lengthCompare) Func2; //等价类型//Func和Func2函数类型typedef bool (*FuncP)(const string &,const string &);typedef decltype(lengthCompare) *Func2P; //等价类型//FuncP和Func2P是函数指针类型注意:decltype()返回的是函数类型,需加*才能得到指针。//useBigger的等价声明void useBigger(const string &,const string &,Func); //编译器自动将Func函数名转换为指向函数的指针。void useBigger(const string &,const string &,Func2P);
5、返回指向函数的指针
(1)函数虽然不能返回函数类型,但能返回指向函数类型的指针。
(2)编译器不会自动地将函数返回类型当成对应指针类型处理,所以我们必须把返回类型写成指针形式。
(3)声明一个返回函数指针的函数,最简单的变法是使用类型别名
1、using F=int(int *,int); //F为函数类型 using PF=int(*)(int *,int); //PF为函数指针类型2、 PF f1(int); //正确,PF指向函数的指针。 F f1(int); //错误,F是函数类型,f1不能返回一个函数 F *f1(int) //正确:显示地指定返回类型是指向函数的指针 //等价于 int (*f1(int)) (int *,int );3、用尾置返回类型的方式声明一个返回函数指针的函数 auto f1(int)->int (*) (int*,int);
6、可将auto和decltype用于函数指针类型
例:
string::size_type sumLength(const string &,const string &);decltype(sumLength) *getFcn(const string &);
6.7 节练习
练习 6.54:编写函数的声明,令其接受两个int形参并且返回类型也是int;然后声明一个vector对象,令其元素是指向该函数的指针。
int funcint(int a=1,int b=2){ return a+b;}typedef int (*funcp) (int,int);int main(){ std::vector<funcp> vfucp; funcp first=funcint; vfucp.push_back(first); //int a=(*vfucp[0])(1,23); int a=vfucp[0](1,23); cout<<a<<endl; return 0;}
练习6.55:编写4个函数,分别对两个int值执行+、-、*、/运算,在vector对象中保存这些函数指针。
#include<iostream>#include"stdlib.h"#include<string>#include<vector>#include<stdexcept>using namespace std;using std::string;using std::endl;using std::cout;using std::cin;int sum(int a=1,int b=2){ return a+b;}int subtract(int a=1,int b=2){ return a-b;}int multiply(int a=1,int b=2){ return a*b;}int getalong(int a=1,int b=2){ try{ if(b==0) throw runtime_error("b must not equal to 0!"); }catch(runtime_error err){ cout<<err.what(); cout<<"Exit"<<endl; return 0; } return a/b;}typedef int (*funcp) (int,int);int main(){ std::vector<funcp> vfucp; vfucp.push_back(sum); vfucp.push_back(subtract); vfucp.push_back(multiply); vfucp.push_back(getalong); for(auto vfp=vfucp.begin();vfp!=vfucp.end();++vfp) cout<<(*vfp)(2,0)<<endl; return 0;}
术语表总结
1、Assert是一个预处理宏,作用于一条表示条件的表达式。当未定义预处理变量NDEBUG时,assert对条件求值,条件为假,输出一条错误信息并终止当前程序的执行。
2、链接(link):是一个变异过程,负责把若干个对象文件连接起来形成可执行程序。
3、尾置返回类型(trailing return type)在参数列表后面指定返回类型。
- C++primer(第五版)第六章函数学习笔记
- 《C++primer(第五版)》学习之路-第六章:函数
- C++ Primer学习笔记之第六章--函数
- C++primer学习笔记-第六章语句
- C++primer学习笔记之函数
- 《C Primer Plus》学习笔记之 函数
- C++Primer第五版【笔记】——第六章 函数
- C++primer第五版笔记-第六章函数
- C++Primer学习笔记第六章(6/18)语句
- C++primer第五版第六章学习笔记
- C++ Primer 学习笔记-第六章之总结
- c++primer第六章函数小结-6
- C++primer——第六章 函数
- [C++ primer]第六章笔记-函数
- 【c++ primer 笔记】第六章 函数
- C++Primer笔记 第六章 语句
- C++primer 第六章笔记 初稿
- C++primer 第六章学习小记
- Java程序员面试题集(1-50)
- jQuery选择元素
- OpenCV4Android学习之图像特征匹配算法
- 基于TI-RTOS的CC2650DK开发(3)---按钮的使用
- 十五周—字符串逆序输出
- C++ Primer学习笔记之第六章--函数
- Linux下安装Hadoop(2.7.1)详解及WordCount运行
- DatabaseTest
- 来来来, 出个题考一下你的计算机功底------程序中有某一个变量, 为什么在同一时刻有不同的值?
- [AHK]Gmailkeys-for-outlook
- 中国农历
- linux系统启动过程详解
- Matlab中Bode图--HZ显示横坐标
- 【设计模式 - 11】之享元模式(FlyWeight)