C++学习笔记之四 | 字数总计: 4.4k | 阅读时长: 18分钟 | 阅读量: |
数组 数组是可以存储一个固定大小的相同类型元素的顺序集合,声明数组的格式为:
1 type arrayName [ arraySize ];
这是一维数组
arraySize必须是一个大于零的整数常量,type可以是任意有效的C++数据类型,举例:
类型为double的包含10个元素的数组balance,现在balance是一个可用的数组,可以容纳10个类型为 double 的数字
数组初始化 例如:
1 2 3 balance[4 ] = 50.0 ; double balance[5 ] = {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 };double balance[] = {1000.0 , 2.0 , 3.4 , 7.0 , 50.0 };
如上述例子所示,可以逐个对数组里的元素赋值,也可以直接创建并赋值,这里需要注意的是,数组每个元素的索引是从 [0]
开始的,同时给数组赋值时,赋值的元素个数不能超过创建数组的长度,如果创建数组时留空,那么这个数组的长度就是所赋值的元素个数
访问数组元素 例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> #include <iomanip> int main () { int n[ 10 ]; for (int i = 0 ; i < 10 ; i++) { n[ i ] = i + 100 ; } std::cout << "Element" << std::setw (13 ) << "Value" << std::endl; for (int j = 0 ; j < 10 ; j++) { std::cout << std::setw (7 )<< j << std::setw (13 ) << n[j] << std::endl; } return 0 ; }
setw()
函数只是用来格式化输出
输出结果:
多维数组 常见的就是二维数组:
1 type arrayName [ x ][ y ];
说白了,二维数组就类似个Excel表格,x和y表示第几行第几列的某个元素,例如:
1 2 3 4 5 6 7 int a[3 ][4 ] = { {0 , 1 , 2 , 3 }, {4 , 5 , 6 , 7 }, {8 , 9 , 10 , 11 } }; int a[3 ][4 ] = {0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 };
访问二维数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> int main () { int a[5 ][2 ] = { {0 ,0 }, {1 ,2 }, {2 ,4 }, {3 ,6 }, {4 ,8 } }; for (int i = 0 ; i < 5 ; i++) for (int j = 0 ; j < 2 ; j++) { std::cout << "a[" << i << "][" << j << "]: " ; std::cout << a[i][j]<< std::endl; } return 0 ; }
输出结果:
字符串 在C语言中,字符串实际上是使用null字符 \0
终止的一维字符数组,例如:
1 2 3 char site[7 ] = {'H' , 'e' , 'l' , 'l' , 'o' , '!' , '\0' };char site[] = "Hello!" ;
C++编译器会在初始化字符串数组时,自动把 \0
放在字符串的末尾
示例:
1 2 3 4 5 6 #include <iostream> int main () { char site[7 ] = {'H' , 'e' , 'l' , 'l' , 'o' , '!' , '\0' }; std::cout << site << std::endl; return 0 ; }
下面这些函数可以用来操作以null结尾的数组字符串:
strcpy(s1, s2);
复制字符串s2到字符串s1
strcat(s1, s2);
连接字符串s2到字符串s1的末尾,连接字符串也可以用 +
号
strlen(s1);
返回字符串s1的长度
strcmp(s1, s2);
如果s1和s2是相同的,则返回0;如果s1<s2则返回值小于0;如果s1>s2则返回值大于0
strchr(s1, ch);
返回一个指针,指向字符串s1中字符ch的第一次出现的位置
strstr(s1, s2);
返回一个指针,指向字符串s1中字符串s2的第一次出现的位置
在C++中,引入了 string
这个类,可以直接定义字符串,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> #include <string> int main () { std::string str1 = "Hello" ; std::string str2 = "World" ; std::string str3; int len; str3 = str1; std::cout << "str3 : " << str3 << std::endl; str3 = str1 + str2; std::cout << "str1 + str2 : " << str3 << std::endl; len = str3.size (); std::cout << "str3.size() : " << len << std::endl; return 0 ; }
执行结果:
指针 每一个变量都有一个内存位置,每一个内存位置都定义了可使用 &
运算符访问的地址,它表示了在内存中的一个地址
1 2 3 4 5 6 7 8 9 10 #include <iostream> int main () { int var1; char var2[10 ]; std::cout << "var1 变量的地址: " ; std::cout << &var1 << std::endl; std::cout << "var2 变量的地址: " ; std::cout << &var2 << std::endl; return 0 ; }
输出:
指针是一个变量,值为另一个变量的地址,即内存位置的直接地址,指针变量声明:
type是指针的基类型(数据类型),var_name是指针变量的名称
所有指针的值的实际数据类型 ,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数,不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同
指针的作用:指针变量传递的时候只传递该变量的内存地址,需要调用该变量的时候直接通过内存地址来访问到实际的内容,减少变量内容过大的时候传递耗时
此为个人理解
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> int main () { int var = 20 ; int *ip; ip = &var; std::cout << "Value of var variable: " ; std::cout << var << std::endl; std::cout << "Address stored in ip variable: " ; std::cout << ip << std::endl; std::cout << "Value of *ip variable: " ; std::cout << *ip << std::endl; return 0 ; }
执行结果:
数组指针 在数组的定义中,一个数组的名称就是指向该数组中第一个元素的一个常量指针 ,例如:
1 2 3 4 5 6 double myArray[5 ] = {1 , 2 , 3 , 4 , 5 };double *p;p = myArray;
那么此时,直接使用 *p
就可以访问到 myArray[0]
的值,也就是1
同样的道理,使用 *(myArray +2)
就可以访问到 myArray[2]
的值,也就是3,示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <iostream> int main () { double myArray[5 ] = {1000.0 , 2.0 , 3.4 , 17.0 , 50.0 }; double *p; p = myArray; std::cout << "使用指针的数组值 " << std::endl; for (int i = 0 ; i < 5 ; i++) { std::cout << "*(p + " << i << ") : " ; std::cout << *(p + i) << std::endl; } std::cout << "使用myArray作为地址的数组值 " << std::endl; for (int i = 0 ; i < 5 ; i++) { std::cout << "*(myArray + " << i << ") : " ; std::cout << *(myArray + i) << std::endl; } return 0 ; }
p是一个指向double型的指针,也就是说它可以存储一个double类型的变量
执行结果:
传递数组给函数 C++中可以通过指定不带索引的数组名来传递一个指向数组的指针
当传递数组给一个函数,数组类型自动转换为指针类型,因而传的实际是数组的内存地址
当然,指针只是其中一种方式,还可以如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 void myFunction (int *param) { } int myArray[2 ] = {1 , 2 };myFunction (myArray);void myFunction (int param[10 ]) {} void myFunction (int param[]) { }
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> double getAverage (int arr[], int size) ;int main () { int balance[5 ] = {1000 , 2 , 3 , 17 , 50 }; double avg; avg = getAverage (balance, 5 ); std::cout << "平均值是:" << avg << std::endl; return 0 ; } double getAverage (int arr[], int size) { int i, sum = 0 ; double avg; for (i = 0 ; i < size; ++i) { sum += arr[i]; } avg = double (sum) / size; return avg; }
这里的 ++i
表示先自增,后引用自增后的值
执行结果:
从函数返回数组 C++不允许返回一个完整的数组作为函数的参数,但是可以通过指定不带索引的数组名来返回一个指向数组的指针
如果要从函数返回一个一维数组,则必须声明一个返回指针的函数:
C++中不能简单地返回指向函数内部的局部数组的指针,因为当函数结束时,局部数组将被销毁,指向它的指针将变得无效,除非定义局部变量为 static
变量,例如:
1 2 3 4 int *myFunction () { static int myArray[3 ] = {1 , 2 , 3 }; return myArray; }
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <cstdlib> #include <ctime> int *getRandom () { static int r[10 ]; srand ((unsigned )time (NULL )); for (int i = 0 ; i < 10 ; ++i) { r[i] = rand (); std::cout << r[i] << std::endl; } return r; } int main () { int *p; p = getRandom (); for ( int i = 0 ; i < 10 ; i++ ) { std::cout << "*(p + " << i << ") : " ; std::cout << *(p + i) << std::endl; } return 0 ; }
执行结果:
动态分配数组 使用动态分配数组需要在函数内部使用 new
来分配一个数组,并在函数结束时使用 delete
释放该数组,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int *myFunction () { int *myArray = new int [3 ]; myArray[0 ] = 1 ; myArray[1 ] = 2 ; myArray[2 ] = 3 ; return myArray; } int main () { int *result = myFunction (); delete [] result; return 0 ; }
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> int *createArray (int size) { int *arr = new int [size]; for (int i = 0 ; i < size; i++) { arr[i] = i + 1 ; } return arr; } int main () { int *myArray = createArray (5 ); for (int i = 0 ; i < 5 ; i++) { std::cout << myArray[i] << " " ; } std::cout << std::endl; delete [] myArray; return 0 ; }
执行结果:
空指针 在指针变量声明的时候,如果没有确切的地址可以赋值,为其赋一个 NULL
值是一个良好的编程习惯,这种指针就称为为空指针,例如:
1 2 3 4 5 6 #include <iostream> int main () { int *ptr = NULL ; std::cout << "ptr 的值是 " << ptr; return 0 ; }
输出:
在大多数的操作系统上,程序不允许访问地址为0的内存,因为该内存是操作系统保留的
内存地址0有特别重要的意义,它表明该指针不指向一个可访问的内存位置
但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西
如需检查一个空指针,可以使用if语句,如下所示:
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> int main () { int *ptr = NULL ; if (ptr) { std::cout << "ptr 的值是 " << ptr; } if (!ptr) { std::cout << "ptr的值为空!" << ptr; } return 0 ; }
执行结果:
指针递增递减 如果 ptr
指向一个内存地址 为1000的字符,执行 ptr++
指针 ptr
的值会增加,指向下一个字符元素的地址,由于 ptr
是一个字符指针,每个字符占据1个字节,因此 ptr++
会将 ptr
的值增加1,执行后 ptr
指向内存地址 1001
那么如果当一个指针p加上一个整数n时,结果是指针p向前移动n个元素的大小。例如,如果p是一个int类型的指针,每个int占4个字节,那么 p+1
将指向p所指向的下一个int元素,内存地址也会增加移动了4
定义有点抽象,举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> const int MAX = 3 ;int main () { int var[MAX] = {10 , 100 , 200 }; int *ptr; ptr = var; for (int i = 0 ; i < MAX; i++) { std::cout << "Address of var[" << i << "] = " ; std::cout << ptr << std::endl; std::cout << "Value of var[" << i << "] = " ; std::cout << *ptr << std::endl; ptr++; } return 0 ; }
执行结果:
递减也是同样的道理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> const int MAX = 3 ;int main () { int var[MAX] = {10 , 100 , 200 }; int *ptr; ptr = &var[MAX-1 ]; for (int i = MAX; i > 0 ; i--) { std::cout << "Address of var[" << i << "] = " ; std::cout << ptr << std::endl; std::cout << "Value of var[" << i << "] = " ; std::cout << *ptr << std::endl; ptr--; } return 0 ; }
执行结果:
指针的比较 修改上述递增的例子,将判断条件改为变量指针所指向的地址 小于或等于数组的最后一个元素的地址 &var[MAX - 1]
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> const int MAX = 3 ;int main () { int var[MAX] = {10 , 100 , 200 }; int *ptr; ptr = var; int i = 0 ; while ( ptr <= &var[MAX - 1 ] ) { std::cout << "Address of var[" << i << "] = " ; std::cout << ptr << std::endl; std::cout << "Value of var[" << i << "] = " ; std::cout << *ptr << std::endl; ptr++; i++; } return 0 ; }
执行结果:
引用 引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字,一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量
引用和指针的区别:
不存在空引用,引用必须连接到一块合法的内存
一旦引用被初始化为一个对象,就不能被指向到另一个对象,而指针可以在任何时候指向到另一个对象
引用必须在创建时被初始化,指针可以在任何时间被初始化
引用的声明:
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> int main () { int i; double d; int & r = i; double & s = d; i = 5 ; std::cout << "Value of i : " << i << std::endl; std::cout << "Value of i reference : " << r << std::endl; d = 11.7 ; std::cout << "Value of d : " << d << std::endl; std::cout << "Value of d reference : " << s << std::endl; return 0 ; }
执行结果:
把引用作为参数 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> void swap (int &x, int &y) ;int main () { int a = 100 ; int b = 200 ; std::cout << "交换前,a 的值:" << a << std::endl; std::cout << "交换前,b 的值:" << b << std::endl; swap (a, b); std::cout << "交换后,a 的值:" << a << std::endl; std::cout << "交换后,b 的值:" << b << std::endl; return 0 ; } void swap (int &x, int &y) { int temp; temp = x; x = y; y = temp; return ; }
int& x
== int &x
,指针同理
执行结果:
把引用作为返回值 通过使用引用来替代指针,会使代码更容易阅读和维护
C++函数可以返回一个引用,方式与返回一个指针类似
当函数返回一个引用时,则返回一个指向返回值的隐式指针,这样,函数就可以放在赋值语句的左边
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <iostream> double vals[] = {10.1 , 12.6 , 33.1 , 24.1 , 50.0 };double &setValues (int i) { double &ref = vals[i]; return ref; } int main () { std::cout << "改变前的值" << std::endl; for (int i = 0 ; i < 5 ; i++) { std::cout << "vals[" << i << "] = " ; std::cout << vals[i] << std::endl; } setValues (1 ) = 20.23 ; setValues (3 ) = 70.8 ; std::cout << "改变后的值" << std::endl; for (int i = 0 ; i < 5 ; i++) { std::cout << "vals[" << i << "] = " ; std::cout << vals[i] << std::endl; } return 0 ; }
执行结果:
当返回一个引用时,要注意被引用的对象不能超出作用域
所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用:
1 2 3 4 5 6 int &func () { int q; static int x; return x; }