C字符串与常用函数
# 字符串
# 介绍
字符串(character string)是一个或多个字符的序列,例如:"hello,world!",C语言没有专门用于存储字符串的变量类型,字符串都被存储在char类型的数组中,每个单元存储一个字符。 字符串的末尾还会自动添加一个空字符'\0'作为结束标识,这意味着数组大小至少要比待存储字符串中的字符数多1。
- 定义字符串:
char text[14] = "Hello, world!";
- 输入字符串:
scanf("%s", 字符串变量);
由于数组变量的值是数组本身的指针,所以直接指定字符串变量名即可。
# 字符串声明
// 声明字符串
char msg1[] = "It's My GO!!!!!";
// 指针方式声明字符串,该方式无法通过索引针对字符进行修改,所以一般会加上常量限定符
const char * msg3 = "It's My GO!!!!!";
// 声明字符串数组
char msg[2][10] = {"It's", "MyGO!!!!!"};
// 指针方式声明字符串数组,可以针对元素进行修改,但无法针对字符进行修改,所以一般会加上常量限定符
const char * msg2[] = { "It's", "MyGO!!!!!" };
2
3
4
5
6
7
8
9
10
11
数组声明和指针声明的区别:
- 数组声明可以通过
arr[n]='x';
修改字符值,而指针声明不行,但指针声明也可以通过[]进行读取arr[n]
。- 指针声明可以通过
*(arr++)
进行递增操作,而数组声明不行,但数组声明也可以进行加法*(arr + n)
。
# printf函数
printf(格式字符串, 格式值1, 格式值2...);
,格式化输出字符串,会将字符串中的格式字符按顺序替换传入的格式值参数。
例如:printf("my age is: %d\n", 18);
# scanf函数
scanf("格式化字符串", 指针1,...);
,阻塞程序并接收输入的内容,直到回车。scanf函数在读取值到基本类型变量时需要使用&,数组等非基本类型则不需要。scanf函数正常会返回读取的数量,如果没有读取任何项、或需要读取一个数字而用户却输入一个非数值字符串,scanf便返回0。
这是个不安全的函数,如果需要使用则要在源代码最顶部添加
#define _CRT_SECURE_NO_WARNINGS
来禁用警告即可。 scanf函数会使用空白(换行符、制表符和空格)把输入分成多个字段,我们可以指定多个变量来接收输入的多个字段。比较特殊的是%c,它会读取每个字符包括空格。 另外还需要注意在读取字符串时如果字符串用空格隔开,则会出现单个变量只读取到了第一段的情况,也就是说输入字符串hello world只会读取hello。所以一般我们会使用其他更常用的函数来读取字符串输入。
例如:
// 会将接收的输入内容进行格式化,并将第一段并作为值,传入到myAge变量中去。
int myAge;
scanf("%d", &myAge);
// 可以一次传入多个值到不同变量中,如果格式化字符串的参数使用任意空白字符隔开,则我们输入时也可以使用任意空白字符隔开,空白字符可以是单个空格、多个空格、或则直接不隔开,"%d %d" = "%d %d" = "%d%d"。
int myPhone, myScore;
scanf("%d %d", &myPhone, &myScore);
// 如果格式化字符串的参数使用非空格字符隔开,则我们输入时需要严格按照格式进行输入,例如此处值之间必须一个,隔开。
scanf("%d,%d", &myPhone, &myScore);
// 传入字符串
char myName[40];
scanf("%s", myName);
2
3
4
5
6
7
8
9
10
11
12
13
其他用法:
- scan函数可以限制输入的字符长度,在格式化字符串中添加限定的长度即可
%[限定的长度]类型
,例如:%20d
- scan函数还可以跳过特定输入项,在格式化字符串时把*加入到要跳过的%和待转格式之间即可,例如:
int n;
// 此时如果输入1 2 3则会跳过1和2,只将3赋值给n变量
scanf("%*d %*d %d", &n);
2
3
# 转义符号
\a
警报(ANSI C)
\b
退格
\f
换页
\n
换行
\r
回车
\t
水平制表符
\v
垂直制表符
\\
反斜杠(\)
\'
单引号
\"
双引号
\?
问号
\0oo
八进制值(oo必须是有效的八进制数,即每个o可表示0~7中的一个数)
\xhh
十六进制值(hh必须是有效的十六进制数,即每个h可表示0~f中的一个数)
# 不同类型输出格式符号
- 浮点类型:
%.nf
,其中%.nf表述输出小数点后n位,例如%.2f
- 指数计数法打印浮点类型:
%e
、%E
- P计数法打印浮点类型:
%a
、%A
- 指数计数法打印long double类型:
%Lf
、&Le
- int类型:
%d
- short类型:
%hd
- long类型:
%ld
- long long类型:
%lld
- unsigned int类型:
%u
- unsigned short类型:
%hu
- unsigned long类型:
%lu
- unsigned long long类型:
%llu
- 输出指针:
%p
- 输出指针差值:
%td
- 八进制输出int类型:
%o
,要加前缀0则使用%#o
- 十六进制输出int类型:
%x
,要加前缀0x或0X则使用%#x
、%#X
- 八进制输出long类型:
%lo
- 十六进制输出long类型:
%lx
- 八进制输出short类型:
%ho
- 十六进制输出short类型:
%hx
- 输出字符串:
%s
- 输出百分号:
%%
- 格式输出字符串:
%[指定0会补零,不指定补空格][对齐长度]类型
- 对齐长度:如果为正数则是右对齐,负数则是左对齐,*表示由printf的参数决定而非字符串固定,例如:
printf("%24d", num)
、printf("%-24d", num)
、printf("%*d", 24, num)
、printf("%*.*f", 24, f_var, 2);
- 对齐长度:如果为正数则是右对齐,负数则是左对齐,*表示由printf的参数决定而非字符串固定,例如:
# 使用字符类型
字符类型用于存储单个字符串,在赋值时可以直接赋值单个字符、也可以指定字符代码,赋值字符时需要使用引号包裹。 例如:
// 会将字符转化为对应的字符代码进行存储
char ima = 'A';
// 直接指定字符代码,可读性不好,不建议使用
char ima_num = 65;
// 打印字符类型,输出结果为:A A 65
printf("%c %c %d", ima, ima_num, ima);
2
3
4
5
6
# 字符串浅拷贝
直接通过arr2 = arr1;
方式进行的拷贝就是浅拷贝,浅拷贝会将arr1值的数组首指针地址,复制给arr2作为值,也就是两者指向同一个数组的首指针。
例如:
#include <stdio.h>
int main(void){
const char * mesg = "Don't be a fool!";
const char * copy;
copy = mesg;
printf("%s\n", copy);
printf("mesg = %s; &mesg = %p; value = %p\n", mesg, &mesg,
mesg);
printf("copy = %s; © = %p; value = %p\n", copy, ©,
copy);
return 0;
}
// 输出
>> Don't be a fool!
>> mesg = Don't be a fool!; &mesg = 0x0012ff48; value = 0x0040a000
>> copy = Don't be a fool!; © = 0x0012ff44; value = 0x0040a000
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 字符串深拷贝
# strcpy函数
深拷贝需要使用strcpy函数,可以把整个字符串从拷贝至目标数组。或者使用strncpy函数指定仅拷贝前n个字符数。
第1个参数需要指向一个数据对象,如数组。
第2个指针可以是指针、数组名或字符串常量。
这是个不安全的函数,如果需要使用则要在源代码最顶部添加
#define _CRT_SECURE_NO_WARNINGS
来禁用警告即可。或使用strcpy_s函数代替。
库:string.h
例如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main(void) {
char mesg[10] = "hello";
char copy[10];
strcpy(copy, mesg);
printf("%s\n", copy);
printf("mesg = %s; &mesg = %p; value = %p\n", mesg, &mesg,
mesg);
printf("copy = %s; © = %p; value = %p\n", copy, ©,
copy);
return 0;
}
// 输出,可以看到两者值存储到了不同的内存空间
>> hello
>> mesg = hello; &mesg = 0000001C57CFF8B8; value = 0000001C57CFF8B8
>> copy = hello; © = 0000001C57CFF8E8; value = 0000001C57CFF8E8
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# strcpy_s函数
与strcpy不同的是,该函数可以指定目标字符串的最大长度,以防止缓冲区溢出。
库:string.h
函数:strcpy_s(目标字符串, 最大长度, 拷贝字符串);
# 常用函数
# getchar函数
getchar函数用于更简洁的方式接收标准输入的字符,包括空白字符。
库:stdio.h
函数:getchar()
- 返回值是接收的字符。
例如:
char ch;
// 同等于scanf("%c", &ch);
ch = getchar();
printf("%c", ch);
2
3
4
# putchar函数
getchar函数用于更简洁的方式打印输出字符。
库:stdio.h
函数:putchar()
例如:
char ch = 'A';
// 同等于printf("%c", ch);
putchar(ch);
2
3
# puts函数
puts函数与printf函数类似,但只能用来输出字符串,且在输出时会在末尾自动加上换行符。
库:stdio.h
例如:
char oneMsg[] = "Who are they?"
char * twoMsg = "It's My GO!!!!!";
puts(oneMsg);
puts(twoMsg);
2
3
4
# gets函数
gets函数用于读取整行输入,直至遇到换行符,然后丢弃换行符存储其余字符。
更建议使用fgets函数而非gets函数。因为该函数无法确保字符串能够被数组装下,所以输入内容如果超过数组能够存储的长度,就会导致缓冲区溢出,是个不安全的函数。
gets是不安全的输入函数,所以一般使用fgets或gets_s函数作为代替。
库:stdio.h
例如:
char testArr[10];
gets(testArr);
puts(testArr);
2
3
# fgets函数
fgets函数用于从屏幕或文件读入字符串,该函数会通过限制字符数来解决溢出的问题。与gets不同fgets函数不会丢弃换行符。
fgets函数的第1个参数指明用于输入的字符串数组。
fgets函数的第2个参数指明了读入字符的最大数。如果该参数的值是n,那么fgets将读入n-1个字符,或者读到遇到的第一个换行符为止。
fgets函数的第3个参数指明要读入的文件。如果需要读入从键盘输入的数据,则需要以stdin作为参数。
fgets()函数正常会返回指向char的指针,与传入的第1个参数相同。但如果函数读到文件结
尾,它将返回一个特殊的指针:空指针。该指针保证不会指向有效的数据,所以可用于标识这种特殊情况。在代码中表示空指针可以使用数字0表示,但一般使用宏NULL表示更常见。
库:stdio.h
# fputs函数
fputs函数用于输出字符串到屏幕或文件。与puts不同fputs函数不会在末尾进行换行。
fputs函数的第1个参数指明它要输出的字符串数组。
fputs函数的第2个参数指明它要写入的文件。如果要输出到计算机屏幕上,则需要以stdout作为参数。
库:stdio.h
# gets_s函数
C11新增了gets_s函数,该函数与fgets函数类似,用一个参数限制读入的字符数。但gets_s函数只从标准输入中读取数据,所以不需要第3个参数。另外gets_s与gets函数一样会丢弃末尾的换行符。
如果gets_s读到最大字符数都没有读到换行符,则会把目标数组中的首字符设置为空字符,然后返回空指针。所以一般还是使用fgets更灵活。
库:stdio.h
# strlen函数
strlen函数用于获取字符串的实际长度。
库:string.h
函数:strlen(字符串变量)
- 获取数组内字符串的长度,空字符不会算在内。
例如:
char name[10] = "hello";
// 返回5,表示实际字符为5个
printf("%d", strlen(name));
2
3
# strcat函数
strcat函数接受两个字符串作为参数,用于拼接字符串。该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后的新字符串作为第1个字符串,并返回第1个字符串的地址。
strcat函数无法检查第1个数组是否能容纳第2个字符串,所以可能会出现缓冲区溢出的情况。
这是个不安全的函数,如果需要使用则要在源代码最顶部添加
#define _CRT_SECURE_NO_WARNINGS
来禁用警告即可。或使用strcat_s函数代替。
库:string.h
例如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main(void) {
char content[10] = "abc";
strcat(content, "defg");
puts(content);
return 0;
}
// 输出
>> abcdefg
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# strcat_s函数
与strcat不同的是,该函数可以指定目标字符串的最大长度,以防止缓冲区溢出。
库:string.h
函数:strcat_s(目标字符串, 最大长度, 拼接字符串);
例如:
#include <stdio.h>
#include <string.h>
#define SIZE 10
int main(void) {
char content[SIZE] = "abc";
strcat_s(content, SIZE, "defg");
puts(content);
return 0;
}
2
3
4
5
6
7
8
9
10
11
# strncat函数
strncat函数与strcat不同的地方在于,strncat的第3个参数可以指定最大添加字符数。
这是个不安全的函数,如果需要使用则要在源代码最顶部添加
#define _CRT_SECURE_NO_WARNINGS
来禁用警告即可。或使用strncat_s函数代替。
库:string.h
例如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main(void) {
char content[10] = "abc";
strncat(content, "defg", 2);
puts(content);
return 0;
}
// 输出
>> abcde
2
3
4
5
6
7
8
9
10
11
12
13
14
# strncat_s函数
与strncat不同的是,该函数可以指定目标字符串的最大长度,以防止缓冲区溢出。
库:string.h
函数:strncat_s(目标字符串, 最大长度, 拼接字符串, 拼接长度);
例如:
#include <stdio.h>
#include <string.h>
#define SIZE 10
int main(void) {
char content[SIZE] = "abc";
strncat_s(content, SIZE, "defg", 3);
puts(content);
return 0;
}
2
3
4
5
6
7
8
9
10
11
# strcmp函数
strcmp函数用于比较的是字符串的内容。如果两个字符串参数的内容相同,该函数返回0,否则返回非零值。
库:string.h
例如:
#include <stdio.h>
#include <string.h>
int main(void) {
char a[10] = "abc";
char b[5] = "cde";
char c[5] = "abc";
printf("%d\n", strcmp(a, b)); // 返回-1
printf("%d\n", strcmp(a, c)); // 返回0
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
# strncmp函数
与strcmp不同的是strncmp函数可以指定仅对比前n位字符串。
库:string.h
例如:
#include <stdio.h>
#include <string.h>
int main(void) {
char a[5] = "abc";
char b[5] = "abe";
printf("%d\n", strncmp(a, b, 2)); // 返回0
return 0;
}
2
3
4
5
6
7
8
9
10
11
# 自定义输出函数
我们可以通过循环+指针来判断字符串是否输出完毕。
例如:
#include <stdio.h>
int put1(const char* string) {
int count = 0;
// 如果*string指向'\0'空字符则表示输出完毕,空字符会被作为条件假。
while (*string) {
putchar(*string++);
count++;
}
return count;
}
int main(void) {
printf(" %d", put1("tet"));
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 字符判断函数
C提供一系列函数来判断字符是否属于某特别的类别。属于则返回真,否则返回假。
库:ctype.h
函数:
// 判断字符是否为字母
isalpha()
// 判断字符是否为字母/数字
isalnum()
// 判断是否为标准空白字符(空格、水平制表符或换行符)或任何其他本地化指定为空白的字符
isblank()
// 判断是否为控制字符,如Ctrl+B
iscntrl()
// 判断是否为数字
isdigit()
// 判断是否为除空格之外的任意可打印字符
isgraph()
// 判断是否为小写字母
islower()
// 判断是否为可打印字符
isprint()
// 判断是否为标点符号(除空格或字母数字字符以外的任何可打印字符)
ispunct()
// 判断是否为空白字符(空格、换行符、换页符、回车符、垂直制表符、水平制表符或其他本地化定义的字符)
isspace()
// 判断是否为大写字母
isupper()
// 判断是否为十六进制数字符
isxdigit()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 大小写转换函数
大小写转换函数用于将大写字符转化为小写、或将小写转化为大写。
库:ctype.h
函数:
// 如果参数是大写字符,该函数返回小写字符;否则,返回原始参数
tolower()
// 如果参数是小写字符,该函数返回大写字符;否则,返回原始参数
toupper()
2
3
4
# 字符串转换函数
库:stdlib.h
// 将字符串转化为int整数并返回
atoi(字符串)
// 将字符串转化为double浮点数并返回
atof(字符串)
// 将字符串转化为long长整数并存储到指定对象
strtol(字符串, &变量名, 字符串中的进制数)
// 将字符串转化为unsigned long无符号长整数并存储到指定对象
strtoul(字符串, &变量名, 字符串中的进制数)
// 将字符串转化为double浮点数并存储到指定对象
strtod(字符串, &变量名, 字符串中的进制数)
2
3
4
5
6
7
8
9
10
11
12
13
14