字符数组
字符数组介绍
数组的元素如果是字符类型,这种数组就是字符数组,字符数组可以是⼀维数组,可以是⼆维数组(多维数组)。
接下来主要讨论⼀维的字符数组。
char arr1[5]; //⼀维数组
char arr2[3][5];//⼆维数组
C语⾔中使⽤双引号括起来⼀串字符表⽰字符串,这种⽅式虽然在C++中也是⽀持的,但是⼀般我们会将这种字符串称为C语⾔⻛格的字符串。如果需要将⼀个C语⾔⻛格的字符串存储起来,就可以是字符数组。
字符数组的初始化
char a[10]; //字符数组的创建
字符数组的创建同⼀维数组的创建就不再赘述,但是字符串数的初始化有2种⽅式,如下:
//⽅式1
char ch1[10] = "abcdef";
char ch2[] = "abcdef";//如果数组初始化的时候,数组的⼤⼩可以省略不写,数组⼤⼩会根据初始化内容来确定 //⽅式2
char ch3[10] = {'a', 'b', 'c', 'd', 'e', 'f'};
char ch4[] = {'a', 'b', 'c', 'd', 'e', 'f'};
如果调试看⼀下ch2 和 ch4 数组的内容,我们会明显的发现,数组 ch2 中多⼀个 ‘\0’ 字符,这是因为字符串的末尾其实隐藏⼀个 ‘\0’ 字符,这个 ‘\0’ 是字符串的结束标志,在打印字符串的时候遇到 ‘\0’ ,打印结束。
当我们把字符串存放在⼀个字符数组中的时候,这时候也可以把字符数组当做字符串看到。
字符串⻓度-strlen
字符数组中存放的着字符串,这个字符数组有⾃⼰的⻓度,也就是数组的元素个数,这个可以使⽤sizeof 计算,数组中存放的字符串的⻓度,C/C++中有⼀个库函数叫: strlen ,可以求字符串的⻓度,其实统计的就是字符串中 \0 之前的字符个数。 strlen 需要的头⽂件是 <cstring>
。
size_t strlen ( const char * str );
//str - 指针,存放的是字符串的起始地址,从这个地址开始计算字符串的⻓度
#include <iostream>
#include <cstring>
using namespace std; int main()
{ char arr[20] = "abcdef"; cout << "数组的⻓度:" << sizeof(arr)/sizeof(arr[0]) << endl; cout << "字符串的⻓度:" << strlen(arr) << endl; return 0;
}
字符数组的输⼊
输⼊没有空格字符串
使⽤scanf函数和字符数组来实现:
#include <cstdio> int main()
{ char arr[20] = { 0 }; //输⼊ scanf("%s", arr); //输出 printf("%s", arr); return 0;
}
使⽤cin和字符数组来实现:
#include <iostream>
using namespace std; int main()
{ char arr[20] = { 0 }; //输⼊ cin >> arr; //输出 cout << arr << endl; return 0;
}
上⾯两个代码都是将字符串读取后从数组的起始位置开始存放的,当然也可以指定位置存放,⽐如从数组的第⼆个元素的位置开始存放,如下代码:
#include <iostream>
using namespace std; int main()
{ char arr[20] = { 0 }; //输⼊ cin >> arr + 1;//arr表⽰数组的起始位置,+1意思是跳过⼀个元素,就是第⼆个元素的位置 //可以通过调试观察⼀下arr的内容 cout << arr + 1; return 0;
}
那么从第n个元素开始存放,就应该是 cin >> arr + n; 使⽤ scanf 函数也是⼀样的。
#include <cstdio> int main()
{ char arr[20] = { 0 }; //输⼊ scanf("%s", arr+2);//从arr+2的位置开始存放 //输出 printf("%s", arr+2);//从arr+2的位置开始打印 return 0;
}
输⼊有空格的字符串
scanf的⽅式
#include <cstdio> int main()
{ char arr[20] = {0}; //输⼊ scanf("%s", arr); //输出 printf("%s", arr); return 0;
}
当输⼊"abc def"的时候,实际上scanf只读取了abc就结束了,也就是相当于遇到空格就结束了。
这⾥特别说⼀下占位符%s ,它其实不能简单地等同于字符串。
它的规则是,从当前第⼀个⾮空⽩字符开始读起,直到遇到空⽩字符(即空格、换⾏符、制表符等)为⽌。
因为 %s 的读取不会包含空⽩字符,所以⽆法⽤来读取多个单词,除⾮多个 %s ⼀起使⽤。这也意味着, scanf() 不适合读取可能包含空格的字符串,⽐如书名或歌曲名。另外有⼀个细节注意⼀下, scanf() 遇到 %s 占位符,会在字符串变量末尾存储⼀个 \0 字符。
同时 scanf() 将字符串读⼊字符数组时,不会检测字符串是否超过了数组⻓度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防⽌这种情况,使⽤ %s 占位符时,可以指定读⼊字符串的最⻓⻓度,即写成 %[m]s
,其中的 [m]
是⼀个整数,表⽰读取字符串的最⼤⻓度,后⾯的字符将被丢弃。
#include <cstdio>
int main()
{ char name[11]; scanf("%10s", name); return 0;
}
上⾯⽰例中, name 是⼀个⻓度为11的字符数组, scanf() 的占位符 %10s 表⽰最多读取⽤⼾输⼊的10个字符,后⾯的字符将被丢弃,这样就不会有数组溢出的⻛险了。
cin的⽅式
#include <iostream>
using namespace std; int main()
{ char arr[20] = { 0 };//输⼊ cin >> arr; //输出 cout << arr << endl; return 0;
}
其实cin在读取⼀个字符串的时候,在遇到空⽩字符的时候,就认为字符串结束了,不再继续往后读取剩余的字符,同时将已经读取到的字符串末尾加上\0,直接存储起来。
解决问题
gets和fgets
使⽤ gets 函数的⽅式,这种⽅式能解决问题,但是因为 gets 存在安全性问题,在C++11中取消了gets ,给出了更加安全的⽅案: fgets 。
char * gets ( char * str );
char * fgets ( char * str, int num, FILE * stream );
gets 是从第⼀个字符开始读取,⼀直读取到 \n 停⽌,但是不会读取 \n ,也就是读取到的内容中没有包含 \n ,但是会在读取到的内容后⾃动加上 \0 。
fgets 也是从第⼀个字符开始读取,最多读取 num-1 个字符,最后⼀个位置留给 \0 ,如果num 的⻓度是远⼤于输⼊的字符串⻓度,就会⼀直读取到 \n 停⽌,并且会读取 \n ,将 \n 作为读取到内容的⼀部分,同时在读取到的内容后⾃动加上 \0 。
#include <cstdio>
//⽅案1
int main()
{ char arr[10] = {0}; gets(arr); printf("%s\n", arr); return 0;
} //替代⽅案-⽅法2
#include <cstdio>
int main()
{ char arr[10] = {0}; fgets(arr, sizeof(arr), stdin);printf("%s\n", arr); return 0;
}
上述两个程序,同样在运⾏起来后,在控制台窗⼝中输⼊: abc def ,按回⻋,⽅案1和⽅案2中arr 数组的内容中差异如下:
有DevC++中使⽤gets函数,确实没有报错,但是在其他的IDE上,⽐如:VS2022上直接报错,不允许使⽤gets函数。
所以在代码中还是慎⽤gets函数。
scanf
当然C语⾔中使⽤ scanf 函数其实也能做到读取带有空格的字符串,只是不常⻅⽽已。⽅式就是将 “%s” 改成 "%[^\n]s
" ,其中在 %
和 s
之间加上了 [^\n]
,意思是⼀直读取,直到遇到\n ,这样即使遇到空格也就不会结束了。
这种⽅式读取,不会将 \n 读取进来,但是在读取到的字符串末尾加上 \0 。
#include <cstdio>
int main()
{ char arr[10] = "xxxxxxxx"; scanf("%[^\n]s", arr); printf("%s\n", arr); return 0;
}
getchar
使⽤ getchar 逐个字符的读取,也是可以读取⼀个字符串的。
#include <cstdio> int main()
{ char arr[10] = { 0 }; int ch = 0; int i = 0; while ((ch = getchar()) != '\n') { arr[i++] = ch; }printf("%s\n", arr); return 0;
}
字符数组的输出
- C语⾔中可以在 printf 函数中使⽤ %s 占位符的⽅式,打印字符数组中的字符串。
- C++中使⽤ cout ,可以直接打印字符数组中的字符串内容。
- 也可以采⽤循环的⽅式逐个字符打印字符串的内容。
//⽅法1
#include <iostream>
#include <cstdio>
using namespace std; int main()
{ char a[] = "hello world"; cout << a << endl; printf("%s\n", a); return 0;
} //⽅法2
//单个字符的打印,直到\0字符,\0不打印
#include <iostream>
using namespace std; int main()
{ char a[] = "hello world"; int i = 0; while (a[i] != '\0') { cout << a[i]; i++; } cout << endl; return 0;
} //⽅法3
//单个字符打印,根据字符串⻓度来逐个打印
//strlen可以求出字符串的⻓度,不包含\0
#include <iostream>
#include <cstring>
using namespace std; int main()
{ char a[] = "hello world"; int i = 0; for (i = 0; i < strlen(a); i++) { cout << a[i]; } cout << endl; return 0;
}
strcpy和strcat
strcpy
使⽤字符数组可以存放字符串,但是字符数组能否直接赋值
char arr1[] = "abcdef";
char arr2[20] = {0};
arr2 = arr1;//这样这节赋值可以吗?
就整型数组中,我们说的过⼀样,这⾥也是不⾏的。
将arr1中的字符串,拷⻉到arr2中,其实C/C++中有⼀个库函数叫strcpy,可以完成。
char * strcpy ( char * destination, const char * source );
//destination - 是⽬标空间的地址
//source - 是源头空间的地址
//需要的头⽂件 <cstring>
#include <cstdio>
#include <cstring>int main()
{ char arr1[] = "abcdef"; char arr2[20] = {0}; strcpy(arr2, arr1); printf("%s\n", arr2); return 0;
}
strcat
有时候我们需要在⼀个字符的末尾再追加⼀个字符串,那字符数组能直接追加吗?⽐如:
char arr1[20] = "hello ";
char arr2[] = "world";
arr1 += arr2;//这样也是不⾏的
C/C++中有⼀个库函数叫strcat,可以完成。
char * strcat ( char * destination, const char * source );
//destination - 是⽬标空间的地址
//source - 是源头空间的地址
//需要的头⽂件 <cstring>
#include <cstdio>
#include <cstring>int main()
{ char arr1[20] = "hello "; char arr2[] = "world"; strcat(arr1, arr2); printf("%s\n", arr1); return 0;
}