● 基本概念
数组:数组是一组在内存中依次连续存放的(数组所有元素在内存中的地址是连续的)、具有同一类型的数据变量所组成的集合体。其中的每个变量称为数组元素,它们属于同一种数据类型,数组元素用数组名与带方括号的数组下标一起标识。数组可以是一维的,也可以是多维的。
数组的定义: 数据类型 数组名[常量表达式1][常量表达式2]; data_type array_name[constant_express_1][constant_expression_2] 数组名的两种含义:
当数组名是一个右值时, 它是一个指针常量; 当数组名在等号左边时, 它代表一个数组所占据的那块内存, 但数组名不可以被整体赋值, 即它不能作为左值, 只有数组的元素才能被分别赋值. 其实, 普通的数据类型也是如此, 如: int i = 6; a=i; i作为右值时, 它代表一个值, i作为右值时, 它代表i占据的内存.
左值和右值的三种解释: 第一种: ●左值: 可以放在赋值符号左边的变量,左值表示可由用户访问的内存单元,用户可以改变存储在其中的值, 左值相当于地址值. 右值:当一个符号或者常量放在操作符右边的时候,计算机就读取他们的"右值",也就是其代表的真实值,右值相当于数据值. 判断一个量是左值还是右值要根据该量赋值之后其本身还有无空间存储来判断。比如一个变量a,它可以作为左值和右值; 再比如一个const int a,它的值是只读的,就不能给它赋值,但并不代表它是个右值,右值是常量还是变量要看变量定义的类型。
第二种: 简单赋值运算符的使用格式: 左表达式 = 右表达式 其功能是将右表达式(右操作数)的值放到左表达式表示的内存单元中,因此左表达式一般是变量或表示某个地址的表达式,称为左值,在运算中作为地址使用。右表达式在赋值运算中是取其值使用,称为右值。所有赋值运算左表达式都要求是左值。
第三种: 简单来说: 左值就是那些能够出现在赋值符号左边的表达式。 右值就是那些可以出现赋值符号右边的表达式。 例如: a就是一个左值,因为它标识了一个可以存储结果值的地点,b + 25是个右值,因为它指定了一个值。但是它们可以互换吗? b + 25 = a; 原先用作左值的a此时也可以当作右值,因为每个位置都包含一个值。然而,b + 25不能作为左值,因为它并未标识一个特定的位置。因此,这条赋值语句是非法的。
L-value中的L指的是Location,表示可寻址-- a value that has an address. R-value中的R指的是Read,表示可读-- a value that does not have an address in a computer language. |
● 数组是一种类型
#include <iostream> using namespace std;
main() { int a[2][3]={1,2,3,4,5,6}; //声明并初始化二维数组 printf("%p\n",a); printf("%p\n",&a); printf("%p\n",&a[0]); int **pp; } 可见a, &a, &a[0]都代表地址, 并且值相同, 但是它们的类型不同:△ pp=a; //错误,因为pp是一个int**型变量,a是一个int[2][3]型的地址 pp=&a; //错误,pp是一个int**型变量,&a是一个(*)int[2][3]型的地址 pp=&a[0]; //错误,pp是一个int**型变量,&a[0]是一个(*)int[3]型的地址 |
#include <iostream> using namespace std;
main() { int a[2][3]={1,2,3,4,5,6}; //声明并初始化二维数组 printf("%p\n", a[0]+0); printf("%p\n", a[0]+1); printf("%p\n", a[0]+2); printf("%p\n", a[1]+0); printf("%p\n", a[1]+1); printf("%p\n", a[1]+2); printf("________________\n"); int *p_a[3]; //声明整型指针数组 p_a[0]=a[0]; //初始化指针数组元素, 在二维数组中, a[0]相当于a, 即&a[0][0] p_a[1]=a[1]; //a[1]即a[1]+0, 表第1行第0个元素的地址 p_a[1]=a[2]; //a[2]即a[2]+0, 表第2行第0个元素的地址 printf("%p\n", p_a[0]); printf("%p\n", p_a[1]); printf("%p\n", p_a[1]); printf("________________\n"); int **pp; pp=p_a; printf("%p\n", &p_a[0]); printf("%p\n", p_a); //p_a是一个数组(指针数组)名, 因此, 它的值是其第一个元素(存放着指针), 即p_a[0]的地址 } |
● 一维数组的声明 /引用 / 初始化, 语法
数据类型 数组名 [常量表达式] ={初值1, 初值2,…, 初值n};
例如,定义了一个数组int a[5]; 其内存排列(分配)示意图如下: |
#include <stdio.h>
int main() { int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int b[] = { 1, 2, 3, 4, 5};//定义一个数组,初始化成员, 编译器能自动获得数组元素的个数 int c[10] = {1, 2, 3, 4, 5};//后面的值初始化为0 int *p=c; //x //int d[10] = { 0 };//将数组中所有的元素初始化为0 //a = 100;//这个语句不合法,因为数组的名字是一个常量,而不是一个变量(数组的名字表示数组首元素的地址) //a[3]//这才是一个变量 //&a[0];//取数组首元素的地址 //"%p" printf("%p\n", &a[0]); //相当于printf("%x\n", &a[0]);只是如果输出的结果如果左边有0, 会被删除 printf("%p\n", a); printf("%d\n", b[2]); printf("%d\n", sizeof(b)); printf("%d\n", sizeof(p)); return 0; } |
sizeof(b)的值为20, 数组b的指针p的大小, 即sizeof(p)为4, 说明数组名并不完全等同于指针, 只是它的值等于地址值 |
● 枚举类型的数组
注意下面这个枚举类型的数组, |
weekday b[10]; //定义了一个10个元素的枚举数组b,weekday为已定义的枚举类型, 数组b的元素的值只能为枚举常量的值. |
● 二维数组的声明 /引用 / 初始化
※ 二维数组是按行存放的, 例如数组int a[3][4]; 其长度是3*4=12: 行下标表达式与列下标表达式的值同样从0开始,a[i][j]表示数组的第i+1行、第j+1列的元素。由于数组元素是变量,可以对其进行各种各种操作。 数组元素如果定义数组a[m][n], 即数组第1维大小为n, 第2维大小为m。a[i][j]的排列位置与在内存中的地址计算公式如下: a[i][j]的排列位置=第1维大小n*i+j+1; a[i][j]的地址=a的起始地址+(第1维大小n*i+j)*sizeof(数据类型) 例如: a, a[0]: 为数组a的起始地址, 即a[0][0]的地址; a[i]+j: 为数组的第i+1行的第j+1元素的地址,即a[i][j]的地址; |
#include <iostream.h>
int main() { int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; //使用聚合方式赋值 /* 相当于如下的按元素赋值 a[0][0]=1 ;a[0][1]=2;a[0][2]=3;a[0][3]=4; a[1][0]=5;a[1][1]=6;a[1][2]=7;a[1][3]=8; a[2][0]=9;a[2][1]=1 0;a[2][2]=11 ;a[2][3]=12;*/ int b[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; //按行进行赋值 int c[3][4]={1,2,3,4}; //二维数组可以只对前几个元素赋值, 其余值为0 cout<<b[1][2]<<endl; b[1][2]=a[2][1]/2; //数组元素是左值, 可以对其赋值 cout<<b[1][2]<<endl; return 0; } |
● 多维数组
多维数组在内存的排列方式同样是先排低维数组,由低向高依次排列。 |
如:b[2][3][4]的排列顺序为: |
如:下列都是b[2][3][4]的地址表达式。 b[1]; //b[1][0][0]的地址; b[2]; //错误,下标越界; b[0]+1; //与b[0][1]相同,b[0][1][0]的地址。 b[1][2]; //b[1][2][0]的地址 b[1][2]+4; //b[1][2][4]的地址,但数组b中没有b[1][2][4]这个元素,故指向了其它地方。 |
● 数组与函数参数
当数组作函数参数时, 只传递数组的地址(指向该数组的第一个元素的指针), 而不是将整个数组赋值到函数中. 数组名是一个地址,不能当作一个左值,但是可以作为函数的形参,接受实参传送来的地址。 当形参接受实参传送来的地址后,形参数组与实参共享内存中的一块空间。函数体通过形参对数组内容的改变会直接作用到实参上。 注意: (1)使用数组名传递地址时,虽然传递的是地址,但形参与实参的地址 (数组)类型应一致。 (2)形式参数中数组元素个数没有给定,因此,在函数体中,对数组存取的下标可以为任意值而不会出现编译错误。但是,当这个下标超过了实参数组的元素个数范围时,存取的就不是实参数组中的内容了。 |
数组元素作为函数参数 |
#include<stdio.h>
void ShowMember(int iMember); /*声明函数*/
int main() { int count[10]; /*定义一个整型的数组*/ int i; /*定义整型变量,用于循环*/ for(i=0;i<10;i++) /*进行赋值循环*/ { count[i]=i; } for(i=0;i<10;i++) /*循环操作*/ { ShowMember(count[i]); /*执行输出函数操作*/ } return 0; }
void ShowMember(int member) /*函数定义*/ { printf("The member is %d\n", member); /*输出数据*/ }
|
数组名作为函数参数 |
#include <stdio.h> #include <stdlib.h>
void test(int Array[], int len) //形参, 数组名[], 也可以这样:void test(int *Array , int len) { int i; for(i=0;i<len;i++) { printf( "num[%d]=%d\n", i, i+1); } }
int main() { int MyArray[5]; test(MyArray, 5); //实参:数组名,目的是将数组的首地址传给test函数 system("pause"); return 0; } |
#include<stdio.h>
void Evaluate(int ArrayName[10]); /*声明赋值函数*/ void Display(int ArrayName[10]); /*声明显示函数*/
int main() { int Array[10]; /*定义一个具有10个元素的整型数组*/
Evaluate(Array); /*调用函数进行赋值操作,将数组名作为参数*/ Display(Array); /*调用函数进行赋值操作,将数组名作为参数*/ return 0; }
/* 进行数组元素的赋值 */
void Evaluate(int ArrayName[10]) { int i; for(i=0;i<10;i++) { ArrayName[i]=i; } }
/* 数组元素的显示 */
void Display(int ArrayName[10]) { int i; for(i=0;i<10;i++) { printf("the member is %d\n",ArrayName[i]); } } |
可变长度数组作为函数参数 |
#include<stdio.h>
void Evaluate(int ArrayName[]); /*声明函数,参数为可变长度数组*/ void Display(int ArrayName[]); /*声明函数,参数为可变长度数组*/
int main() { int Array[10]; /*定义一个具有10个元素的整型数组*/
Evaluate(Array); /*调用函数进行赋值操作,将数组名作为参数*/ Display(Array); /*调用函数进行赋值操作,将数组名作为参数*/ return 0; }
/* 进行数组元素的赋值 */ void Evaluate(int ArrayName[]) /*定义函数,参数为可变长度数组*/ { int i; /*定义整型变量*/ for(i=0;i<10;i++) /*执行循环语句*/ { /*在循环语句中执行赋值操作*/ ArrayName[i]=i; } }
/* 数组元素的显示 */ void Display(int ArrayName[]) /*定义函数,参数为可变长度数组*/ { int i; /*定义整型变量*/ for(i=0;i<10;i++) /*执行循环的语句*/ { /*在循环语句中执行输出操作*/ printf("the member is %d\n", ArrayName[i]); } }
|
使用指针作为函数参数 |
#include<stdio.h>
void Evaluate(int* p); /*声明函数,参数为可变长度数组*/ void Display(int* p); /*声明函数,参数为可变长度数组*/
int main() { int Array[10]; /*定义一个具有10个元素的整型数组*/
Evaluate(Array); /*调用函数进行赋值操作,将数组名作为参数*/ Display(Array); /*调用函数进行赋值操作,将数组名作为参数*/ return 0; }
/* 进行数组元素的赋值 */ void Evaluate(int* p) /*定义函数,参数为可变长度数组*/ { int i; /*定义整型变量*/ for(i=0;i<10;i++) /*执行循环语句*/ { /*在循环语句中执行赋值操作*/ p[i]=i; } }
/* 数组元素的显示 */ void Display(int* p) /*定义函数,参数为可变长度数组*/ { int i; /*定义整型变量*/ for(i=0;i<10;i++) /*执行循环的语句*/ { /*在循环语句中执行输出操作*/ printf("the member is %d\n",p[i]); } }
|
● 字符数组和字符串
字符数组: 每个元素是字符的数组; 字符数组也分为一维数组和多维数组。 字符串: C语言将字符串作为字符数组来处理, 以字符'\0'作为结束标志
字符数组的初始化形式: 1.用字符进行初始化 例如:char s1[ ]={'C','h','i','n','a'}; char s2[ ][4]={{'H','o','w'},{'a','r','e'},{'y','o','u'}}; 2.用字符串进行初始化 例如:char s3[ ]="China"; char s4[ ][4]={"how", "are", "you"}; |
#include <iostream.h>
int main() { char word1[20]; //声明一个字符数组; /*字符数组可对数组元素逐一赋值 word[0]='H';word[1]='e';word[2]='l'; word[3]='l';word[4]='o'; */ //也可以使用聚合方式赋值 char word2[20]={'H','e','l','l','o'}; //聚合方式只能在声明时使用,下面的方法错误: //char word[20]; //word={'H','e','l','l','o'}; //错误, 因为数组名不能作为左值 //也不能把一个字符数组赋值给另一字符数组, 不过一个字符数组的元素的值可以赋值给另一字符数组元素 //在字符数组结尾加'\0'就形成了字符串: char a[]="Hello!"; //等同于char a[]="Hello!\0", 也等同于char a[]={'H', 'e', 'l', 'l', 'o', '\0'} cout<<a<<endl; } |
● 字符数组和字符串的大小
#include <stdio.h>
int main() { char a[] = {'H','e','l','l','o'}; char b[] = "Hello"; char *c="Hello"; printf("%d\n",sizeof(a)); printf("%d\n",sizeof(b)); printf("%d\n",sizeof(c)); return 0; } |
● 字符指针的三种情况
① //字符指针指向单个字符 #include <stdio.h>
int main() { char a='G'; char *p=&a; printf("%c\n", *p); //改变字符a的值 *p='H'; printf("%c\n", a); return 0; }
|
② //字符指针指向字符数组 #include <stdio.h>
int main() { char a[]= {'H','e','l','l','o'}; char *p=a; printf("%c\n", *p); printf("%c\n", *(p+2)); //改变第0个字符 *p='G'; printf("%c\n", *p); return 0; } |
③ //字符指针指向字符串常量 #include <stdio.h>
int main() { char *p= "Hello"; //字符串常量和数组名一样,也是被编译器当成指针来对待的; 它的值就是字符串的基地址。 printf("%c\n", *p); printf("%c\n", *(p+2)); //改变第0个字符 *p='G'; printf("%c\n", *p); return 0; }
//虽然通过了编译, 但是运行时出错 |
● 常用字符与字符串处理函数
函数的用法 |
函数的用法 |
函数的用法 |
strlen(字符串) |
返回字符串的长度(不包括\0) |
Cstring |
strset(字符数组, 字符C) |
将字符数组中的所有字符都设为指定字符C, 并以\0结尾 |
|
strlwr(字符串) |
将字符串中的所有字符转换成小写字符 |
|
strupr(字符串) |
将字符串中的所有字符转换成大写字符 |
|
strcmp(串s1, 串s2) |
比较两个字符串的大小,即按从左到右的顺序逐个比较对应字符的ASCII码值。若s1大于s2,返回1;若s1小于s2,返回-1;若s1等于s2,返回0。串s1、s2可以是字符串常量 |
常用字符与字符串处理函数(续)
函数的用法 |
函数的用法 |
函数的用法 |
strcpy(串s1, 串s2) |
将字符串s2拷贝到s1所指的存储空间中,然后返回s1。 其中, 串s2可以是字符串常量 |
Cstring |
strcat(串s1, 串s2) |
返回字符串的长度(不包括\0) |
Ctype |
toupper(字符) |
将字符数组中的所有字符都设为指定字符C, 并以\0结尾 |
|
tolower(字符) |
将字符串中的所有字符转换成小写字符 |
Cstdlib |
atoi(字符串) |
将字符串中的所有字符转换成大写字符 |
|
atol(字符串) |
比较两个字符串的大小,即按从左到右的顺序逐个比较对应字符的ASCII码值。若s1大于s2,返回1;若s1小于s2,返回-1;若s1等于s2,返回0。串s1、s2可以是字符串常量 |
|
atof(字符串) |
将字符串s2拷贝到s1所指的存储空间中,然后返回s1。 其中, 串s2可以是字符串常量 |
● 字符串处理函数:①stracat; ②strcpy; ③strcmp; ④strlen
这几个函数的声明如下: char *strcat(char *dest, const char *src) //返回该函数返回一个指向最终的目标字符串 dest 的指针。 char *strcpy(char *dest, const char *src) //返回该函数返回一个指向最终的目标字符串 dest 的指针。 ※ dest 为字符指针,src 为字符常量指针(字符指针可能指向单个字符, 也可能是字符数组(包括字符串)) int strcmp(const char *str1, const char *str2) // 如果返回值 < 0,则表示 str1 小于 str2; 如果返回值 > 0,则表示str1 大于str2; 如果返回值 = 0,则表示 str1 等于 str2 size_t strlen(const char *str) //该函数返回字符串的长度, |
//方案一:两个参数都是数组 #include <string.h> #include <stdio.h>
int main(void) { char dest[30] = "Hello"; char src[] = "World"; printf("%p\n", dest); //打印dest字符串参数的起始地址
strcat(dest, src); printf("dest:[%s]\n", dest); printf("%p\n",strcat(dest, src)); //strcat()返回的是dest字符串参数的起始地址, 因此与与上面打印的dest字符串的起始地址是同一个地址 return 0; } //得到的结果是:
//方案二:两个参数都是指针 //下面的程序错误: #include <stdio.h> #include <string.h>
int main(void) { char *dest = NULL; char *src = "World";
strcat(dest, src); printf("dest:[%s]", dest);
return 0; } //到的结果是:Segmentation fault (core dumped)竟然是段错误 //因为dest没有足够的空间来存储src中的内容
//修改之后的: #include <stdio.h> #include <stdlib.h> #include <string.h>
int main(void) { char *dest = NULL; dest = (char *)malloc(1024); char *src = "World";
strcat(dest, src); printf("dest:[%s]", dest);
return 0; } //得到的结果为:dest:[World]
//方案三:第一个是数组,第二个位指针 #include <stdio.h> #include <string.h>
int main(void) { char dest[6] = "Hello"; char *src = "World";
strcat(dest, src); printf("dest:[%s]\n", dest);
return 0; } //结果:dest:[HelloWorld] //一般来说, 空间足够的时候才可以拷贝成功. //空间不够这属于数组越界的问题,在C语言中,C不检查也不提示,所以这里的拷贝用到了dest[6]后面紧挨着的几个存储单元
//方案四:第一个指针,第二个数组 #include <stdio.h> #include <string.h>
int main(void) { char *dest; char src[] = "World";
strcat(dest, src); printf("dest:[%s]\n", dest);
return 0; } //结果:Segmentation fault (core dumped)
//修改之后: #include <stdio.h> #include <stdlib.h> #include <string.h>
int main(void) { char *dest; dest = (char *)malloc(1024); char src[] = "World";
strcat(dest, src); printf("dest:[%s]\n", dest);
return 0; } 结果:dest:[World] |
strcpy |
#include <stdio.h> #include <string.h>
int main() { char str1[] = "Sample string"; char str2[40]; char str3[40]; strcpy(str2, str1); strcpy(str3, "copy successful"); printf("str1: %s\nstr2: %s\nstr3: %s\n", str1, str2, str3); return 0; } |
str1: Sample string str2: Sample string str3: copy successful |
strcmp |
#include<iostream> #include <string> using namespace std;
#include<string> void main() { char str1[30],str2[20]; int i=0; cout<<"please input string1:"<< endl; gets(str1); cout<<"please input string2:"<<endl; gets(str2); i=strcmp(str1,str2); if(i>0) cout <<"str1>str2"<<endl; else if(i<0) cout <<"str1<str2"<<endl; else cout <<"str1=str2"<<endl; } |
strncmp,字符串有限比较 |
#include <stdio.h> #include <string.h>
int main() { char str[][5] = { "R2D2", "C3PO", "R2A6" }; int n; puts("Looking for R2 astromech droids..."); // "astromech droids"的意思是宇航机器人. for (n = 0; n<3; n++) if (strncmp(str[n], "R2xx", 2) == 0) //比较二维字符数组str的第n个元素,与字符串" R2xx"的前两个字符是否相同. { printf("found %s\n", str[n]); } return 0; } |
● string类类型(string class type)的字符串
C++支持两种字符串:C风格字符串和string类类型(string class type)的字符串。string类类型位于C++标准库中(C++ Standard Library). 之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为它和前者比较起来,不必担心内存是否足够、字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(甚至是100%)的需要。我们可以用 = 进行赋值操作,== 进行比较,+ 做串联(是不是很简单?)。我们尽可以把它看成是C++的基本数据类型。此外,CString类在MFC中广泛应用,简化了字符串的处理。 |
● 读写字符串
#include <iostream> #include <string> using namespace std; void main() { string str;//或者是char str[10] cout<<"Please input string: "<<endl; cin>>str; cout<<"The string is :"<<endl; cout<<str<<endl; } |
//string类类型的输入操作符读取并忽略开头所有的空白字符, 但在输出时读取字符直至再次遇到空白字符 |
如果是char str[];会警示error C2133: 'str' : unknown size; 如果是char* str;输入字符串"Hello World!"以后程序会崩溃(APPCRASH).
但是可以像下面这样直接赋值: void main() { char* str="Hello!"; cout<<str<<endl; }
void main() { char str[]="Hello!"; cout<<str<<endl; }
void main() { char str[20]="Hello!"; cout<<str<<endl; } //如果是str[10]会提示error C2117: 'Hello World!' : array bounds overflow |
● 输入带空格的字符串
//C风格 #include <iostream> #include <string> using namespace std; void main() { char str[50]; cout<<"input a string:"<<endl; gets(str); cout<<str<<endl; } |
//C++风格 |
#include <iostream> #include <string> using namespace std;
void main() { string str; cout<<"input a string:"<<endl; getline(cin,str); cout<<str<<endl; } |
● 字符串赋值
#include <iostream> #include <string> using namespace std; void main() { string str1,str2; cout<<"Please input str1: "<<endl; cin>>str1; str2=str1; cout<<str2<<endl; } |
#include <iostream> #include <string> using namespace std; void main() { string str1,str2; str1=" Hello World!"; str2=str1; cout<<str2<<endl; } |
● 字符串
#include <iostream> #include <string> using namespace std; void main() { string str1="hello"; string str2="Hello"; string str3="Hello World"; int flag; flag=(str1==str2); if(flag==1) cout<<"str1=str2"<<endl; else { flag=(str1>str2); if (flag==1) cout<<"str1>str2"<<endl;//str1>str2 else cout<<"str1<str2"<<endl; } flag=(str2==str3); if(flag==1) cout<<"str2=str3"<<endl; else { flag=(str2>str3); if (flag==1) cout<<"str2>str3"<<endl; else cout<<"str2<str3"<<endl;//str2<str3 } } |
//任意一个大写字母都小于任意的小写字母 |
● 字符串长度
#include <iostream> #include <string> using namespace std; void main() { string str="Hello World!"; int num; num=str.size(); //str是个对象, size()是其成员函数 cout<<"the length of "<<str<<" is : "<<num<<endl; } |
● 空字符串
#include <iostream> #include <string> using namespace std; void main() { string str="Hello World!"; int num; num=str.size(); if (num==0) cout<<"The str is empty"<<endl; else cout<<"The str is not empty"<<endl; if(str.empty()) //用专门的empty操作判断 cout<<"The str is empty"<<endl; else cout<<"The str is not empty"<<endl; } |