数组
在定义一个数组的时候,中括号[]中的elements 域必须是一个常量数值,因为数组是内存中一块有固定大小的静态空间,编译器必须在编译所有相关指令之前先能够确定要给该数组分配多少内存空间。
当声明一个本地范围内(在一个函数内)的数组时,除非我们特别指定,否则数组将不会被初始化,因此它的内容在我们将数值存储进去之前是不定的。如果我们声明一个全局数组(在所有函数之外),则它的内容将被初始化为所有元素均为0。
在C++ 中对数组使用超出范围的index是合法的,这就会产生问题,因为它不会产生编译错误而不易被察觉,但是在运行时会产生意想不到的结果,甚至导致严重运行错误。超出范围的index 之所以合法的原因我们在后面学习指针(pointer)的时候会了解。
方括号[ ]在对数组操作中的两种不同用法。它们完成两种任务:一种是在声明数组的时候定义数组的长度;另一种是在引用具体的数组元素的时候指明一个索引号(index)。b = billy [a+2];//[]中可以放表达式 billy[billy[a]] = billy[2] + 5;
多维数组(Multidimensional arrays)并不局限于2维。如果需要,它可以有任意多维,虽然需要3维以上的时候并不多。但是考虑一下一个有很多维的数组所需要的内存空间。多维数组只是一个抽象的概念,因为我们只需要把各个索引的乘积放入一个简单的数组中就可以获得同样的结果。
int jimmy [3][5]; (效果上等价于?) int jimmy [15]; (3 * 5 = 15)
唯一的区别是编译器帮我们记住每一个想象中的维度的深度。下面的例子中我们就可以看到,两段代码一个使用2维数组,另一个使用简单数组,都获得同样的结果,即都在内存中开辟了一块叫做jimmy的空间,这个空间有15个连续地址位置,程序结束后都在相同的位置上存储了相同的数值,如后面图中所示:
// multidimensional array #include <iostream.h> #define WIDTH 5 #define HEIGHT 3 int jimmy [HEIGHT][WIDTH]; int n,m; int main (){ for (n=0; n<HEIGHT; n++) { for (m=0; m<WIDTH; m++) jimmy[n][m]=(n+1)*(m+1); } return 0; }
// pseudo-multidimensional array #include <iostream.h> #define WIDTH 5 #define HEIGHT 3 int jimmy [HEIGHT * WIDTH]; int n,m; int main (){ for (n=0; n<HEIGHT; n++){ for (m=0; m<WIDTH; m++) jimmy[n * WIDTH + m]=(n+1)*(m+1); } return 0; }
上面两段代码并不向屏幕输出,但都向内存中的叫做jimmy的内存块存相同内容。
// arrays as parameters #include <iostream.h> void printarray (int arg[ ], int length) { for (int n=0; n<length; n++) { cout << arg[n] << " "; } cout << "\n"; } int main () { int firstarray[ ] = {5, 10, 15}; int secondarray[ ] = {2, 4, 6, 8, 10}; printarray (firstarray,3); printarray (secondarray,5); return 0; }
可以看到,函数的第一个参数(int arg[ ])接受任何整型数组为参数,不管其长度如何。因此,我们用了第2个参数来告知函数我们传给它的第一个参数数组的长度。这样函数中打印数组内容的for 循环才能知道需要检查的数组范围。
在函数的声明中也包含多维数组参数。定义一个3维数组 (tridimensional array) 的形式是:base_type[ ][depth][depth]
例如,一个函数包含多维数组参数的函数可以定义为:
void procedure (int myarray[ ][3][4])
注意第一对括号[ ]中为空,而后面两对不为空。这是必须的,因为编译器必须能够在函数中确定每一个增加的维度的深度。
字符序列 (Character Sequences)
C++的标准函数库提供了一个string类来支持对字符串的操作。然而,字符串实际就是一串连续的字符序列,所以我们也可以用简单的字符数组来表示它。
字符数组经常被用于存储短于其总长的字符串,在字符串的有效内容的结尾处加一个空字符(null character)来表示字符结束,它的常量表示可写为0 或’\0’。注意在有效内容结尾是如何用空字符null character (‘\0’)来表示字符串结束的。 后面灰色的空格表示不确定数值。char mystring[] = { 'H', 'e', 'l', 'l', 'o', '\0' }; char mystring[] = "Hello"; /*注意:同时给数组赋多个值只有在数组初始化时,也就是在声明数组时,才是合法的。象下面代码现实的表达式都是错误的:*/ mystring = "Hello"; mystring[ ] = "Hello"; mystring = { 'H', 'e', 'l', 'l', 'o', '\0' };
只有在数组初始化时才能够同时赋多个值给它。其原因在学习了指针(pointer)之后会比较容易理解,因为那时会看到一个数组其实只是一个指向被分配的内存块的常量指针(constant pointer),数组自己不能够被赋予任何数值,但我们可以给数组中的每一个元素赋值。
在数组初始化的时候是特殊情况,因为它不是一个赋值,虽然同样使用了等号(=) 。
因为赋值运算的 left value 只能是数组的一个元素,而不能使整个数组,所以,用以下方式将一个字符串赋给一个字符数组是合法的:mystring[0] = 'H'; mystring[1] = 'e'; mystring[2] = 'l'; mystring[3] = 'l'; mystring[4] = 'o'; mystring[5] = '\0';
但正如你可能想到的,这并不是一个实用的方法。通常给数组赋值,或更具体些,给字符序列赋值的方法是使用一些函数,例如strcpy。strcpy (string copy) 在函数库cstring (string.h) 中被定义,可以用以下方式被调用:
strcpy (string1, string2);
这个函数将string2 中的内容拷贝给string1。string2 可以是一个数组,一个指针,或一个字符串常量constant string。// setting value to string #include <iostream.h> #include <string.h>//必要 int main () { char szMyName [20]; strcpy (szMyName,"J. Soulie"); cout << szMyName; return 0; }
另一个给数组赋值的常用方法是直接使用输入流(cin)。在这种情况下,字符序列的值是在程序运行时由用户输入的。
当cin 被用来输入字符序列值时,它通常与函数getline 一起使用,方法如下:cin.getline ( char buffer[], int length, char delimiter = ' \n');
这里buffer 是用来存储输入的地址(例如一个数组名),length 是一个缓存buffer 的最大容量,而delimiter 是用来判断用户输入结束的字符,它的默认值(如果我们不写这个参数时)是换行符newline character (‘\n’)。
两次调用 cin.getline 时我们都使用了同一个字符串标识 (mybuffer)。程序在第二次调用时将新输入的内容会直接覆盖到第一次输入到buffer 中的内容。cin >> mybuffer;
这种方法也可以工作,但它相比有以下局限性:
- 它只能接收单独的词(而不能是完整的句子),因为这种方法以任何空白符为分隔符,包括空格spaces,跳跃符tabulators,换行符newlines和回车符arriage returns。
- 它不能给buffer指定容量,这使得程序不稳定,如果用户输入超出数组长度,输入信息会被丢失。
因此,建议在需要用cin来输入字符串时,使用cin.getline来代替cin >>。
鉴于字符串可能包含其他数据类型的内容,例如数字,将字符串内容转换成数字型变量的功能会有用处。例如一个字符串的内容可能是”1977”,但这一个5个字符组成序列,并不容易转换为一个单独的整数。因此,函数库cstdlib (stdlib.h) 提供了3个有用的函数:atoi: 将字符串string 转换为整型int atol: 将字符串string 转换为长整型long atof: 将字符串string 转换为浮点型float
所有这些函数接受一个参数,返回一个指定类型的数据(int, long 或 float)。这三个函数与cin.getline 一起使用来获得用户输入的数值,比传统的cin>> 方法更可靠:
// cin and ato* functions #include <iostream.h> #include <stdlib.h> int main () { char mybuffer [100]; float price; int quantity; cout << "Enter price: "; cin.getline (mybuffer,100); price = atof (mybuffer); cout << "Enter quantity: "; cin.getline (mybuffer,100); quantity = atoi (mybuffer); cout << "Total price: " << price*quantity; return 0; }
Enter price: 2.75
Enter quantity: 21
Total price: 57.75指针 (Pointers)
(只写点特别的)
指针void 是一种特殊类型的指针。void 指针可以指向任意类型的数据,可以是整数,浮点数甚至字符串。唯一个限制是被指向的数值不可以被直接引用(不可以对它们使用引用星号*),因为它的长度是不定的,因此,必须使用类型转换操作或赋值操作来把void 指针指向一个具体的数据类型。
它的应用之一是被用来给函数传递通用参数:// integer increaser #include <iostream.h> void increase (void* data, int type) { switch (type) { case sizeof(char) : (*((char*)data))++; break; case sizeof(short): (*((short*)data))++; break; case sizeof(long) : (*((long*)data))++; break; } } int main () { char a = 5; short b = 9; long c = 12; increase (&a,sizeof(a)); increase (&b,sizeof(b)); increase (&c,sizeof(c)); cout << (int) a << ", " << b << ", " << c; return 0; }
输出 : 6, 10, 13
C++ 允许对指向函数的指针进行操作。它最大的作用是把一个函数作为参数传递给另外一个函数。声明一个函数指针像声明一个函数原型一样,除了函数的名字需要被括在括号内并在前面加星号asterisk (*)。例如:// pointer to functions #include <iostream.h> int addition (int a, int b) { return (a+b); } int subtraction (int a, int b) { return (a-b); } int (*minus)(int,int) = subtraction; int operation (int x, int y, int (*functocall)(int,int)) { int g; g = (*functocall)(x,y); return (g); } int main () { int m,n; m = operation (7, 5, addition); n = operation (20, m, minus); cout <<n; return 0; } //输出:8
在这个例子里, minus 是一个全局指针,指向一个有两个整型参数的函数,它被赋值指向函数subtraction,所有这些由一行代码实现:
int (* minus)(int,int) = subtraction;
这里似乎解释的不太清楚,有问题问为什么(int int)只有类型,没有参数,就再多说两句。
这里 int (*minus)(int int)实际是在定义一个指针变量,这个指针的名字叫做minus,这个指针的类型是指向一个函数,函数的类型是有两个整型参数并返回一个整型值。
整句话“int (*minus)(int,int) = subtraction;”是定义了这样一个指针并把函数subtraction的值赋给它,也就是说有了这个定义后minus就代表了函数subtraction。因此括号中的两个int int实际只是一种变量类型的声明,也就是说是一种形式参数而不是实际参数。
C++初步(3)
最新推荐文章于 2021-05-22 09:37:55 发布