Visual C ++ 2010 Chapter 4 arrays, strings and pointers

4.1 a plurality of data values of the same type of processing
4.1.1 array

  • Array: an array of memory locations of a set of elements or simply named elements, each memory location can store the same data type item, all memory locations can be referenced by the same variable name.
  • Array of individual data items specified by the index value, the index value of an integer number of order of the array elements.
    4.1.2 declare arrays
    long height [6];
    each long value in the memory to 4 bytes, the entire array total of 24 bytes.

4.1.4 character array and string manipulation

  • It is referred to an array of char character array, commonly used to store the string. Strings are attached with special characters (end of the string sign) sequence of characters.

  • String termination character indicates the string has ended, the character "\ 0" is defined by the escape sequence, also known as a null character.
    Here Insert Picture Description

  • Note that the string in each character occupies one byte, so the final null character count, the number of characters in a string require the number of bytes that contains more than one.

char movie_star [15] = "Marilyn
Monroe"; Note, the termination character "\ 0" is automatically added to the compiler.

  • Create a string of Unicode characters, the characters in the string of type wchar_t.

    President wchar_t [] = L "Ulysses Grant";
    L: represents a string literal is a string of wide, i.e., each character string (including the terminating null character) will occupy 2 bytes.

String input

  • getline () function , the function reads characters from the keyboard in sequence, and a string (with "\ 0" termination character) which is stored character array.
    Here Insert Picture Description
    Here Insert Picture Description

4.1.5 Multidimensional Arrays

double beans [12] [10] ;
array are stored in the memory is to rightmost index changing fast.
data [3] [4];
Here Insert Picture Description

4.2 indirect data access
4.2.1 statement pointer
long * pnumber; pointer pointing long type
in C ++, used in variable names letter p is a pointer indicating the start of common conventions.

Address-operator &
&: address-operator, obtaining the address of the variable.

pnumber = & number;
use & operator to obtain the address of any variable, but require a suitable type to store an address pointer.
Here Insert Picture Description

4.2.3 pointer
indirection operator *
a * indirection operator for use with a pointer, the pointer to access the contents of the variable.

4.2.4 initialize the pointer
int Number (0);
int * pNumber (& Number);

pNumber * int (nullptr a);
nullptr a: the Visual C ++ pointer literal not any object.
nullptr implicitly converted to type bool; nullptr bool value is converted to false, any other pointer values are converted to true.
Here Insert Picture Description

Point pointers to char

  • char type pointer can be initialized with a string literal.
    char * proverb ( "A miss is as good as a mile.");
    Here Insert Picture Description

Here Insert Picture Description
An array of pointers memory usage
Here Insert Picture Description

4.2.5 sizeof operator

  • sizeof 操作符,产生size_t (通常等价于unsinged
    int)类型的整数值,给出其操作数占用的字节数量,其中size_t是由标准库定义的类型。

    • cout << sizeof dice;
      表达式sizeof dice 值是4,因为dice被声明为int类型,要占用4个字节。

    • sizeof 操作符可以应用于数组元素或整个数组。
      ①当该操作符应用于数组名称本身时,将产生整个数组占用的字节数量;
      ②当应用于一个带有适当索引值的元素时,则得到该元素占用的字节数量。
      cout << (sizeof pstr)/(sizeof pstr[0]);
      将整个指针数组占用的字节数量除以数组中第一个元素占用的字节数量。该数组中各个元素占用相同数量的内存,故结果是数组元素的数量。
      注意,pstr是一个指针数组。对该数组或当元素使用sizeof操作符不能获得任何文本字符串的内存占用情况。pstr[0] 是1个指向字符数组的指针,只占用4个字节。

    • sizeof 操作符应用于类型名称,其结果是该类型的变量占用的字节数量。类型名称应该用()括起来。
      size_t long_size = sizeof (long);

4.2.6 常量指针和指向常量的指针
字符串字面值的类型是const char 数组。

情形 表达式 说明
指向常量对象的指针 const char* pstring(“some text”); 不能修改被指向的对象,但可以使指针指向其他对象
指向某个对象的常量指针 char* const pstring(“some text”); 不能修改指针中存储的地址,但可以修改指针指向的对象
指向常量对象的常量指针 const char* const pstring(“some text”); 指针和被指向的对象都被定义成常量,因此都不能被修改

4.2.7 指针和数组

  • 单独使用一维数组的名称,则该名称将自动转换为指向数组第一个元素的指针。
    注意,当数组名称用作sizeof操作符的操作数时,情况就不是这样。
    double* pdata(nullptr);
    double data[5];
    pdata = data; //将数组第一个元素的地址赋给指针pdata
    如果使用带索引值的数组名data,则引用的是该索引值的元素的内容。若希望在指针中存储该元素的地址,则必须使用取址运算符:
    pdata = &data[1];

1.指针算术运算

  • 指针算术运算,仅限于+、-,还可以比较指针值,从而产生逻辑效果。

  • 指针算术运算是在指针包含的地址上进行的。
    pdata = &data[2];
    pdata += 1; //pdata 中包含的地址增加了数据data中1个元素占用的字节数。
    Here Insert Picture Description

  • 注意,指针运算符产生的地址范围可以从数组第一个元素的地址到最后一个元素之后的第一个地址。如果超出该范围,则指针的行为是不确定的。

//计算质数
#include <iostream> //包含 cout、endl
#include <iomanip> //包含 setw()

using std::cout;
using std::endl;
using std::setw;

int main()
{
	const int MAX(100); //定义希望该程序产生的质数数量
	long primes[MAX] = { 2,3,5 }; //定义数组,包含前3个已经确定的质数
	long trial(5);
	int count(3);
	bool found(false);

	do //do-while循环挑选出下一个要检查的数值,当该数值是质数时将其添加到primes数组中
	{
		trial += 2;
		found = false;

		for(int i = 0;i<count;i++) // for循环检查某个数值是否是质数
		{
			found = (trial % *(primes + i))==0; //trial除以当前质数*(primes + i)时没有余数,found设置为true
				if(found) 
					break; //found值为true,if语句使for循环终止
		}
		if (!found) //判断trial是不是质数
			*(primes + count++) = trial; //trial确实是质数,在primes[count]中存储该质数
	}while (count<MAX);
	
	for(int i=0;i<MAX;i++) 
	{
		if(i % 5 == 0)
			cout<< endl;
		cout<< setw(10)<<*(primes + i); //setw(int n) 控制输出间隔
	}
	cout<<endl;

	return 0;
}
//计算输入字符个数
#include <iostream> 
using std::cout;
using std::endl;
using std::cin;

int main()
{
	const int MAX(80); //定义希望该程序产生的质数数量
	char buffer[MAX];
	char* pbuffer(buffer);

	cout << endl
		 << "Enter a string of less than "
		 << MAX <<" characters:"
		 << endl;

	cin.getline(buffer,MAX,'\n');

	while(*pbuffer) 
		pbuffer++;	//发现\0 字符后递增停止
					//输入字符串中字符的个数就是指针pbuffer中存储的地址
					//与buffer指示的始祖开始地址的差值
	cout <<endl
		 << "The string \""<< buffer
		 << "\"has" << pbuffer - buffer << "characters.";
	cout << endl;
	return 0;
}

2. 使用指针处理多维数组
double beans[3][4];
double* pbeans;
pbeans = &beans[0][0]; //将指针设置为数组中第1个元素的地址
pbeans = beans[0]; //将指针设置为数组中第一行的地址

注意:不能用下面的语句设置指针中的地址
pbeans = beans; //问题在于类型不同,前面指针类型为 double*,数据beans 类型double[3][4]

3.多维数组的指针形式
使用数组名的指针形式来引用数组元素:
2种方法:

beans[i][j]  //使用带2个索引值的数组名;
*(*(beans + i) + j ) //使用指针形式的数组名
// beans 引用数组的第一行的地址,
// beans + i 引用数组的第i行,*(beans + i) 第i行第1个元素的地址,
// *(beans + i) + j  是i行中偏移量为j的那个元素的地址

4.3 动态内存分配
4.3.1 堆的别名——空闲存储器

  • 当执行程序时,计算机中有部分未使用的内存。这些内存在C++中被称为堆,也称 空闲存储器。
  • new操作符:在空闲存储器中为特定类型的新变量分配空间。该操作符返回分配给变量的内存地址。
  • delete操作符:释放先前new分配的内存。

4.3.2 new和delete操作符

  • 为某个double变量分配空间:
	double* pvalue(nullptr);
	pvalue = new double;
// new操作符应该返回空闲存储器中分配给double 变量的内存地址,并在指针pvalue中存储改地址。

空闲存储器的破碎:即没有足够的连续字节提供给需要获得空间的变量。

  • new创建的变量初始化:
	pvalue = new double(999.0);
	double* pvalue (new double(999.0));
	//delete 操作符释放内存到空闲存储器中
	delete pvalue;
  • 内存泄漏:不使用delete,随后又在指针中存入一个不同的地址值,那么将无法释放这块内存,或使用其包含的变量。因为我们失去了访问该地址的途径。

4.3.3 为数组动态分配内存

pstr = new char[20];// 为20个字符的char数组分配空间,并将其地址存入pstr中

delete [ ] pstr; //[ ] 指出要删除的是一个数组,不用指定任何维数,只需写出[] 即可
//当使用delete操作符抛弃之前分配的某些内存之后,还应该将该指针重新设置成nullptr
pstr = nullptr;
//计算任意多个质数,使用空闲存储器的内存来存储质数
#include <iostream>
#include <iomanip>

using std::cin;
using std::cout;
using std::endl;
using std::setw;

int main()
{
	long* pprime(nullptr);
	long trial(5);
	int count(3);
	int found(0);
	int max(0);

	cout << endl
		 << "Enter the number of primes you would like (at least 4):";
	cin >> max;

	if(max < 4)
		max = 4;
	pprime = new long[max]; // 分配内存
	*pprime = 2;
	*(pprime + 1) = 3;
	*(pprime + 2) = 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 = nullptr;
	cout<< endl;
	return 0;
}

4.3.4 多维数组的动态分配

pbeans = new double[3][4]; // 二维数组分配空间
pBigArray = new double [5] [10] [10];
//无论创建的数组是多少维,都可以用下面的语句将数组销毁
delete [ ] pBigArray;
pBigArray = nullptr;

// 使用变量来指定new分配的多维数组:仅限于用变量指定最左边的那一维。
pBigArray = new double [max] [10] [10];

4.4 使用引用

4.4.1 引用的概念
引用 是可用作其他对象的别名的一个名称。

  • 引用分为2种类型
    • lvalue 引用:另一个变量的别名,它引用的是一个可出现在赋值操作左边的持久存储位置。
    • rvalue 引用:也可用作变量别名,它与lvalue引用的区别在于它也能引用rvalue,这实质上是一个暂存的临时值。

4.4.2 声明并初始化lvalue引用
使用引用:

long number(0L);
long & rnumber(number); //为number变量声明一个lvalue引用
// & 表明声明的是一个lvalue引用,该引用表示的变量名number作为初始值被写在圆括号内。
// 变量rnumber 属于 “指向long的引用” 类型
// 可以使用引用代替原来的变量名:
	rnumber += 10L; 
// 为保持常量值的完整性,必须使用一个const引用:
const int & refData = 5; // 这样就可以使用refData 引用访问字面量5

使用指针:

long* pnumber(&number);
*pnumber += 10L;

使用指针与使用引用的区别:

  • 指针需要被解除引用才能参与到表达式中,用来访问变量的是指针包含的地址。
  • 使用引用的情况下,不必解除引用。在某些方面,引用就像是已经被解除引用的指针,但不能改为引用别的对象。lvalue 引用完全等价于被引用的变量。

4.4.3 声明并初始化rvalue引用

  • 在类型名后面使用2个& 来指定一个rvalue 引用类型。
int x(5);
int&& rx = x; //定义了一个rvalue引用rx,它引用x。
// 这就表明可以用一个lvalue来初始化一个rvalue引用,从而使rvalue引用能够像lvalue引用一样工作。

int&& rExpr = 2*x + 3;
//rvalue 引用初始化为引用表达式2*x + 3 的求值结果,是一个临时值。

4.5 字符串的本地C++库函数

  • 标准库提供的cstring 头文件中包含操作以空字符结尾的字符串的函数。
  • 本地C++的string 标准头定义了代表字符串string和wstring类。
    • string类:char 类型的字符串
    • wstring类:wchar_t 类型的字符串

4.5.1 查找以空字符结尾的字符串的长度

  • strlen()函数:将char*类型的参数字符串的长度作为1个size_t类型的值返回。类型size_t是一个对应于无符号整型的实现定义类型,无符号整型一般用来表示各种序号的长度。
  • wcslen()函数与wchar*t类型字符串的功能相同。
char* str( "A miss is as good as a mile." );
cout << "The string contains "<< strlen(str) << "characters." << endl;
//输出
The string contains 28 characters.
//注意:返回的长度值不包括结尾的空字符

4.5.2 连接以空字符结尾的字符串

  • strcat()函数:用来拼接2个以空字符结尾的字符串。在第一个参数指定的字符串后面添加第二个参数指定的字符串。
char str1[30] = "Many hands"; //可容纳30个字符的数组str1
// 第一个参数指定的字符串必须有足够的空间来容纳连接后的2个字符串,如果空间不够,会出现错误,因为那样会重写第一个字符串之外的区域。
char* str2("make light work.");
stract(str1,str2);
cout << str1 << endl;
// 第二个参数指定的字符串的第一个字符重写第一个参数结尾的空字符,第二个字符串余下的所有字符都被复制过去,包括结尾的空字符。
//输出:
Many hands make light work.

//stract()函数返回第一个参数的指针,因此可将上述程序的后2句写成:
cout << strncat(str1,str2) << endl;

Here Insert Picture Description

  • wcscat()函数拼接宽字符串,其他方面与strcat()函数的运行方式完全相同。
  • strncat()函数:将一个以空字符结尾的字符串的一部分附加到另一个字符串后面。前2个参数分别是目标字符串和源字符串,第三个参数是要从源字符串中附加的字符个数。
cout << stract(str1,str2) << endl;
// 执行后,str1 就包含字符串“Many hands make light”,
// 这个运算符str2 中的11个字符附加到str1上,重写了str1中结尾的‘\0’,然后在末尾加上1个‘\0’字符。
  • wcsncat()函数提供的功能与strncat函数相同,只是它用于宽字符
  • 拼接字符串的函数都依靠查找字符串中结尾的空字符才能正常运行,当处理不受信任的数据时他们是不安全的。 < cstring >中的strcat_s()、wcscat_s()、strncat_s()、wcsncat_s() 函数提供了安全的替换方法。
const size_t count = 30 ;
char str1[count] = "Many hands";
char* str2(" make light work.");

errno_t error = strcat_s(str1,count,str2); 
// stract_s()函数的第1个参数是目标字符串;将源字符串向这个目标字符串附加
// 第3个参数所指定个数的字符;第2个参数是目标位置上可用的字节总数
// 这个函数返回一个errno_t 类型的整数值来指示拼接情况:
// ①运算成功,错误返回值为0;
// ②源字符串或目标字符串是NULLPTR,则错误返回值为EINVAL;
// ③目标长度太小,则返回ERANGE;
if(error == 0)
	cout << "String joined successfully." << endl;
else if (error == EINVAL)
	cout << "Error! Source or destination string is NULL." << endl;
else if (error == ERANGE)
	cout << "Error! Destination string too small." << endl;

错误代码值EINVAL 和 ERANGE 都在cerrno头文件中定义的。

4.5.3 复制以空字符结尾的字符串

  • 标准库函数strcpy():
    • 将字符串从源位置复制到目标位置。
    • 第1个参数:指向目标位置的指针;第2个参数:指向源字符串的指针;2个参数类型均为 char*
    • 该函数返回一个指向目标字符串的指针。
const size_t LENGTH = 22;
const char source[LENGTH] = "The more the merrier!";
char destination[LENGTH] ;
cout << "The destination string is : " << strcpy(destination,source) << endl;
// 必须确保目标字符串有足够的空间容纳源字符串
  • strcpy()函数的安全版本 strcpy_s()函数:
    • 在目标参数与源参数之间用以个额外的参数来指定目标字符串缓冲区的大小。
    • 该函数返回一个errno_t 类型的整数值来指示有没有发生错误。
const size_t LENGTH = 22;
const char source[LENGTH] = "The more the merrier!";
char destination[LENGTH] ;

errno_t error = strcpy_s(destination,LENGTH,source);
if(error == EINVAL)
	cout << "Error. The source or the destination is NULLPTR."<<endl;
else if (error == ERANGE)
	cout << "Error. The destination is too small."<<endl;
else
	cout << "The destination string is : "<< destination << endl;

// 此例需要包含cstring 和cerno 头文件
// strcpy_s()函数确认源字符串和目标字符串都不是NULLPTR,且目标缓冲区有足够的空间容纳源字符串。
//①源字符串和目标字符串两者之一为NULLPTR时或两者都为NULLPTR时,函数返回值 EINVAL;
//②目标缓冲区太小,函数返回值 ERANGE;
//③复制成功,函数返回值 0 ;
  • 复制函数宽字符版本:wcscpy() 和 wcscpy_s().

4.5.4 比较以空字符结尾的字符串

  • strcmp()函数:比较通过参数(char* 类型的指针)指定的2个以空字符结尾的字符串。该函数返回一个int类型的值,根据第1个参数指向的字符串小于、等于还是大于第2个参数指向的字符串,返回值将会<0、=0、>0.
char* str1("Jill");
char* str2("Jacko");
int result = strcmp(str1,str2);
if(result < 0)
	cout<< str1 << "is less than "<< str2 << '.'<< endl;
else if (0==result)
	cout<< str1 << "is equal to "<< str2 << '.'<< endl;
else
	cout<< str1 << "is greater than "<< str2 << '.'<< endl;
  • wcscmp()函数时对应于strcmp()函数的宽字符版本。

4.5.5 搜索以空字符结尾的字符串

  • strspn()函数:搜索字符串中不包含在给定集合中的第一个字符,并返回该字符的索引。第1个参数指向要搜索的字符串的指针,第2个参数指向包含该字符集合的字符串的指针。
  • 该函数的返回值表示子串的长度,该子串从第1个参数字符串的第1个字符开始,且完全由第2个参数字符中的字符组成。
//搜索不是元音的第1个字符
char* str = "I agree with everything.";
char *vowels = "aeiouAEIOU ";
size_t index = strspn(str,vowels);
cout << "The first character that is not a vowel is  '" << str[index]
	<< " ' at position " << index << endl;
// 输出
The first character that is not a vowel is 'g' at position 3
  • wcsspn()函数是strspn()的宽字符串版本。
  • strstr()函数:返回一个指针,指向第1个参数中第2个参数指定的子字符串的位置。
char* str = "I agree with everything.";
char* substring = "ever";
char* psubstr = strstr(str,substring); // 在str中搜索子串第一次出现的位置。
if(!psubstr) //如果没有找到子字符串,则返回NULL
	cout << "\"" << substring << "\" not found in \" " << str << "\" " << endl;
else //如果找到子字符串,则返回一个指向字符串位置的指针
	cout << "The first ocurrence of \" " << substring
		<< "\" in \" "<< str << "\"is at position "
		<< psubstr-str << endl; // psubstr-str 给出子字符串中第一个字符的索引位置。

//输出
The first ocurrence of "ever" in "I agree with everything." is at position 13
//搜索一个给定的字符串来确定给定子字符串出现的次数
#include <iostream>
#include <cstring>

using std::cout;
using std::endl;
using std::strlen;
using std::strstr;

int main()
{
	char* str("Smith,where Jones had had \"had had\" had had \"had\".""\n\"Had had \" had had the examiners' approval.");
	char* word("had");
	cout << "The string to be searched is :"
		 << endl << str << endl;

	int count(0);
	char* pstr(str);
	char* found(nullptr);

	while(true)
	{
		found = strstr(pstr,word); //strstr()返回的地址存储在found中
		if(!found) //如果在pstr中没有找到word,返回nullptr,if语句结束循环
			break;
		++count;  //如果found不为nullptr 递增word的出现次数
		pstr = found + strlen(word); // 更新pstr 使它指向str中找到的word实例后面的那个字符
	}
	cout << "\"" << word << "\" was found "
		 << count <<" times in the string."<< endl;
	return 0;
}

Here Insert Picture Description

4.6 C++/CLI编程

  • CLR的动态内存分配: CLR 维护其独立于本地C++堆的内存堆。当不需要再CLR堆上分配的内存时,CLR会自动将其删除。因此,不必在CLR编写的程序中使用delete操作符。
  • CLR 可以压缩内存堆,以避免不时产生的碎片。即CLR消除了出现内存泄漏和内存碎片的可能性。
  • CLR提供的堆管理和整理机制称作垃圾回收,垃圾即被抛弃的变量和对象;CLR管理的堆被称为 可回收垃圾的堆。
  • 在C++/CLI 程序中,使用gcnew操作符分配内存。前缀gc,表明是在可回收垃圾的堆上分配内存。CLR的垃圾回收器能够删除不再需要的对象,并释放它们所占用的内存。
  • 垃圾回收器如何知道何时不再需要堆上的某个对象?
    CLR记录着每个引用堆内对象的变量,如果所有变量都不包含某个对象的地址,则该对象就不可能在程序中再次被引用,因此可以将其删除。
  • 因为垃圾回收过程可能需要压缩对的内存区域,以消除零碎的未用内存块,所以存储在堆内的数据项的地址可能改变。因此需要访问堆内对象的方法是,应该在垃圾回收器重新安排对内数据项的位置时,能够更新相应的地址。在CLR程序中有2种方法:
    (1) 跟踪句柄(简称句柄);
    (2) 跟踪引用;

4.6.1 跟踪句柄

  • 跟踪句柄:确实存储着某个地震仪,如果堆压缩过程中移动了句柄引用的对象,那么垃圾回收器将自动更新句柄包含的地址。但,不能使用跟踪句柄执行地址的算术操作;跟踪句柄也不允许强制类型转换。

声明跟踪句柄

  • 通过将符号 ^(通常被称为“帽子”) 放在类型名称的后面来指定该类型的句柄。
String^ proverb; 
//声明了一个可以存储string类型对象的地址、名为proverb的跟踪句柄。
//当声明某个句柄时,系统自动将其初始化为空,因此该句柄将不引用任何对象。
// 显式的将某个句柄设置为空:
proverb = nullptr;
String^ saying(L"I used to think I was indecisive but now I'm not so sure ");
// 在堆上创建一个包含括号中字符串的String 对象,在saying中存储新建对象的地址。
// 注意,字符串字面值的类型是 const wchar_t*,而非String,定义Strinig 类的方式使这样的字面值可以用来创建String类对象。

为值类型创建句柄:

int^ value = 99;
// 创建1个int^ 类型的句柄value,并将堆内该句柄指向的值初始化为99.
//我们创建的是一种指针,在没有解除引用的情况下value不能参与算术运算。 解除方式:使用 *运算符。
int result(2*(*value)+15); //*value 访问该跟踪句柄指向的地址存储的整数。

在赋值语句左边使用句柄时,不需要显式的解除引用就可以用来存储结果,编译器将替我们处理好一切:

int^ result(nullptr);
result = 2*(*value)+15; 
// 显式的解除对赋值语句左边句柄的引用
*result = 2*(*value)+15; 

4.6.2 CLR 数组

  • 使用关键字array 指定数字变量的类型,必须在array 关键字后面的尖括号内指定数组元素的类型。
//通用形式:array <element type>^
array <int>^ data;
//数组变量data可以存储任何对元素类型为int 的一维数组的引用

在声明数组变量的同时,可以使用gcnew操作符创建CLR数组:

array<int>^ data = gcnew array<int>(100);
//创建一个名为data的一维数组,数组变量是跟踪句柄。
// 函数表示法来初始化变量data:
array <int>^ data(gcnew array<int> (100));
// 给data数组中的元素赋值:
for(int i=0;i<100;i++)
	data[i] = 2*(i+1);// 该循环将数组元素的值设置为2,4,6...,200.
// CLR数组中的元素都是对象,因此该循环是在数组中存储Int32类型的对象。

// 使用数组Length属性:
for(int i=0;i<data->Length;i++) //使用->操作符来访问Length属性
	data[i] = 2*(i+1);
//Length 属性是32位整数值,记录着数组中元素的数量。

使用for each 循环,逐一处理数组中所有元素

array<int>^ values = { 3,5,6,8,6}; //用由一组值定义的数组来初始化数组句柄,数组大小由{ }之间的初始值数量来确定。
for each(int item in values) // item依次引用数组中的每个元素
{
	item = 2*item + 1; //将当前元素值乘以2在加1的结果存储到item中
	Console::Write("{0,5}",item); 在5字符宽的字段中以右对齐方式输出当前元素的新值。
}
//输出
7  11  13   17   13

创建1个字符串数组:

array <String^>^ names = {"Jack","Jane","Joe","Jessica","Jim","Joanna"};
// 该数组的元素用{ }内的字符串进行初始化,字符串的数量决定着数组元素的数量。
// String 对象是在CLR堆上创建的,因此数组元素属于跟踪句柄类型String^.

如果声明数组时不进行初始化,然后又想用它引用以后创建的数组,那么在使用一列初始值时必须显式的创建该数组:

array<String^>^ names;
names = gcnew array<String^>{"Jack","Jane","Joe","Jessica","Jim","Joanna"};

可以使用Array类中定义的静态Clear()函数,将数组中任一的连续数组元素清零。使用类名可以调用静态函数。

Array::Clear(samples,0,samples->Length);
// Clear()的第1个参数:要被清除的数组;
// 第2个参数:要清除的第1个元素的索引值;
// 第3个参数:要清除的元素数量;
// Ex4_07.cpp: 主项目文件。
// 生成一个随机数组,然后找出其中的最大值
#include "stdafx.h"
using namespace System;

int main(array<System::String ^> ^args)
{
	array <double>^samples = gcnew array<double>(50); //创建一个可存储50个double类型数值的数组,数组变量samples是跟踪句柄
	Random^ generator = gcnew Random; //在CLR堆上创建一个Random类型的对象,Random对象具有一些可生成伪随机数的函数
	for(int i = 0; i< samples->Length;i++)
		samples[i] = 100.0 * generator->NextDouble(); //用double类型的伪随机数给数组元素赋值
	// NextDouble()函数,返回0.0~1.0之间的double类型随机数。100.0与其相乘,得到0.0~100.0之间的值。
	Console::WriteLine(L"The array contains the following values:");
	for(int i = 0;i < samples->Length;i++)
	{
		Console::Write(L"{0,10:F2}",samples[i]); //将各个元素的值输出到控制台,字段宽度10个字符,有2个小数位。
		if((i+1)%5==0)
			Console::WriteLine();//每行5个元素
	}
	double max(0);
	for each(double sample in samples)
		if(max < sample)
			max = sample; //找出数组中的最大值,存储在max中
	Console::WriteLine(L"The maximum value in the array is {0:F2}",max); 

	Console::ReadLine();
	return 0;
}

输出:
Here Insert Picture Description

  • Random对象的Next()函数可以返回int类型的随机非负数。
    • 如果调用Next()函数时提供一个整数作参数,那么该函数将返回小于给定参数的随机非负整数。
    • 如果提供2个整数参数,它们表示将返回的随机整数的最小值和最大值。

1.一维数组排序
System名称空间中Array类定义了一个可将一位数组的元素以升序排列的函数 Sort().为排序某个数组,只需将该数组的句柄传递给Sort()函数即可。

array<int>^ samples = { 27,3,54,11,18,2,16};
Array::Sort(samples);
for each(int value in samples)
	Console::Write(L"{0,8}",value);
Console::WriteLine();
//输出结果:
2	3	11	16	18	27	54

排序数组中的元素:给Sort()函数再多提供2个参数,指定要排序的元素中第一个元素的索引值 和 要排序元素的数量:

array<int>^ samples = { 27,3,54,11,18,2,16};
Array::Sort(samples,2,3); //将samples数组中的3个元素排序,开始的索引位置是2
//输出:
27	3	11	18	54	2	16

排序2个相关数组:

// Ex4_08.cpp: 主项目文件。
// 创建1个存储姓名的数组,并将每个人的体重存储在第2个数组的对应元素中,
// 然后用一个的操作排序这2个数组
#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
	array<String^>^ names = {"Jill","Ted","Mary","Eve","Bill","Al"};
	array<int>^ weights = {103,168,128,115,180,176};

	Array::Sort(names,weights);
	for each(String^ name in names)
		Console::Write(L"{0,10}",name);
	Console::WriteLine();

	for each(int weight in weights)
		Console::Write(L"{0,10}",weight);
	Console::WriteLine();


    Console::ReadLine();
    return 0;
}
//Weights数组中的值是names数组中相同索引位置存储的那个人的体重。程序中调用Sort()函数同时
//排序2个数组,排序时使用第1个数组参数来确定2个数组的顺序。

Here Insert Picture Description

2.搜索一维数组

  • Array类搜索一维数组元素的函数:BinarySearch()函数的版本使用二元搜索算法,在整个数组中或者从给定范围的元素中,查找特定元素的索引位置。
  • 二元搜索算法要求数组元素是顺序排列的,因此需要在搜索之前排序数组元素。
array<int>^ values = {23,45,68,94,123,127,150,203,299};
int toBeFound(127); //要查找的值存储在 toBeFound 变量中
int position = Array::BinarySearch(values,toBeFound); 
// BinarySearch() 第1个参数是被搜索数组的句柄;第2个参数指定要查找的内容。返回的搜索结果是int类型的值。
// 如果在第1个参数指定的数组中找到了第2个参数,则返回目标元素的索引位置;否则返回一个负整数
if(position < 0)
	Console::WriteLine(L"{0} was not found.",toBeFound);
else
	Console::WriteLine(L"{0} was found at index positon {1}.",toBeFound,position);
//输出
127 was found at index position 5.

搜索数组中给定范围的元素,使用接受4个参数的BinarySearch()函数:

  • 第1个参数:被搜索数组的句柄;
  • 第2个参数:开始搜索时对应的元素的索引位置;
  • 第3个参数:搜索的元素数量;
  • 第4个参数:要查找的内容;
array<int>^ values = {23,45,68,94,123,127,150,203,299};
int toBeFound(127);
int position = Array::BinarySearch(values,3,6,toBeFound);

搜索数组:

#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
	array<String^>^ names = {"Jill","Ted","Mary","Eve","Bill"
							,"Al","Ned","Zoe","Dan","Jean"};

	array<int>^weights = {103,168,128,115,180,176,209,98,190,130};
	array<String^>^ toBeFound = {"Bill","Eve","Al","Fred"};

	Array::Sort(names,weights); //用names数组来决定最终顺序

	int result(0);
	for each(String^ name in toBeFound)
	{
		result = Array::BinarySearch(names,name);
		if(result<0)
			Console::WriteLine(L"{0} was not found.",name);
		else 
			Console::WriteLine(L"{0} weighs {1} lbs.",name,weights[result]);
	}

    Console::ReadLine();
    return 0;
}

Here Insert Picture Description

  • 当二元搜索操作失败时,函数返回的数值不是任意的负数。该整数是第1个大于搜索目标的元素索引位置的按位补码,如果没有大于搜索目标的元素,则该整数是数组Length属性的按位补码。
  • 根据这一点,可以使用BinarySearch()函数求出为了仍然保持元素的正确顺序应该在数组的什么位置插入新对象。
array<String^>^ names = {"Jill","Ted","Mary","Eve","Bill"
							,"Al","Ned","Zoe","Dan","Jean"};
Array::Sort(names);
String^ name = L"Fred";
int position (Array::BinarySearch(names,name));
//如果搜索结果为负,将所有位反转就是应该插入新姓名的索引位置,
//如果是正数,说明新姓名与该位置的姓名相同。可以直接将结果用作新位置
if(position<0)  
	position =~ position; 
array <String^>^ newNames = gcnew array<String^>(names->Length+1);
// 将旧数组names中的元素逐一赋值到新数组newNames中
for(int i = 0;i < position;i++)
	newNames[i] = names[i];
newNames[position] = name;
if(position < names->Length)
	for(int i = position;i < names->Length ; i++)
		newNames[i+1] = names[i];

3. 多维数组
在< >内元素类型后面指定数组的维数,中间用逗号隔开。数据的维数默认是1.

array<int,2>^ values = gcnew array<int,2>(4,5);//创建一个4行5列的2维数组

//访问多维数组的元素,需要指定多个索引值。每一维1个,这些索引值必须写在数组名后面的方括号内,中间以逗号分开。
int nrows(4);
int ncols(5);
array <int,2>^ values(gcnew array<int,2>(nrows,ncols));
for (int i = 0 ; i < nrows ; i++)
	for(int j = 0; j < ncols ; j++)
		values[i,j] = (i+1)*(j+1);
  • 注意,不能对二维C++/CLI数组使用单一的索引值,因为这种数组是元素的二维数组,而非数组的数组。
// 在二维数组中创建12x12的乘法表
#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
	const int SIZE(12);
	array <int,2>^ products(gcnew array<int,2>(SIZE,SIZE));//创建等级为2的数组

	for(int i = 0; i < SIZE ; i++)
		for(int j = 0; j < SIZE; j++)
			products[i,j] = (i+1)*(j+1); //设置product数组中各元素的值

	Console::WriteLine(L"Here is the {0} times table:",SIZE);

	for(int i = 0; i <= SIZE ; i++)
		Console::Write(L"____");
	Console::WriteLine();

	Console::Write(L"    |");
	for(int i = 1; i <= SIZE ; i++)
		Console::Write(L"{0,3} |",i);
	Console::WriteLine();

	for(int i = 0; i <= SIZE ; i++)
		Console::Write(L"____|");
	Console::WriteLine();

	for(int i = 0;i < SIZE; i++)
	{
		Console::Write(L"{0,3} |",i+1);
		for(int j = 0; j < SIZE; j++)
			Console::Write(L"{0,3} |",products[i,j]);
		Console::WriteLine();
	}

	for(int i = 0; i <= SIZE ; i++);
		Console::Write(L"____");
	Console::WriteLine();

	Console::ReadLine();
    return 0;
}

Here Insert Picture Description

4. 数组的数组

  • 数组元素可以是任意类型,因此也可以是引用数组的跟踪句柄。
    例:按照得分等级,分组存储班内儿童的姓名,等级为A、B、C、D、E.
    首先创建包含5个元素的数组,各个元素存储一个姓名数组。
array< array< String^ >^ >^ grades (gcnew array < array<String^>^>(5));
// 数组变量grades 是 array<type>^ 类型的句柄,
// 该数组的每个元素也是数组的句柄,即 array<type>^
// 故 结果为 array < array<type>^>^ ,由于是字符串类型 即 String^ (字符串类型的句柄)

使用数组的数组:

#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
	array < array< String^>^ >^ grades = gcnew array < array< String^>^ > 
	{
		gcnew array<String^> {"Louise","Jack"},					// Grade A
		gcnew array<String^> {"Bill","Mary","Ben","Joan"},		// Grade B
		gcnew array<String^> {"Jill","Will","Phil"},			// Grade C
		gcnew array<String^> {"Ned","Fred","Ted","Jed","Ed"},	// Grade D
		gcnew array<String^> {"Dan","Ann"}					// Grade E
	};

	wchar_t gradeLetter('A');// 该变量用来在输出中给出等级

	for each(array <String^>^ grade in grades) 
	{
		Console::WriteLine(L"Students with Grade {0}:",gradeLetter++);

		for each(String^ student in grade)
			Console::Write(L"{0,12}",student);
		Console::WriteLine();
	}
	Console::ReadLine();
    return 0;
}

Here Insert Picture Description

4.6.3 字符串

  • String类:表示由System::Char 类型的字符序列组成的字符串。
  • 创建字符串:
System::String^ saying (L"Many hands make light work.");
// 变量saying是一个跟踪句柄,它引用被初始化为圆括号中字符串的String对象。
  • 使用下标访问字符串中的各个字符:
Console::WriteLine(L"The third character in the string si {0}.",saying[2]);
//注意:只能使用索引值来检索字符串中的字符,不能以这种方式更新字符串。
//String对象是固定不变的,不能被修改。
  • 通过访问Length属性,获取字符串的长度
Console::WriteLine(L"The string has {0} characters.",saying->Length);
// saying 是跟踪句柄(一种指针),必须使用->运算符才能访问Length属性。

1.连接字符串

  • 使用 + 运算符连接:
String^ name1(L"Beth");
String^ name2(L"Betty");
String^ name3(name1 + L"and" + name2); 
// 执行结果:Beth and Betty
还可以连接String对象和数值或bool值,在连接操作之前这些值被自动转换为字符串
String^ str(L"Value: ");
String^ str1(str + 2.5);	//Result is new string L"Value: 2.5"
String^ str1(str + 25);	//Result is new string L"Value: 25"
String^ str1(str + true);	//Result is new string L"Value: true"
//连接String对象和字符,结果取决于字符的类型:
char ch('Z');
wchar_t wch(L'Z');
String^ str4(str + ch); 	//Result is new string L"Value: 90"
String^ str5(str + wch); 	//Result is new string L"Value: Z"
//char 类型的字符被作为数值对待,即字符Z的代码值
//wchar_t 字符与String对象中的字符是相同类型(Char类型),故字符Z附加到字符串后面。
  • 注意:String对象是固定不变的,所有表面上在修改String对象的操作实际上都是在创建新的String对象。
  • Join()函数:用于将数组中存储的多个字符串连接成一个字符串,原来的字符串之间用分隔符分开。
array<String^>^ names ={L "Jill",L"Ted",L"Mary",L"Eve",L"Bill"};
String^ separator(L",");
String^ jioned = String::join(separator,names);
//输出:jioned将引用字符串L"Jill,Ted,Marry,Eve,Bill"

处理字符串:

//假设有一个整型数组,希望按列对齐输出这些整数。不仅要求输出数字对齐
//而且要求列字段够宽,以容纳数组中最长的值和列与列之间的间隔。
#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
	array<int>^ values = {2,456,23,-46,34211,456,5609,112098,234,-76504,
							341,6788,-909121,99,10};
	String^ formatStr1(L"{0,");
	String^ formatStr2(L"}");
	String^ number;

	int maxLength(0);
	for each(int value in values)
	{
		number = L" "+ value;
		if(maxLength < number->Length)
			maxLength = number->Length;
	}
	String^ format(formatStr1 + (maxLength+1) + formatStr2);// 创建格式字符串

	int numberPerLine(3);
	for(int i=0;i< values->Length;i++)
	{
		Console::Write(format,values[i]);//使用format格式输出
		if((i+1)%numberPerLine == 0)
			Console::WriteLine();
	}

	Console::ReadLine();
	return 0;
}

Here Insert Picture Description

2. 修改字符串

  • 删除字符串头部和尾部的空格,Trim()函数
String^ str = {L" Handsome is as handsome does... "};
String^ newStr(str->Trim()); // Trim()函数将删除str头部和尾部的所有空格
//指定从字符串的头部和尾部删除的字符
String^ toBeTrimed(L"wool wool sheep sheep wool wool wool");
array<wchar_t>^ notWanted = {L'w',L'o',L'l',L' '};
Console::WriteLine(toBeTrimed -> Trim(notWanted));
//输出:sheep sheep

//Trim()函数中显示的指定要删除的字符
Console::WriteLine(toBeTrimed -> Trim(L'w',L'o',L'l',L' '));

只剪裁字符串的一端,可以使用TrimEnd()或TrimStart()

- 不带任何参数,则删除空格; 
- 以数组作为参数,则删除在数组中指定的字符; 
- 使用显示的wchar_t参数,则删除这些字符;
  • 填充空格或其他字符:PadLeft()和PadRight()函数 分别在字符串的左边或右边填充字符。
  • 用途:在宽度固定的字段中靠左或靠右对齐字符串是格式化输出。
//指定结果字符串长度
String^ value(L"3.142");
String^ leftPadded(value->PadLeft(10));	// Result: L"   3.142"
String^ rightPadded(value->PadRight(10));	// Result: L"3.142   "
//如果参数指定的长度小于或等于原来字符串的长度,则返回一个与原来字符串相同的新String对象

//填充字符
String^ value(L"3.142");
String^ leftPadded(value->PadLeft(10,L'*'));	// Result: L"*****3.142"
String^ rightPadded(value->PadRight(10,L'#'));	// Result: L"3.142#####"

//将整个字符串转换为大写或小写字母:ToUpper() 和 ToLower()函数
String^ proverb(L"Many hands make light work.");
String^ upper(proverb->ToUpper());
//Result: L"MANY HANDS MAKE LIGHT WORK."

//在现有字符串的给定位置插入一个字符串:Insert()函数
String^ proverb(L"Many hands make light work.");
String^ newProverb(proverb->Insert(5,L"deck"));
//Result: Many deck hands make light work.

//字符替换,字符串替换.Replace(要被替换的字符或子串,替换者):
String^ proverb(L"Many hands make light work.");
Console::WriteLine(proverb->Replace(L' ',L'*'));
Console::WriteLine(proverb->Replace(L"Many hands",L"Pressing switch"));
//Result: Many*hands*make*light*work.
//Result: Pressing switch make light work.

3. 比较字符串

  • Compare()函数:比较2个String对象。函数返回1个整数,根据第1个参数是<,=,> 第2个参数,返回值将<0,=0,>0.
String^ him(L"Jacko");
String^ her(L"Jillo");
int result(String::Compare(him,her));
if(result<0)
	Console::WriteLine(L"{0} is less than {1}.",him,her);
else if(result>0)
	Console::WriteLine(L"{0} is greater than {1}.",him,her);
else
	Console::WriteLine(L"{0} is equal to {1}.",him,her);

Compare()函数另一个版本,需要第3个参数,类型为bool值,如果第3个参数为True,那么比较前2个参数引用的字符串时就忽略大小写。

4.搜索字符串

  • To test whether a string to the start or end of a string StartsWith () given sub and EndsWith ().
    Parameters: To find a substring handle; Returns: a bool value that indicates whether the substring exists.
String^ sentence(L"Hide,the cow's outside.");
if(sentence->StartsWith(L"Hide"))
	Console::WriteLine(L"The sentence starts with 'Hide'.");
//输出:The sentence starts with 'Hide'.
  • The IndexOf () Function: Character search for a given position of character or substring in the first occurrence of the string. Returns the search is successful the corresponding index value; -1 failure parameters: looking for a character or substring
String^ sentence(L"Hide, the cow's outside.");
int ePosition(sentence->IndexOf(L'e'));		// Returns 3
int theposition(sentence->IndexOf(L'the'));	// Returns 6
  • To find out all the character positions for a given character or substring occurs in a string
String^ words(L"wool wool sheep sheep wool wool wool");
String^ word(L"wool");
int index(0);
int count(0);
while((index = words->IndexOf(word,index))>=0) // index指定搜索开始的索引位置
{
	index += word->Length;
	++count;
}
Console::WriteLine(L"'{0}' was found {1} times in:\n {2}",word,count,words);
//输出: ‘wool’ was found 5 times in:
	      wool wool sheep sheep wool wool wool 
  • The LastIndexOf () function: the rear or back the specified index search string.
int index(words->Length-1);
int count(0);
while(index >= 0 && (index = words->LastIndexOf(word,index))>= 0)
{
	--index;
	++count;
}
  • IndexOfAny () function, the search string in the first instance to provide his wchar_t any character type array as a parameter.
// 在字符串中搜索标点符号
#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
	// 创建包含要查找的字符的数组
	array<wchar_t>^ punctuation = {L'"',L'\'',L'.',L',',L':',L';',L'!',L'?'}; 
	
	String^ sentence(L"\"It's chilly in here\",the boy's mother said coldly.");
	// 定义一个字符数组,初始化为空格符
	array<wchar_t>^ indicators(gcnew array<wchar_t>(sentence->Length){L' '});

	int index(0);
	int count(0);
	while((index = sentence->IndexOfAny(punctuation,index))>=0)
	{
		indicators[index] = L'^'; //找到标点符号 将indicators 中适当的数组元素改为‘^’
		++index;
		++count;
	}
	Console::WriteLine(L"There are {0} punctuation charators in the string :",count);
	// 将indicators数组传递给String类的构造函数,从而在堆上根据该数组创建一个新的String对象。
	Console::WriteLine(L"\n{0}\n{1}",sentence,gcnew String(indicators));

    Console::ReadLine();
    return 0;
}

Here Insert Picture Description

4.6.4 Tracking quote

  • Track, means that some CLR heap object alias.
  • % Using operator defined reference tracking:
int value(10);
int% trackValue(value);
//将trackValue定义为已经在栈上创建的value变量的跟踪引用。
trackValue *= 5;
Console::WriteLine(value);
//输出: 50

4.6.5 Internal pointer

  • Use the keyword interior_ptr definition of interior pointers. Automatic local variables inside a function pointer is always.
array<double>^ data = {1.5,3.5,6.7,4.2,2.1};
interior_ptr<double> pstart(&data[0]); 
//必须在interior_ptr后的<>内指定内部指针指向的对象类型
// 使用& 运算符以数组中第1个元素的地址初始化该指针
// 不提供初始值,则系统默认将其初始化为nullptr。
  • In terms of the type of internal pointers specified limits:
    • Internal address pointers may contain values ​​on the stack class object;
    • It may contain the address CLR heap handle pointing to an object;
    • CLR objects can not contain the entire heap address;
    • You can point to a local or local class object pointer;
// 使用指向数值和字符串的内部指针
#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
	array<double>^ data = {1.5,3.5,6.7,4.2,2.1};
	interior_ptr<double> pstart(&data[0]);
	interior_ptr<double> pend(&data[data->Length-1]);
	double sum(0.0);

	while(pstart <= pend)
		sum += *pstart++;
	Console::WriteLine(L"Total of data array elements = {0}\n",sum);

	array<String^>^ strings = {L"Land ahoy!",L"Splice the mainbrace!",
								L"Shiver me timbers!",L"Never throw into the wind!"};

	for(interior_ptr<String^> pstrings = & strings[0];
		pstrings - &strings[0]< strings->Length; ++pstrings)
		Console::WriteLine(*pstrings);

    Console::ReadLine();
    return 0;
}

Here Insert Picture Description

4.9 This chapter contents
Here Insert Picture Description
Here Insert Picture Description

To be continue…

Guess you like

Origin blog.csdn.net/madao1234/article/details/84981792