C语言中指针与char数组定义字符串的区别及相关问题
1. 未初始化char指针搭配scanf的运行结果
你给出的第一段代码本身存在两处问题:一是printf语法错误(漏写右括号),二是指针未初始化的逻辑错误,修复语法后的代码如下:
int main() { char* word; scanf("%s",word); printf("%s",word); }
首先明确结论:scanf不会自动申请内存并给指针赋值。char* word是栈上的局部指针变量,未初始化时它存储的地址是随机值,指向的内存区域完全不受你的程序控制。scanf("%s", word)会直接把输入的字符串写入word指向的随机地址:
- 如果该地址属于受系统保护的内存/未分配内存,程序会直接报段错误崩溃
- 如果该地址刚好是程序可写的内存,输入的内容会直接覆盖这块内存原有的数据,属于典型的野指针非法访问,是未定义行为,可能出现变量值篡改、程序逻辑异常、安全漏洞等任意后果。不存在“输入过长才会覆盖其他数据”的问题,只要写入操作发生,就已经在非法修改内存了。
2. 固定长度char数组搭配scanf的运行结果
第二段代码同样存在printf漏写右括号的语法问题,修复后代码如下:
int main() { char word[100]; scanf("%s",word); printf("%s",word); }
char word[100]会在栈上分配连续100字节的存储空间,数组名是指向这块内存起始地址的常量指针,本身是合法有效的。但C语言本身没有内置数组越界检查机制,编译器也不会额外设置存储上限的运行时校验。
如果输入的字符串长度超过99字节(需要留1字节存储字符串结束符\0),就会发生栈溢出,超出部分的内容会直接覆盖栈上后续的内存数据(包括其他局部变量、函数返回地址等)。这也是未定义行为,不一定会立刻触发明显的运行时错误,可能程序会异常运行、崩溃,甚至被利用进行栈溢出攻击。
3. 两种用法的核心区别
- 第一种的
char* word本身只是一个存储地址的指针变量,仅占4/8字节(取决于系统位数),没有分配任何用于存储字符串的内存空间,未初始化时指向的地址是非法的。 - 第二种的
char word[100]直接分配了100字节的可写内存空间,数组名对应的地址固定且合法,足以存储最长99字节的字符串。
4. 其他类型指针的偏移写入规则
C语言允许对任意类型的指针做偏移运算,也允许向偏移后的地址写入数据,但前提是你必须保证偏移后的地址属于你合法申请、拥有写入权限的内存区域。如果偏移到了未申请/不可写的内存区域,不管是什么类型的指针,写入操作都属于非法内存访问,同样会触发未定义行为,和野指针、数组越界的后果一致。
补充正确实践
如果要安全使用scanf读入字符串,可以指定读取长度避免溢出,例如针对长度为100的数组,写为scanf("%99s", word)即可限制最多读取99个字符。如果要使用指针存储字符串,需要先手动申请内存:char* word = malloc(100);,使用完后要调用free(word)释放内存避免泄漏。
内容的提问来源于stack exchange,提问作者Mohammed Elbagoury

