strtok()偶发栈溢出?程序异常现象及原因咨询
strtok()异常行为及栈溢出问题解答
问题背景
使用Kubuntu 22.04 LTS系统、Kate v22.04.3编辑器及gcc v11.3.0编译器,编写了测试strtok()字符串分割功能的C程序,代码如下:
#include <stdio.h> #include <string.h> int main(void) { char inString[] = ""; // string read in from keyboard. char * token = ""; // A word (token) from the input string. char delimiters[] = " ,"; // Items that separate words (tokens). // explain nature of program. printf("This program reads in a string from the keyboard" "\nand breaks it into separate words (tokens) which" "\nare then output one token per line.\n"); printf("\nEnter a string: "); scanf("%s", inString); /* get the first token */ token = strtok(inString, delimiters); /* Walk through other tokens. */ while (token != NULL) { printf("%s", token); printf("\n"); // Get next token. token = strtok(NULL, delimiters); } return 0; }
运行时出现异常:
- 首次输入
fred , steve , nick,仅输出"f"和"ed" - 第二次输入
steve , barney , nick,输出"s"和"eve"后触发栈溢出检测并终止程序 - 测试发现首个token为4字符时程序可勉强运行,5字符及以上则触发栈溢出
a) 为何首个token总是在第二个字符处被拆分?
核心问题是char inString[] = "";仅分配了1字节内存(仅能存空终止符'\0')。用scanf("%s", inString)输入字符串时,输入的字符会直接越界写入相邻的栈内存——刚好覆盖了token指针的存储位置。
比如输入"fred"时,inString的内存被越界写入:inString[0] = 'f',inString[1] = 'r',而token变量就在相邻地址,于是token的内容被篡改,指向了inString+2(也就是字符'e'的地址)。后续strtok的操作和输出,都是基于被破坏后的指针,看起来就像token在第二个字符处被拆分。
b) 为何后续token无法输出?
首先,scanf("%s", inString)只会读取到第一个空白字符就停止,输入的"fred , steve..."实际上只有"fred"被读入(还越界了),后面的", steve..."根本没被读取。
其次,inString的内存溢出已经破坏了栈上的其他数据,包括strtok内部依赖的静态状态变量,导致strtok无法正确识别后续token,直接出现异常行为。
c) 为何首个token长度大于4字符会引发栈溢出?
栈上的变量是连续分配的:inString(1字节)、token(64位系统下占8字节)、delimiters(3字节)依次排列在栈中。输入字符长度超过1字节后,会不断覆盖后续栈内存:
- 输入4字符时,刚好覆盖
token和部分delimiters的内存,还没触及栈帧的关键数据(比如返回地址、栈基指针),程序还能勉强运行; - 输入5字符及以上时,溢出的字符会覆盖栈帧的关键结构,触发GCC默认开启的栈溢出保护机制(
-fstack-protector),程序直接被终止。
内容的提问来源于stack exchange,提问作者Stuart
相关产品推荐
相关产品推荐

