/* //学习内容 数组及其使用方法 如何声明和初始化不同类型的数组 如何声明和使用多维数组 指针及其使用方法 如果声明和初始化不同类型的指针 数组和指针之间的关系 引用的概念及声明方法,关于使用引用的几点初步建议 如何在本地C++程序中给变量动态分配内存 如何在CLR程序中动态分配内存 跟踪句柄和跟踪引用的概念,CLR程序中需要它们的原因 如何在C++/CLI程序中处理字符串和数组 内部指针的概念,创建和使用内部针的方法 4.1 处理多个相同类型的数据值 4.1.2 声明数组 #include "stdafx.h" #include <iostream> #include <iomanip> using namespace std; int main() { const int MAX = 20; double gas[MAX]; long miles[MAX]; int count = 0; char ch = 'y'; while((ch=='Y' || ch=='y') && count < MAX) { cout<<"气油的价格:"; cin>>gas[count]; cout<<endl; cout<<"请输入miles数值:"; cin>>miles[count]; count++; cout<<"是否继续执行(y/n)"<<endl; cin>>ch; } if(count <= 1) { cout<<"对不起,没有有效数据"<<endl; return 0; } for(int i=1; i<count; i++) { cout<<setw(2)<<i<<" . "; cout<<" Gas purchased = "<<gas[i]<<" gallons "; cout<<((miles[i]-miles[i-1])/gas[i])<<" miles per gallon."; cout<<endl; } system("pause"); return 0; }*/ //4.3.1 初始化数组 /*#include "stdafx.h" #include <iostream> #include <iomanip> using namespace std; int main() { int value1[5]={1,2,3}; int value2[5]; for(int i=0; i<5; i++){ cout<<setw(2)<<value1[i]<<" "; } cout<<endl; for(int i=0; i<5; i++){ cout<<setw(2)<<value2[i]<<" "; } cout<<endl; system("pause"); return 0; } //4.1.4 字符数组和字符串处理 //注意:字符串中每个字符占用一个字节,因此算上最后的空字符,字符串需要的字节数要比包含的字符数多一个 //wchar_t president[] = L"Ulysses Grant"; 前缀L表示字符串字面值是一个宽字符串,因此字符串的每个字符(包括终止空字符)都会占两个字节,当然,对字符串的索引会引用字符,而不是字节 1 字符串输入 const int MAX = 80; char name[MAX]; cin.getline(name,MAX,'\n'); name: char[]类型数组的名称,从cin读取的字符存储在该数组中 MAX: 要读取的字符的最大数量,当读取了最大数量的字符数后,停止输入 '\0': 结束输入过程的字符,在此可以指定任一字符,且该字符在首次出现时就将结束输入过程 */ /*#include "stdafx.h" #include <iostream> using namespace std; int main() { const int AMX = 80; char buffer[AMX]; int count=0; cout<<"请输入一串字符:"<<endl; cin.getline(buffer,AMX,'\n'); while(buffer[count] !='\0'){ count ++; } cout<<"输入的字符串为:"<<buffer<<" 长度为:"<<count<<endl; system("pause"); return 0; }*/ //4.1.5 多维数组 //1初始化多维数组 /*#include "stdafx.h" #include <iostream> #include <iomanip> using namespace std; int main() { //int ch[3][4]={0}; //char ch[3][4] = {s}; //这里默认声明字符串数组好像不行 char ch[3][4] = {'s','v','v','t', 's','v','t','e', 's','v','t','e'}; //注意:数组中有多少维,初值组就需要多少层嵌套的大括号,这句话么意思哦,不懂啊 是等号右边吗?不行哦 for(int i=0; i<3; i++){ cout<<setw(2)<<i; for(int j=0; j<4; j++){ cout<<setw(2)<<ch[i][j]<<" "; } cout<<endl; } system("pause"); return 0; } #include "stdafx.h" #include <iostream> #include <iomanip> using namespace std; int main() { char ch[][80]= {"Robert RedFord", "Gioakibg Cassudt", "Lassie", "Slim Pickens", "Boris Karloff", "Oliver Hardy"}; //注意:数组中有多少维,初值组就需要多少层嵌套的大括号,这句话么意思哦,不懂啊 是等号右边吗?不行哦 int i; cout<<"请输入1 to 6 数字:"; cin>>i; if(i>=1 && i<=6){ cout<<"ch["<<i<<"]:"<<ch[i-1]<<endl; }else{ cout<<"输入的值不合法"<<endl; } system("pause"); return 0; }*/ //4.2 间接数据存取 //4.2.1 指针的概念 //4.2.2 声明指针 //long * pnumber; //long *pnumber; //long* pnumber, number = 99; //操这里蛋疼了,pnumber是long型的指针,而number只是一个long型的变量, //在C++中,使用以字母p开始的变量名表示指针的通用惯例,这使我们能更容易看清程序中哪里些变量是指针,从而使程序更易于理解 //取地址运算符 //我们需要的是取地运算符符&,该运算符是一元运虎符,用于获得变量的地址,又名引用运算符。 //pnumber = &number; //4.2.3 使用指针 //1 间接运算符 //2 需要使用指针的原因 /*#include "stdafx.h" #include <iostream> using namespace std; int main() { long* pnumber = NULL; long number1 = 55, number2=99; pnumber = &number1; *pnumber +=11; //66 cout<<"number1:"<<number1<<endl; cout<<"&number1="<<hex<<pnumber<<endl; pnumber = &number2; //99 number1 = *pnumber * 10; //990 //number1=990 cout<<"number1="<<dec<<number1<<endl; //990 cout<<"pnumber="<<hex<<pnumber<<endl;; //99 cout<<"*pnumber="<<dec<<*pnumber<<endl;; //99 system("pause"); return 0; }*/ //*pnumber +=11; //间接运算符使我们将pnumber所指变量--number1的内容加上班11,如果忘记在这条语句中写上*号,则计算机将使该指针存储的地址加上11 //那不就是内存地址的加11 //4.2.4 初始化指针 //int* pnumber = NULL; //if(pnumber == NULL) cout<<"pnumber is null."; //int* pnumber = 0; //if(pnumber == NULL) cout<<"pnumber is 0."; //if(!pnumber){ cout<<"pnumber is null."} //NULL指针指向的地址包含无用数据,我们永远不应该解除对某个空指针的引用,因为那样将使程序立即终止 //1 指向char类型的指针 //char * proverb = "A miss is as good as a mile"; //看起来类似初始化char数组,但二者细微的区别,这条语句用引号之间的字符串创建某个以/0终止的字符串字面值(实际上是const char类型的数组),并将该字面值的地址存储在指针proverb中,该字面值的地址将是其第一个字符的地址 /*#include "stdafx.h" #include <iostream> using namespace std; int main() { char* pstr1 = "Robert Redford"; char* pstr2 = "Hopalong Cassidy"; char* pstr3 = "Lassie"; char* pstr4 = "Slim Pickens"; char* pstr5 = "Boris Karloff"; char* pstr6 = "Oliver Hardy"; char* pstr = "Your lucky star is "; int dice = 0; cout<<"Pick a lucky star 1 and 6:"; cin>>dice; switch(dice) { case 1: cout<<pstr<<pstr1<<endl; break; case 2: cout<<pstr<<pstr2<<endl; break; case 3: cout<<pstr<<pstr3<<endl; break; case 4: cout<<pstr<<pstr4<<endl; break; case 5: cout<<pstr<<pstr5<<endl; break; case 6: cout<<pstr<<pstr6<<endl; break; default: cout<<"sorry 输入的值不正确"<<endl; break; } //输出指针指向的字符串简单得不能再简单了, //显示出来的是该指针包含的地址,为里却不同呢, //在于输出操作看待char*类型指针的方式,输出操作以一种特殊的方式来看待这种类型的指针,即将其视为字符串(即char数组) //因些输出字符串本身,而不是字符串的地址 system("pause"); return 0; }*/ /* #include "stdafx.h" #include <iostream> using namespace std; int main() { char* pstr[] ={ "Robert Redford", "Hopalong Cassidy", "Lassie", "Slim Pickens", "Boris Karloff", "Oliver Hardy", }; char* pFirstStr = "Your lucky star is "; int dice = 0; cout<<"Pick a lucky star 1 and 6:"; cin>>dice; if(dice >= 1 && dice <= 6) { cout<<pFirstStr<<pstr[dice-1]<<endl; }else{ cout<<"sorry 输入的值不正确"<<endl; } //输出指针指向的字符串简单得不能再简单了, //显示出来的是该指针包含的地址,为里却不同呢, //在于输出操作看待char*类型指针的方式,输出操作以一种特殊的方式来看待这种类型的指针,即将其视为字符串(即char数组) //因些输出字符串本身,而不是字符串的地址 system("pause"); return 0; }*/ //4.2.5 sizeof运算符 //cout<<(sizeof pstr)/(sizeof pstr[0]) //记住,pstr是一个指针数组,对该数组或元素使用sizeof运算会不能获得任何文本字符中内存占用情况 /*#include "stdafx.h" #include <iostream> using namespace std; int main() { char* pstr[] ={ "Robert Redford", "Hopalong Cassidy", "Lassie", "Slim Pickens", "Boris Karloff", "Oliver Hardy", }; char* pFirstStr = "Your lucky star is "; int count = (sizeof pstr) / (sizeof pstr[0]); cout<<"count:"<<count<<endl; //这里计算一共有多省个字符串 int dice = 0; cout<<"Pick a lucky star 1 and "<<count<<":"; cin>>dice; if(dice >= 1 && dice <= 6) { cout<<pFirstStr<<pstr[dice-1]<<endl; }else{ cout<<"sorry 输入的值不正确"<<endl; } //输出指针指向的字符串简单得不能再简单了, //显示出来的是该指针包含的地址,为里却不同呢, //在于输出操作看待char*类型指针的方式,输出操作以一种特殊的方式来看待这种类型的指针,即将其视为字符串(即char数组) //因些输出字符串本身,而不是字符串的地址 system("pause"); return 0; }*/ //4.2.6 常量指针和指向常量的指针 //const int count = (sizeof / pstr) / (sizeof pstr[0]) //#include "stdafx.h" //#include <iostream> //#include <iomanip> //#include <string> //#include <cstring> //using namespace std; //int main() //{ /*char* pstr[] ={ "Robert Redford", "Hopalong Cassidy", "Lassie", "Slim Pickens", "Boris Karloff", "Oliver Hardy", };*/ //char* pFirstStr = "Your lucky star is "; //修改pstr[0]所指向的字符内容 //pstr[0]="Xlc"; //cout<<pstr[0]<<endl; //cout<<*pstr[0]<<endl; //*pstr[0] = "Stan Laurel"; //书上说这样不可以编译,但我这里却可以 //*pstr[0] = 'x'; // /*const char* pstr[] ={ "Robert Redford", "Hopalong Cassidy", "Lassie", "Slim Pickens", "Boris Karloff", "Oliver Hardy", };*/ //指针数组的元素指向const字符串,如果我们现在试图修必这些字符串,则次被编译器在编译时标记为错误 //*pstr[0] = 'x'; 不可以修改 //当然,我们仍然可以合法地编写下面的语句 //pstr[0] = pstr[1]; //注意,这条语句并没有改变指针数组元素指向的对像的值,改变的只是pstr[0]中存储的指针的值,我们应该禁止这种修改, /*const char* const pstr[] ={ "Robert Redford", "Hopalong Cassidy", "Lassie", "Slim Pickens", "Boris Karloff", "Oliver Hardy", };*/ //总之,我们应该区分下面这三种const,指针及指针指向的对像有关的情形 //指向常量对像的指针 //const char* pstr[]={}; //指向某个对像的常量指针 //char* const pstr[]={} //指向常量对像的常量指针 //const char* const pstr[]={} //第一种情况,我们不能修改被指向的对像,但可以使指针指向其它对像 //const char* pstring = "Some text"; //const char* pstring = "Some text"; //如果修改指针指向其它对像, 这里需要知道这么回事,但还是不太理解,其它的对像,是指一个"I some text"吗? //但我用 const char _str[] = "I some text"; 然后赋值给pstring还是不成功 //pstring="I some text"; //cout<<"pstring:"<<pstring<<endl; // *pstring = "xxdxxd"; 不能修改被指向的对像 //第二种情况,我们不能修改指针中存储的地址,但可以修改指针指向的对像 //char* const pstring = "Some text"; //常指针就是创建不能移动的固定指针,但是它所指的数据是可以改变的 //pstring[0] = 's'; // char * const pc ="abcd"; // cout <<pc<<endl; // pc[3]='x'; // cout<<pc<<endl; //网上指的,也不起做用 //pstring = "I Some text"; //不能修改指针中存储的地址 //*pstring = "I some text"; //不知道怎么来描述或者说怎么来写,“可以修改指针指向的对像” //在最后一种情况下,指针和被指向的对像都被定义成常量,因此都不能被修改 //const char* const pstring="Some Texst"; //4.2.7 指针和数组 //double* pdata; //double date[5]; //则可以进行下面的赋值操作 //pdata = data; //该语句将数组data中第一个元素的地址赋给指针pdata,单独使用数组名称将引用该数组的地址,如果我们使用带索引仁政的数组名data, //则引用的是对应于索引值的那个元素的内容,因此,如果我们希望将该元素的地址存入指指中,则必须使用取址运算符 //pdata = &data[1]; //1 指针算术运算 //我们可以用指针执行算术操作,在算术方面,我们仅限于加减操作 //pdata = &data[2]; //这种情况下,表达式pdata+1将是数组data中的第四个元素data[3].因此我们通过下面的这条语句,就可以使指针pdata指向元素data[3] //pdata++; //注意,指针算术运算产生的地址范围可以从数组第一个元素的地址到最后一个元素之后的第一个地址,如果超出该范围,则指针的行为是不确定的 //对于作为指针算术运算结果的指针来说,我们当然可以解除对它的引用(否则该指针就没什么用途) //假设pdata仍然指向data[2] //* (pdata+1) = *(pdata+2); //data[3] == data[4] //当我们使某个指针包含的地址递增之后,又希望解除对该指针的引用时,圆括号是必不可少的,因为间接运算符的优先级高于算述运算符+或-的优先级,如果写成*pdata + 1 而非*(pdata+1), //则使pdata指向的地址中存储的数值加1,即等价于执行data[2]+1; 因为该表达式不是一个左值,所以用于前面的赋值语句时将使编译器生成一条出错消息 /**const int MAX = 100; long primes[MAX] = {2,3,5}; long trial = 5; int count = 3; int found = 0; do { trial += 2; //7 found =0; //0 for(int i=0; i<count; i++) { found = (trial % *(primes+i)) == 0; if(found){ cout<<"trial:"<<trial<<endl; cout<<"*(primes+i):"<<*(primes+i)<<endl; break; } } if(found == 0) *(primes + count++) = trial; }while(count < MAX); for(int i=0; i<MAX; i++){ if(i % 5 == 0) cout<<endl; cout<<setw(10)<<*(primes + i); }*/ //计算字符的个数 /*const int MAX = 80; char buffer[MAX]; char* pbuffer = buffer; cout<<"请输入你要的字符串,最大值为"<<MAX<<endl; cin.getline(buffer,MAX,'\n'); //因为指针pbuffer是在while循环中递增的,发现'\0'字符之后递增将停止,当发现'\0'字符时,pbuffer将包含字符串中该字符的地址, //因此,输入的字符串中字符的个数就是指针pubffer中存储的地址与buffer指示的数组开始地址的差值 //这句话很重要,pbuffer将包含字符串中该字符地址,因此,输入的字符串中字符的个数就是指针pbuffer中存储的地址与buffer指示的数组开始的地址的差值 while(*pbuffer) { pbuffer++; } cout<<"the stirng :"<<buffer<<endl; cout<<" has "<<(pbuffer - buffer -1)<<" characters. "; cout<<endl; //注意,我们不能用与使用指针相同的方式使用数组名,表达式buffer++绝对是非法的,因为我们不能修改数组名表示的地址,尽管我们可以在表达式中像使用指针一样使用数组名,但是数组名不是指针,因为它表示的地址是固定的*/ // 2 使用指针处理多维数组 //但相对处理多信数组时,事情就变得稍微复杂一些了 //double beans[3][4]; //我们可以声明一个指针pbeans //double* pbeans; //pbeans = &beans[0][0]; //我们也可以用下面的语句,将指针设置为数组中第一行的地址 //pbeans = beans[0]; //但由于beans是二维数组,因此不能用下面的语句设置指针中的地址 //pbeans = beans; //问题在于类型不同,前面定义的指针类型是double*, 但是数组beans的类型是double[3][4], //存储该数组地址的指针必须是double*[4]类型,C++将数组的大小与类型联系在一起, //上面的语句仅当将指针声明为要求的大小时才合法,这种声明形式比前面所介绍的都复杂一些 //double (*pbeans)[4]; //此处的圆括弧是必需的,否则声明的将是一个指针数组,前面那条语句现在是合法的,但是该指针只能用来存储规定大小的数组的地址 /*double beans[3][4]={10}; double* pbeans = &beans[0][0]; cout<<"pbeans:"<<*pbeans<<endl; //将第一行做为存储地址 double* ppbeans = beans[0]; *(ppbeans+1)=22; //这样是个改第一行的第二个值 //ppbeans++; 这样加不行,这样加是指元素向前提一个 //*(ppbeans+1)=55; //*(*ppbeans+1)=66; //这里还是不知道如何移动到下一次,或者指定下一列的信息 cout<<"beans[0][1]:"<<beans[0][1]<<endl; cout<<"beans[0][2]:"<<beans[0][2]<<endl; cout<<"beans[1][1]:"<<beans[1][1]<<endl; */ //3: 多维数组的指针形式 //使用带两个索引值的数组名 //使用指针形式的数组名 //beans[i][j]; //*(*(beans+i)+j); //beans是数组变量值,而不是指针值 //我们来看一下工作过程,第一行使用正常的数组索引值,引用数组中第i行偏移量为j的元素 //我们可以从内向外确定每样二行的意义,beans引用数组第一行的地址,因此beans+i引用提数的第i行, //表达式*(beans+i)是第i行的第一个元素的地址,因此*(beans+i)+j是i行中偏大移量为j的那个元素的地址,因此,整个表达式引用的该元素的值 /*double beans[3][4]={10}; double* pbeans = &beans[0][0]; cout<<"pbeans:"<<*pbeans<<endl; //我这里个beans[1][1]的元素值 *(*(beans+1)+1) = 55; //大爷的,这里注意是用beans而不是指针变量pbeans去找的 cout<<"beans[1][1]:"<<beans[1][1]<<endl; //*(*(beans+i)+j); //我们不建议这样做,那么可以混合使用数组和指针两种形式, *(beans[2]+1) = 66; cout<<"beasn[2][1]:"<<beans[2][1]<<endl; (*(beans+1))[2] = 77; cout<<"beans[1][2]:"<<beans[1][2]<<endl;*/ //4.3动态内存分配 //4.3.1 堆的别名--*存储器 //4.3.2 new和delete运算符 //double* pvalue = NULL; //pvalue = new double; //*pvalue = 9999.0; //pvalue = new double(999.0); //delete pvalue; //这样将确保这块内存以后可被另一个变量使用,如果我们不使用delete,随后又在pvalue指针中存入一个不同的地址值,那么将无法再释放这块内存,或使用其包含的变量,因为我们失去了访问该地址的途径,这种情况被称为内存泄漏,特别是出现在用户程序中的时候 //4.3.3 为数组动态分配内存 //pstr = new char[20]; 声明动态数组 //delete []pstr; 删除数组 使用方括号是为了指出要删除的是一个数组, //从*存储器中删除数组时,我们应该总是包括方括号,否则结果将不可预料,别请注意,我们不需要指定任何大小,只需要写出[]即可 //当我们使用delete运算符弃某些内存之后,还应该总暗针访俯拾皆是指针重新设置为0 //pstr = 0; /*long* pprime =0; long trial = 5; int count = 3; int found = 0; int max = 0; cout<<"请输入一个最大值:"; cin>>max; if(max < 4){ max = 4; } pprime = new long[max]; //声明一个long的数组 *pprime = 2; //第一个值为2 *(pprime+1)=3; //第二个值为3 *(pprime+2)=5; //第三个值为5 do { trial +=2; found = 0; for(int i=0; i<count; i++) { found = (trial % *(pprime+i)) == 0; if(found) break; } if(found==0){ *(pprime+ count++) = trial; } }while(count < max); for(int i=0; i<max; i++) { if(i % 5 ==0){ cout<<endl; } cout<<setw(10)<<*(pprime+i); } delete []pprime; pprime = 0; //注意:我们不能给动态分配的数组元素指定初值,如果需要设置数组元素的初值,则必须显示使用赋值语句 */ //4.3.4 多维数组的动态分配件 //与一维数组相比,在*存储器中为多维数组分配内存需要以略微复杂的形式使用new运算符 //pbeans = new double[3][4]; //pBigArray = new double[5][10][10]; //前面曾经看到,可以使用变量来指定new分配的一维数组的大小,对二维或多维数组来说同样如此, //但仅限于用变量指定最左边那一维,所有其他维都必须是常量或常量表达式, //pBigArray = new double[max][10][10]; //????????????????????????? //不会吧我得试试才知道了 /*const int max = 20; const int line = 30; const int line2 = 10; double(*pBigArray)[10][10] = new double[max][10][10]; //看看成不成功 delete [] pBigArray;*/ //上面说的是正确的,书上说的是正确的