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

news/2024/10/20 11:32:29/

目录

    • 一、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语言——字符串指针篇


http://www.ppmy.cn/news/856638.html

相关文章

渲染首页数据

01 搭建项目 Vite Vue3 &#xff1a; https://xuexiluxian.cn/blog/detail/5e5d17f75af14e1586d3471f613e458602 Vite Vue项目安装router &#xff1a; https://xuexiluxian.cn/blog/detail/0a44da50c0b440d6b8f591867f8909f503 先做首页头部吧&#xff0c;先做准备工作 : ht…

JS--es6模板字符串

一、模板字符串空格 const str 这是一个${" "}空格; console.log(str); // 这是一个 空格二、模板字符串换行 1.转义 const str 这是一个换行\n内容; console.log(str); //这是一个换行 //内容2.缩进换行 const code function test() {con…

CKA备考实验 | 镜像管理

书籍来源&#xff1a;《CKA/CKAD应试指南&#xff1a;从Docker到Kubernetes完全攻略》 一边学习一边整理老师的课程内容及实验笔记&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;CKA备考实验 | 汇总_热爱编程的通信人…

2020全国工业互联网安全技术技能大赛Web题WP

0x00 SimpleCalculator 打开后&#xff0c;发现flag.php可执行数学函数&#xff0c;在网上找到一个原题&#xff1a;https://www.cnblogs.com/20175211lyz/p/11588219.html 可执行shell拿到flag。 payload如下&#xff1a; http://eci-1cei547jhyas2r4f5r2.cloudeci1.ichunqi…

雷诺卡车Renault C460 发动机失火故障

这是一个简短的案例研究&#xff0c;但这会展现正确的操作步骤&#xff0c;你将发现此任务更简易。在这个案例挑战中&#xff0c;这是一台配备D11发动机的雷诺C460卡车。客户抱怨此车性能不佳且像是发动机失火的异常抖动。没有任何警告灯显示在仪表台上&#xff0c;这很有趣&am…

联想服务器无线网卡被禁用,win10系统联想笔记本禁用无线网络适配器的处理技巧...

有关win10系统联想笔记本禁用无线网络适配器的操作方法想必大家有所耳闻。但是能够对win10系统联想笔记本禁用无线网络适配器进行实际操作的人却不多。其实解决win10系统联想笔记本禁用无线网络适配器的问题也不是难事&#xff0c;小编这里提示两点&#xff1a;1、在联想笔记本…

Node+mysql-注册和登录账号实现(原生)

1.创建数据表 说明&#xff1a;创建id&#xff0c;username,password字段&#xff0c;并设置了类型。 2.导入mysql库 npm i mysql2.18.1 3.创建了db文件夹 说明&#xff1a;创建mysql数据池 // 导入mysql包 const mysqlrequire("mysql") // 创建mysql连接池 const…

机器学习实战 | 深度学习初级项目学习和总结

目录 简介神经网络类型和用法总结1. 卷积神经网络CNN特点结构用处 2. 循环神经网络RNN特点结构用处 3. 长短期记忆网络LSTM特点结构用处 基于Keras的神经网络用法总结1. 创建2. 编译3. 训练4. 保存5. 预测 简介 准备写个系列博客介绍机器学习实战中的部分公开项目。首先从初级…