//5.2.4 给函数传递引用实参
//将函数的某个形参指定为引用,将改变给该形参传递数据的方法,使用的方法不是按值传递,
//其中实参在传递给函数之前被复制,
//而是按引用传递--即形参扮演被传递实参的别名角色,该机制消除了任何复制工作,允许函数直接访问调用函数中的实参,同时意味着,传递和使用指针时所需的取消引用操作也是多余的
/*int incr10(int& num);
int main(array<System::String ^> ^args)
{
int num = 3;
int value = 6;
int result = incr10(num);
cout<<" incr10(num) : "<<result<<endl;
cout<<" num :"<<num<<endl;
result = incr10(num);
cout<<" num :"<<num<<endl;
result = incr10(value);
cout<<" incr10(value) :"<<result<<endl;
cout<<"value:"<<value<<endl;
//result = incr10(20);
//如果企图使用数值作为incr10()的实参,将输出一条出错消息,因为编译器知道在函数内可以修改引用形参,而我们的意图却是使用常量的不时改变,这样就给可有可无的程序带来一种活力
system("pause");
return 0;
}
int incr10(int& num)
{
cout<<"value received = "<<num<<endl;
num += 10;
return num;
}*/
//注意: 输出结果清楚的说明了引用和指针之间的区别,引用是另一个变量的别名,因此可以用作引用该变量的替代方法,使用引用与使用原来的变量名是等价的
//5.2.5 使用const修饰符
/*int incr10(const int& num);
//当我们在函数头和原型中将num指定为const之后,即许下了不修改该变量的诺言,因此编译器要检查我们是否在遵守诺言
int main(array<System::String ^> ^args)
{
int num = 3;
int value = 6;
int result = incr10(num);
cout<<" incr10(num) : "<<result<<endl;
cout<<" num :"<<num<<endl;
result = incr10(num);
cout<<" num :"<<num<<endl;
result = incr10(value);
cout<<" incr10(value) :"<<result<<endl;
cout<<"value:"<<value<<endl;
result = incr10(20);
cout<<"result:"<<result<<endl;
system("pause");
return 0;
}
int incr10(const int& num)
{
cout<<"value received = "<<num<<endl;
//num += 10;
//return num;
return num + 10;
}
//通过使用引用实参,我们现在得到两个方面的好处,一方面,我们可以编写直接访问调用者实参的函数,而且避免了按值传递机制中隐式的复制开销,
//另一方面,如果我们不打算修改某个实参,则只需要给引用使用const修饰符,就能避免该参数被意久修改
*/
//5.2.6 main()函数的实参
/*int main(int argc, char* argv[])
{
//第一个形参是包括程序序名在内的命令行上出现的字符串数量,
//第二个形参是个数组,它包含指向这些字符串的指针,有一个为空值的附加元素
cout<<endl<<"argc="<<argc<<endl;
cout<<"Command line arguments received are:"<<endl;
for(int i=0; i<argc; i++)
{
cout<<"argument "<<(i+1)<<":"<<argv[i]<<endl;
}
system("pause");
return 0;
}
int main(array<System::String ^> ^args)
{
Console::WriteLine("args->Length:{0}",args->Length);
system("pause");
return 0;
}*/
//5.2.7 接受数量不定的函数参数
//可以将函数定义成能够接受任意数量的实参,通过将省略号(3个句点...)写在函数的定义中形参列表的最后,即可表示调用该函数时可以提供数量可变的实参
//int sumValues(int first, ...){}
//函数定义中至少要有一个普通形参,也可以有多个,省略号必须总是放在形参列表的最后
//本地C++库在stdarg.h头文件中定义了va_start va_arg 和va_end宏 以帮助我们做这件事,
//接收数量不定的实参
/*#include <cstdarg>
//第二个形参具有特殊的标记值,以使进行检查和确认
int sum(int count, ...)
{
if(count <= 0)
{
return 0;
}
//为了处理可变的实参列表,我们首先声明一个va_list类型的指针
va_list arg_ptr;
//va_start宏用来初始化arg_ptr,使其指向列表中的第一个实参
va_start(arg_ptr, count);
//这个宏的第二个实参是函数形参列表省略号前面固定形参的名称,用来确定第一个可变实参的位置
int sum = 0;
for(int i=0; i<count; i++)
{
sum += va_arg(arg_ptr, int);
}
//va_arg宏返回arg_ptr指向的位置存储的实参值,并使arg_ptr递增,以指向下一个实参值
//va_arg宏的第二个实参是第一个实参的类型,决定着我们得到的数值以及va_arg递增的方式,因此如果该实参不正确,将造成混乱
//程序也许会执行,但是得到的数值将是无用的数据,而且有arg_ptr将被错误的递增,从而指向其它无用数据
va_end(arg_ptr);
//当结束检索实参值之后,用下面这条语句使arg_ptr复位
return sum;
}
int main()
{
cout<<sum(6,2,4,6,8,10,12)<<endl;
cout<<sum(9,11,22,33,44,55,66,77,88,99)<<endl;
system("pause");
return 0;
}*/
//5.3 从函数返回值
//5.3.1 反回指针
// return &value; 返回写成
// double* treble(double data); //定义写成这样
/*
double* treble(double);
int main()
{
double num = 5.0;
double* pnum = 0;
pnum = treble(num);
cout<<"Three times num = "<<3.0*num<<endl;
cout<<"result = "<<*pnum<<endl;
delete pnum;
system("pause");
return 0;
}*/
/*double* treble(double data)
{
double result = 0.0;
result = 3.0 * data;
return &result; //这里是错误的
//这所以出现错误,是因为函数treble()中的变量result是在该函数开始执行时创建的,并在该函数退出时消毁,因此指针ptr指向的内存不再包含原来的变量值,先前分配给result的内存现在可用于其他目的
//这里显然已经有其他数据使用过这块内存了
}*/
//1 返回地址的规则
//有一条绝对不能违反的规则是:
//永远不要从函数中返回局部自动变量的地址
/*
double* treble(double data)
{
double* result = new double(0.0);
*result = 3.0 * data;
return result;
//没有将result声明为double类型,我们现在将其声明为double*类型,并将new运算符返回的地址存入其中,因为result是指针,所以我们修改函数的其余部分以反映这一点,最后,result中包含的地址被返回给调用程序,我们可以用这个版本代替上一个示例中的treble()函数.
}*/
// 5.3.2 返回引用
/*double& lowest(double values[], int length);
int main()
{
double ar[] = {3.0,10.0,1.5,15.0,2.7,23.0,
4.5,12.0,6.8,13.5,2.1,14.0};
int len = sizeof(ar) / sizeof(ar[0]);
for(int i=0; i<len; i++)
{
cout<<setw(6)<<ar[i]<<" ";
}
cout<<endl;
lowest(ar,len) = 6.9;
lowest(ar,len) = 7.9;
for(int i=0; i<len; i++)
{
cout<<setw(6)<<ar[i]<<" ";
}
system("pause");
return 0;
}
double& lowest(double values[], int length)
{
int j=0;
for(int i=1; i<length; i++)
{
if(values[j] > values[i]){ //找出最小值
j = i;
}
}
return values[j];
//尽管这条语句看起来与返回单值的语句相同,但因为返回类型被声明为引用,所以实际返回的数组元素a[j]的引用,
//而不是该元素包含量的数值,a[j]的地址用来初始化被返回的引用,该引用是编译器创建的,因为返回类型被声明为引用
//不要混淆返回&a[j]与返回引用,如果我们将返回值写作&a[j],则指定的是a[j]的地址-那是个针
//如果我们将回类型指定为引用之后还这样写,
//返回引用的规则
//返回指针的规则同样适用于返回引用
//永远不要从函数中返回对局部变量的引用
}
*/
//5.3.3 函数中的静态变量
//在具有多个执行线程的应用程序中,全局变量同样是危险的,因此我们必须特别注意管理从不同线和中访问全局变量的方式,
//当多个线程都可以访问某个全局变量时,必须处理的基本问题是:
//一个线程使用全局变量时,另一个线程可以修改访变量的值,这样的情况下,最好的解决方案是完全避免使用全局变量
//static int count = 0;
//注意: 函数内静态变量的初始化仅仅发生在第一次调用该函数的时候,事实上,初次调函数时将创建并初始化静态变量
//之后,该变量的程序执行期间将继承存在,退出函数时该变量包含的任何数值都可以在下次调用函数时使用
/*void record(void);
int main(array<System::String ^> ^args)
{
record();
for(int i=0; i<=3; i++)
{
record();
}
system("pause");
return 0;
}
void record(void)
{
static int count = 0;
cout<<"This is count "<<++count;
if((count > 3 ) && (count<21))
{
cout<<" th ";
}else{
//如果取10的余数
switch(count % 10)
{
case 1:
cout<<" st ";
break;
case 2:
cout<<" nd ";
break;
case 3:
cout<<" rd ";
break;
default:
cout<<"th";
break;
}
}
cout<<endl;
cout<<" time i have been called "<<endl;
cout<<"=============================="<<endl;
return;
}*/
//注意:
//return语句,因为函数的返回类型是void,所以该语句如果返回数值将导致编译器错误,在这种特殊情况下,我们实际上不需要return语句
//因为直接写出函数体的闭大括号就等价于没有返回值的return语句,即使我们不包括return,该程序将也正确编译和运行
//5.4 递归函数调用
//当函数包含对自身的调用时,我们称之为递归函数,递归的函数调用也可以是间接的,即函数fun1调用函数fun2,后者再调用fun1
/*double power(double x, int n);
int main(array<System::String ^> ^args)
{
double x = 2.0;
double result = 0.0;
for(int i=-3; i<=3; i++)
{
cout<<x<<" to the power "<<i<<" is "<<power(x, i)<<endl;
}
system("pause");
return 0;
}
double power(double x, int n)
{
if(n < 0)
{
x = 1.0 / x;
// 1.0 / 2 = 0.5
n = -n;
//3 = -(-3);
//n = 3;
//cout<<endl<<" n: "<<n<<endl;
//处理负指数值(x-n 等价于1/xn次方)
}
//支持负数次幂很容易,只需要昨用x(-n)等价于(1/x)(n)的事实即可,
//因此如果n是负数,我们就将x修改为1.0/x,
//并改变n的符号使其成为正数
if(n>0)
{
return x*power(x, n-1);
}else{
return 1.0;
}
}*/
//5.5 C++/CLI编程
//C++/CLI与本地C++的细微差别
//1 CLR程序中函数形参和返回值可以是数值类类型,跟踪句柄,跟踪引用和内部指针
//2 当某个形参是数组是,不需要别一个单独的形参来指定该数组的大小,因为C++/CLI数组的大小存储在Length属性中
//3 在C++/CLI程序中,我们不能像在本地C++程序中那样执行地址算术,因此必须始终使用数组索引
//4 返回CLI堆上分配的内丰的句柄不是什么难题,因为垃圾回收器负责释放那些不再被使用的内存
//5 C++/CLI中接收数量可变实参的机制与本地C++程序的机制不同
//6 在C++/CLI程序中,main()函数访问命令行实参的机制也与本地C++不同
//5.5.1 接受数量可变实参的函数
//int sum(... array<int>^ args){}
/*double sum(... array<double>^ args)
{
double sum = 0.0;
for each(double va in args)
{
sum += va;
}
return sum;
}
int main(array<System::String ^> ^args)
{
Console::WriteLine(sum(3.0,4.0,6.0,8.0,10.0,12.0));
Console::WriteLine(sum(1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9));
system("pause");
return 0;
}*/
//5.5.2 main()的实参
/*int main(array<System::String ^> ^args)
{
Console::WriteLine(L"There were {0} command line arguments.", args->Length);
Console::WriteLine(L"Command line arguments received are:");
int i = 1;
for each(String^ str in args){
Console::WriteLine(L"Argument {0} : {1}", i++, str);
}
system("pause");
return 0;
}*/
//5.6 小结
//1 函数应该是具有明确目的小型代码单元,通常情况下,程序应该是由大量的小函数,而非少量大函数组成
//2 必须在调用程序中定义的函数之前,为该函数提供函数的原型
//3 使用引用给函数传值可以避免传值调用的实参传递机制中隐含和复制开销,应该将函数中不需要个和改的形参指定为const
//4 从本地C++函数中返回引用的指针时,应该确保被返回的对像具有正确的作用域,永远不要返回本地C++函数的局部对像的指针或引用
//5 在C++/CLI程序中,返回动态分配的内存的句柄没有任何问题,因为垃圾回收器,负责删除哪些不再需要的内存
//6 给函数传递C++/CLI数组时,不需要要另一个形参来指定数组的长度,因为在函数体中可以访问数组的Length属性来得到元素的个数
//5.7 练习
//练习题一
/*int Rank(int i);
int main(array<System::String ^> ^args)
{
for(int i = 5; i>= 3; i--)
{
cout<<"Rank(i): "<<Rank(i)<<endl;
}
system("pause");
return 0;
}
//i 值为5
//index 值为需要向下的值
int Rank(int i)
{
//刚才写的,一直没判断1的情况下该返回什么值,只知道递归,而递归到最后就不知道返回什么了
//向你太二了解
if(i == 1){
return i;
}else{
return i * Rank(i-1);
}
}*/
//练习题二
/*void Swap(int* x, int* y);
int main(array<System::String ^> ^args)
{
int x=5, y= 55;
cout<<"x:"<<x<<" y:"<<y<<endl;
Swap(&x, &y);
cout<<"x:"<<x<<" y:"<<y<<endl;
system("pause");
return 0;
}
void Swap(int* x, int* y)
{
int i = *x;
*x = *y;
*y = i;
}*/
//练习题三
/*const double DEC_TO_RAD = 57.2957795;
double sind(double d)
{
return sin(d/DEC_TO_RAD);
}
double cosd(double d)
{
return cos(d/DEC_TO_RAD);
}
double tand(double d)
{
return tan(d/DEC_TO_RAD);
}
int main(array<System::String ^> ^args)
{
cout<<"cos(30) = "<<cosd(30.0)<<endl;
cout<<"sin(30) = "<<sin(30.0)<<endl;
cout<<"tan(30) = "<<tan(30.0)<<endl;
system("pause");
return 0;
}*/
//练习题四
/*void GetData(int& number, char name[])
{
cout<<"请输入一个数值(0退出):";
cin>>number;
if(number != 0)
{
cout<<"请输入一个小于15和字符串"<<endl;
cin>>name;
}
}
void PrintData(const int& number,const char name[])
{
cout<<"输入的number:"<<number<<endl;
cout<<"输入的name:"<<name<<endl;
}
int main(array<System::String ^> ^args)
{
int number=0;
char name[15];
for(;;)
{
GetData(number, name);
if(number == 0){
break;
}
PrintData(number,name);
}
system("pause");
return 0;
}*/
//练习题五
/#include <cstring>
char* parse(const char* str)
{
static char* pStr = 0; //定义一个静态char型指针pStr
static size_t len;
static size_t start = 0;
size_t pos;
char* pReturn;
//如果存在str
if(str)
{
delete pStr; //释放指针
len = strlen(str); //取得字符串长度
pStr = new char(len+1); //实例化一下pStr指针,长度为str的长度加1,因为后面的一个字符串结束符
strcpy_s(pStr,len+1, str); //将str中的内容copy到pStr中去
}
if(start >= len)
{
return 0;
}
//从'开始'遍历字符串,直到我们找到一个空白或结束
for(pos = start; pStr[pos] != ' ' && pStr[pos]!='\0'; pos++);
//将pos设置为pStr的长度
//复制的字符串,如果我们一个词来返回,否则返回NULL
if(pos != start) //当pos不等于start时进行if
{
pReturn = new char[pos - start + 2];
//第一次运行
//pos为字符的长度,start为0,+2
size_t i = 0;
for(size_t j = start; j<pos; i++,j++)
{
pReturn[i] = pStr[j];
}
pReturn[i] = '\0';
start = pos+1;
return pReturn;
}else{
return 0;
}
}
int main(array<System::String ^> ^args)
{
char s1[] = "seventy-one fruit balls, please Doris";
cout<<"string is :"<<s1<<"Parsing......"<<endl;
char *p = parse(s1);
while(p)
{
cout<<p<<endl;
//cout<<"s1:"<<s1<<endl;
p = parse(0);//想通了,因为里面用的是静态变量值,和静态量指针,所以这里用0也是可以的
}
system("pause");
return 0;
}