C:字符串、字符串指针、字符串函数

一、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]地址 = 0000000000404012
 	str[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语言——字符串指针篇

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值