目录
- 一、C字符串(字符数组)
- 二、字符串常量指针(指向字符串的指针)!!!
- 三、C字符串函数
- strlen(str)
- strcat(s1, s2);
- strtok()
- strcpy(s1, s2)
- strcmp(s1, s2)
- strchr(s1, ch);
- strstr(s1, s2);
- strlwr(str); // lower
- strupr(str); // upper
一、C字符串(字符数组)
在 C 语言中,字符串实际上是使用 空字符 \0 结尾的 一维字符数组。因此,\0 是用于标记字符串的结束。
空字符(Null character
)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是 空字符。
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
char site[] = "RUNOOB";
C 编译器会在初始化数组时,自动把 \0 放在字符串的末尾。
每个字符用一个地址来存储。
————
char str[]="hello world!"
char str[20]={"hello world!"}
字符数组 只有在定义时才可一次性赋值,一旦定义完就只能一个个赋值,如:
char str[4];
str = "abc"; //错误
str[0] = 'a';
str[1] = 'b';
str[2] = 'c'; //正确
二、字符串常量指针(指向字符串的指针)!!!
常量指针:指向常量的指针
char str[] = "hello world";
char *pstr = str; // 指针保存地址char *pstr1 = "hello world";
// char str1[] = pstr1 ; // Error! 编译报错!// 或写成
char *str;
str = "hello world"; // 字符串指针可整体赋值,不可局部赋值;
那这两种写法有什么区别么?
在内存中存储区域不同,字符数组 存储在 全局数据区或栈区,字符串指针 形式字符串存储在 常量区。【全局数据区和栈区 的字符串有 读取和写入 的权限,而 常量区 字符串 只有读取权限,没有写入权限。这就导致了 字符数组 在定义后可读取和修改每个字符;而 字符串指针 一旦定义后便不可修改,可整体赋值(更改指针、而非通过指针更改值)】
#include <stdio.h>
#include <string.h>
int main() {// 字符串指针char *pstr = "www.baidu.com"; // " "包围字符串末尾自动添加 '\0'// 字符串指针形式的数据 存储在 常量区,不可更改。// 常量:不可通过指针pstr更改字符串值;但可更改指针pstr的值;// pstr[3]='P'; // 可正常编译和链,但运行会出现错误!// *pstr = 'P'; // 可正常编译和链,但运行会出现错误!pstr = "hello world!"; // 可整体赋值// pstr仅读取 *(pstr+i),不可赋值for(int i = 0; i < strlen(pstr); i++) {printf("%c\t", *(pstr + i));printf("str[i]地址 = %p\n", (pstr + i));}// 字符串数组char str[] = "www.baidu.com"; str[0] = 'Q'; printf("%s\n", str);return 0;
}
h str[i]地址 = 000000000040400E
e str[i]地址 = 000000000040400F
l str[i]地址 = 0000000000404010
l str[i]地址 = 0000000000404011
o str[i]地址 = 0000000000404012str[i]地址 = 0000000000404013
w str[i]地址 = 0000000000404014
o str[i]地址 = 0000000000404015
r str[i]地址 = 0000000000404016
l str[i]地址 = 0000000000404017
d str[i]地址 = 0000000000404018
! str[i]地址 = 0000000000404019
Qww.baidu.com
(pstr+i)、pstr[i]、(str+i) 、str[i] 均可正常使用;
#include <stdio.h>
#include <string.h>
int main() {char str[] = "hello world";char *pstr = str;*pstr = 'Q'; // 初始化时,为字符串数组,存放在全局数据区或栈区;pstr[1] = 'Q'; // 指针只是保存一下数组的地址,不影响其局部赋值操作;int len = strlen(str), i;//使用 *(pstr+i)for(i = 0; i < len; i++) {printf("%c", *(pstr + i));}printf("\n");//使用 pstr[i]for(i = 0; i < len; i++) {printf("%c", pstr[i]);}printf("\n");//使用 *(str+i)for(i = 0; i < len; i++) {printf("%c", *(str + i));}printf("\n");//使用 str[i]for(i = 0; i < len; i++) {printf("%c", str[i]);}return 0;
}
QQllo world
QQllo world
QQllo world
QQllo world
————
struct Data
{
int a,b,c;
};
struct Data * p;
struct Data A = {1,2,3};
int x;
p = &A ;
x = p->a;
// p->a == A.a
// A.a 是实例取变量
// p->a 是指针取所指对象的变量,相当于 *p.a
2.1 字符数组、字符串指针,在for循环中重复创建的地址不同;
for (int i = 0; i < n; ++i) {char res1[10000];printf("%p\n", res1);}
00000000006365C0
00000000006365C0
00000000006365C0
00000000006365C0
00000000006365C0
00000000006365C0
00000000006365C0
00000000006365C0
for (int i = 0; i < n; ++i) {char* res1 = (char*)malloc(sizeof(char)* 10000);printf("%p\n", res1);}
0000000000B965B0
0000000000B99D20
0000000000B9C460
0000000000660080
00000000006627C0
0000000000664F00
0000000000667640
0000000000669D80
三、C字符串函数
strlen(str)
strlen()函数的声明:
size_t strlen (const char * str); // size_t: 无符号(unisgned int)整型类型
strlen(s1);
返回字符串 s1 的长度(字符个数), 不包含'\0'。
strlen 的工作原理:只要给我个地址,那么strlen就可以 向后数 字符,至到遇到 ‘\0’ 就会停止。
这个地址就是字符串中,其中一个字符的地址;strlen() 接收地址后,开始 向后数 字符,至到遇到 ‘\0’ 停止。
测试用例:
#include <stdio.h>
#include <string.h>
int main()
{char str0[8] = {"44778899"}; // 没有预留'\0'的内存,strlen()会产生随机值!char str1[9] = {"44778899"}; // 预留了'\0'的内存,长度为8;char str2[] = {'a','b','c'}; // 末尾没有加字符'\0',strlen()会产生随机值!char str3[] = {'a','b','c', '\0'};printf("%s 的长度是 %d\n", str0, strlen(str0));printf("%s 的长度是 %d\n", str1, strlen(str1));printf("%s 的长度是 %d\n", str2, strlen(str2));printf("%s 的长度是 %d\n", str3, strlen(str3));return(0);
}
44778899�s 的长度是 11
44778899 的长度是 8
abc44778899 的长度是 11 // 产生随机值,因为不知道反斜杠'\0'在哪里
abc 的长度是 3
注意:C语言中,一般在用" "
包围字符串下自动末尾添加\0
, 如:char[]="abc"
;逐个赋值不会自动添加 \0
————
strcat(s1, s2);
连接字符串 s2 到字符串 s1 的末尾。
strcat()
将把 s2 连接到 s1 后面,并删除原来 s1 最后的结束标志 '\0'
。这意味着,s1 必须足够长,要能够同时容纳 s1和 s2,否则会越界(超出范围)。
strcat()
函数的声明:
char *strcat(char *dest, const char *src);
#include <stdio.h>
#include <string.h>
int main() {char arr[20] = "Cyuyan";strcat(arr, arr);printf("%s", arr);return 0;
}
CyuyanCyuyan
提问:
char *pc = "This is";strcat(pc, "string"); // 为什么会报错?为啥对字符串指针就不行,非得是字符数组;
————
strtok()
C/C++字符串函数strtok()详解
char * strtok (char *str, const char * delimiters);
参数:str:待分割的字符串(c-string);delimiters:分割符字符串
当strtok()
在参数 s 的字符串中发现参数 delimiters 中包涵的分割字符时,则会将该字符改为\0
字符。在第一次调用时,strtok()
必需给予参数s
字符串,往后的调用则将参数 s
设置成 NULL
。
每次调用成功则返回指向被分割出片段的指针。
需要注意的是,使用该函数进行字符串分割时,会破坏被分解字符串的完整,调用前和调用后的 s
已经不一样了。第一次分割之后,原字符串str
是分割完成之后的第一个字符串,剩余的字符串存储在一个静态变量中,因此多线程同时访问该静态变量时,则会出现错误。
#include <stdio.h>
#include <string.h>int main()
{char str[] = "ab,cd,ef";char *ptr;printf("str = %s\n", str);ptr = strtok(str, ","); // 指针指向第一个位置while (ptr != NULL) {printf("str= %s\n", str);printf("ptr= %s\n", ptr);ptr = strtok(NULL, ",");}return 0;
}
str = ab,cd,ef
str= ab
ptr= ab
str= ab
ptr= cd
str= ab
ptr= ef
————
strcpy(s1, s2)
复制字符串 s2 到字符串 s1。
strcpy()
会把 s2 中的字符串拷贝到 s1 中,字符串结束标志 '\0'
也一同拷贝。
————
strcmp(s1, s2)
如果 s1 和 s2 是相同的,则返回 0;
如果 s1<s2 则返回小于 0;
如果 s1>s2 则返回大于 0。
————
strchr(s1, ch);
返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
————
strstr(s1, s2);
返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置; 查找失败返回NULL
————
strlwr(str); // lower
将字符串中的大写字母转换成小写字母。
————
strupr(str); // upper
将字符串中的小写字母转换成大写字母。
参考链接:
字符串函数讲解
【C语言】字符串函数「超详细」
C语言——字符串指针篇