C语言指针专题二 -- 字符指针与字符串

news/2025/2/1 18:56:46/

目录

1. 字符指针与字符串的核心原理

字符串的本质

字符串的存储

字符指针的特性

字符指针的操作 

2. 编程实例

3. 常见陷阱与注意事项

4. 总结


1. 字符指针与字符串的核心原理

字符串的本质

  • C语言中没有独立的字符串类型,字符串本质是 \0(空字符)结尾的字符数组

  • 字符串的存储方式:

    • 字符数组:显式声明数组并初始化,如 char str[] = "Hello";

    • 字符指针:直接指向字符串字面量(存储在程序的只读内存区),如 char *p = "Hello";

字符串的存储

在C语言中,字符串是以字符数组的形式存储的,并以空字符 \0 作为字符串的结束标志。下面通过文字描述和图示来解释C语言中的字符串存储规则。

假设有一个简单的字符串 "Hello",在内存中的存储形式如下:

地址    内存内容
-----------------
1000    'H' (72)
1001    'e' (101)
1002    'l' (108)
1003    'l' (108)
1004    'o' (111)
1005    '\0' (0)  // 字符串结束标志
  • 每个字符都占用一个字节的存储空间。
  • '\0' 是字符串的结束标记,它告诉程序这个位置之前的所有字符属于当前字符串。
  • 注意这里的地址(如1000)只是示意性的,实际的内存地址会根据系统分配情况有所不同。

图例表示
如果我们用图形的方式表示上述 "Hello" 字符串在内存中的存储方式,可以想象成如下布局:

+--------+--------+--------+--------+--------+--------+
| 1000   | 1001   | 1002   | 1003   | 1004   | 1005   |
+--------+--------+--------+--------+--------+--------+
| 'H'    | 'e'    | 'l'    | 'l'    | 'o'    | '\0'   |
+--------+--------+--------+--------+--------+--------+

每个方格代表一个字节的内存单元,里面存放的是相应字符的ASCII码值。例如,'H' 的ASCII码是72,但在内存表示中我们通常直接写字符本身而不是其ASCII码值,以便于理解和记忆。

字符指针的特性

  • 指向字符串字面量:字符指针可以指向字符串常量(只读内存区),但不可修改其内容。

    char *p = "Hello"; // p指向只读内存区的字符串
    // p[0] = 'h';     // 错误!尝试修改只读内存会导致崩溃
  • 指向字符数组:字符指针可以指向动态分配的或栈上的字符数组,此时可修改内容。
    char arr[] = "Hello";
    char *p = arr;     // p指向栈上的字符数组
    p[0] = 'h';        // 合法!修改栈内存

字符指针的操作 

  • 遍历字符串:通过指针移动逐个访问字符,直到遇到\0。
  • 动态内存分配:使用malloc为字符串动态分配内存,需手动释放。
  • 字符串函数:C标准库提供<string.h>中的函数(如strcpy、strlen),底层均依赖指针操作。

2. 编程实例

实例1:字符指针与字符串初始化

#include <stdio.h>int main() {// 方式1:字符指针指向字符串字面量(只读)char *str1 = "Hello";printf("str1: %s\n", str1); // 输出: Hello// 方式2:字符数组(可修改)char str2[] = "World";char *p = str2;           // 指针指向字符数组p[0] = 'w';               // 修改第一个字符printf("str2: %s\n", p);  // 输出: worldreturn 0;
}

实例2:动态分配字符串内存

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main() {// 动态分配内存存储字符串char *str = (char*)malloc(20 * sizeof(char));if (str == NULL) {printf("内存分配失败!\n");return 1;}strcpy(str, "Dynamic");  // 复制字符串到堆内存printf("初始内容: %s\n", str); // 输出: Dynamicstrcat(str, "123");      // 追加字符串printf("追加后内容: %s\n", str); // 输出: Dynamic123free(str);  // 释放内存str = NULL; // 避免野指针return 0;
}

实例3:字符指针遍历字符串

#include <stdio.h>void print_string(const char *str) {while (*str != '\0') {  // 遍历直到空字符printf("%c ", *str);str++;              // 指针后移}printf("\n");
}int main() {char *s = "Pointer";print_string(s);  // 输出: P o i n t e r return 0;
}

 实例4:修改字符指针指向的内容

#include <stdio.h>int main() {char arr[] = "Hello";  // 栈上的字符数组char *p1 = arr;        // 指向可修改的数组p1[0] = 'h';           // 合法修改printf("p1: %s\n", p1); // 输出: hellochar *p2 = "Hello";    // 指向只读内存的字符串字面量// p2[0] = 'h';        // 运行时错误(段错误)!printf("p2: %s\n", p2);return 0;
}

 实例5:字符串作为函数参数(指针传递)

#include <stdio.h>// 函数:统计字符串长度(手动实现strlen)
int string_length(const char *str) {int len = 0;while (*str != '\0') {len++;str++;}return len;
}int main() {char *s = "C Programming";printf("字符串长度: %d\n", string_length(s)); // 输出: 13return 0;
}

3. 常见陷阱与注意事项

1. 修改只读字符串

char *p = "Hello";
p[0] = 'h';  // 段错误!字符串字面量不可修改。

 2. 未分配内存直接使用指针

char *p;
strcpy(p, "Hello");  // 野指针!p未初始化指向合法内存。

3. 忘记字符串终止符\0

char arr[5] = {'H', 'e', 'l', 'l', 'o'}; // 缺少\0,遍历时可能越界
printf("%s", arr);  // 输出可能包含乱码

4. 内存泄漏

char *str = malloc(100);
// 忘记调用 free(str);

4. 总结

  • 字符指针是操作字符串的核心工具,灵活但需谨慎使用。

  • 字符串字面量存储在只读内存区,字符指针指向时不可修改。

  • 字符数组(栈或堆内存)可通过指针修改内容。

  • 动态分配字符串内存时,必须手动管理生命周期(malloc/free)。

  • 始终确保字符串以\0结尾,避免越界访问。


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

相关文章

CTF-web: phar反序列化+数据库伪造 [DASCTF2024最后一战 strange_php]

step 1 如何触发反序列化? 漏洞入口在 welcome.php case delete: // 获取删除留言的路径&#xff0c;优先使用 POST 请求中的路径&#xff0c;否则使用会话中的路径 $message $_POST[message_path] ? $_POST[message_path] : $_SESSION[message_path]; $msg $userMes…

双向链表在系统调度、游戏、文本编辑及组态方面的应用

在编程的奇妙世界里&#xff0c;数据结构就像是一把把神奇的钥匙&#xff08;前面我们介绍过单向链表的基础了&#xff0c;这里我们更进一步&#xff09;&#xff0c;能帮我们打开解决各种问题的大门。今天&#xff0c;咱们就来聊聊其中一把特别的钥匙——双向链表。双向链表和…

Spring Boot(6)解决ruoyi框架连续快速发送post请求时,弹出“数据正在处理,请勿重复提交”提醒的问题

一、整个前言 在基于 Ruoyi 框架进行系统开发的过程中&#xff0c;我们常常会遇到各种有趣且具有挑战性的问题。今天&#xff0c;我们就来深入探讨一个在实际开发中较为常见的问题&#xff1a;当连续快速发送 Post 请求时&#xff0c;前端会弹出 “数据正在处理&#xff0c;请…

C++,STL,【目录篇】

文章目录 一、简介二、内容提纲第一部分&#xff1a;STL 概述第二部分&#xff1a;STL 容器第三部分&#xff1a;STL 迭代器第四部分&#xff1a;STL 算法第五部分&#xff1a;STL 函数对象第六部分&#xff1a;STL 高级主题第七部分&#xff1a;STL 实战应用 三、写作风格四、…

freeswitch在centos上编译过程

操作系统&#xff1a;centos9-last usr/local/freeswitch/bin/freeswitch -version FreeSWITCH version: 1.10.13-devgit~20250125T131725Z~3f1e4bf90a~64bit (git 3f1e4bf 2025-01-25 13:17:25Z 64bit)vi /etc/ssh/sshd_config ip a nmtui reboot ip a curl -o /etc/pki/rpm-…

数仓ETL测试

提取&#xff0c;转换和加载有助于组织使数据在不同的数据系统中可访问&#xff0c;有意义且可用。ETL工具是用于提取&#xff0c;转换和加载数据的软件。在当今数据驱动的世界中&#xff0c;无论大小如何&#xff0c;都会从各种组织&#xff0c;机器和小工具中生成大量数据。 …

信号模块--simulink操作

位置simulink/sourses 常用的模块 功能&#xff1a;常数模块&#xff0c;提供一个常数 数据设置可以是一维或多维 一维数据设置 多维数据设置&#xff08;例三维数据设置&#xff09; 方波脉冲模块 模块用于按固定间隔生成方波脉冲信号 振幅就是方波的幅度&#xff0c;0到…

一个简单的自适应html5导航模板

一个简单的 HTML 导航模板示例&#xff0c;它包含基本的导航栏结构&#xff0c;同时使用了 CSS 进行样式美化&#xff0c;让导航栏看起来更美观。另外&#xff0c;还添加了一些 JavaScript 代码&#xff0c;用于在移动端实现导航菜单的展开和收起功能。 PHP <!DOCTYPE htm…