字符数组与字符指针定义字符串的差异及memmove报错原因
字符串数组 vs 字符指针的区别,以及段错误原因解析
嘿,这两个问题本质上是在问C语言中字符串两种常见存储方式的核心差异,咱们一步步讲明白:
问题1:字符串数组和字符指针的区别
这两种定义方式在内存存储、可修改性、灵活性上都有明显不同:
- 存储位置与可修改性
- 数组(
char string[] = "xxx";):如果是局部变量,会在栈内存上分配一块连续空间,把字符串字面量的内容完整复制进去;如果是全局变量则存在全局数据区。这块内存是可写的,你可以随意修改数组里的字符。 - 字符指针(
char* string = "xxx";):指针变量本身存在栈/全局区,但它指向的是存储在**只读数据段(.rodata)**里的字符串字面量。这块内存是操作系统保护的只读区域,尝试修改会触发未定义行为(比如段错误)。
- 数组(
- 内存分配逻辑
- 数组:定义时会根据字符串长度自动分配
字符串长度+1的内存(多出来的1字节存结束符'\0'),内存大小固定,无法动态调整。 - 指针:指针本身只是一个4/8字节的地址变量(取决于平台是32位还是64位),它不存储字符串内容,只是指向已有字符串的起始地址。你可以让它指向动态分配的内存(比如
malloc),也可以指向其他字符串字面量,灵活性更高。
- 数组:定义时会根据字符串长度自动分配
- 初始化与指向特性
- 数组:除了字符串字面量初始化,还可以逐个字符初始化(比如
char string[] = {'H','e','l','l','o','\0'};),一旦定义,数组名是一个常量指针,不能改变它的指向。 - 指针:可以直接指向字符串字面量,也可以指向已有的字符数组或动态内存,并且可以随时修改指针的指向,让它指向其他内存地址。
- 数组:除了字符串字面量初始化,还可以逐个字符初始化(比如
问题2:为什么改成字符指针会触发段错误?
咱们对比两段代码的核心差异:
第一段代码(数组版本)
#include <stdio.h> #include <string.h> int main ( ){ char string [] = "Home Sweet Home"; printf ("%s",(char*)memmove(string,&string[5],10)); }
这里char string[] = "Home Sweet Home";做了一件事:在栈上开辟了一块足够大的内存,把字符串字面量"Home Sweet Home"的每一个字符(包括末尾的'\0')都复制到了栈内存里。memmove操作的是栈上的可写内存,而且memmove本身就是为处理重叠内存区域设计的,所以能正确完成复制,最终输出Sweet Home Home完全符合预期。
第二段代码(指针版本)
#include <stdio.h> #include <string.h> int main ( ) { char* string = "Home Sweet Home"; printf ("%s",(char*)memmove(string,&string[5],10)); }
这里char* string = "Home Sweet Home";是让指针string指向了只读数据段里的字符串字面量。当memmove尝试往这个只读内存区域写入数据时,操作系统的内存保护机制会直接拦截这个操作,触发段错误(Segmentation Fault)——因为只读区域不允许写入操作,这是C标准里明确的未定义行为(虽然语法上允许用char*指向字符串字面量,但修改它是绝对禁止的)。
简单说:数组版本操作的是自己拥有的可写内存,指针版本操作的是系统保护的只读内存,这就是导致段错误的根本原因。
内容的提问来源于stack exchange,提问作者Nimbostratus
相关产品推荐
相关产品推荐

