如何保留strcpy溢出警告的同时消除柔性数组代码的误报?
问题描述
我们使用如下结构体定义动态字符串:
typedef struct { size_t StringLength; char String[1]; } mySTRING;
并通过以下函数创建该结构体实例:
mySTRING * CreateString(char * Input) { size_t Len = strlen(Input); int Needed = sizeof(mySTRING) + Len; mySTRING * pString = malloc(Needed); // 省略其他初始化逻辑 strcpy(pString->String, Input); }
在Red Hat Linux的cc编译器中,这段代码触发了如下合理但属于误报的警告:
strings.c:59:3: warning: âstrcpyâ writing 14 bytes into a region of size 1 overflows the destination [-Wstringop-overflow=]
strcpy(pString->String, Input);
我们明确这段代码是安全的,无需修正,但希望保留-Wstringop-overflow=警告以排查其他潜在问题,请问如何修改代码让编译器识别这段代码是安全的?
解决方法
以下几种修改方式可以让编译器正确识别这段代码的安全性,同时保留原有警告开关:
1. 使用C99灵活数组成员(推荐)
C99标准引入的灵活数组成员是专门为这种动态扩展尾部缓冲区的场景设计的语法,编译器能直接识别其用途,不会触发误报:
typedef struct { size_t StringLength; char String[]; // 灵活数组成员,不占用结构体自身大小 } mySTRING; mySTRING * CreateString(char * Input) { size_t Len = strlen(Input); // 修正内存计算:结构体大小 + 字符串长度 + 终止符(strcpy需要写入) size_t Needed = sizeof(mySTRING) + Len + 1; mySTRING * pString = malloc(Needed); if (pString == NULL) { // 处理内存分配失败逻辑 return NULL; } pString->StringLength = Len; strcpy(pString->String, Input); return pString; }
2. 显式强制类型转换告知缓冲区大小(兼容C89)
如果需要兼容不支持灵活数组成员的C89标准,可以通过强制类型转换,明确告诉编译器String指向的缓冲区实际大小:
mySTRING * CreateString(char * Input) { size_t Len = strlen(Input); int Needed = sizeof(mySTRING) + Len + 1; // 补充终止符空间 mySTRING * pString = malloc(Needed); if (pString == NULL) { return NULL; } pString->StringLength = Len; // 强制转换为指向Len+1字节缓冲区的指针,让编译器知晓实际大小 strcpy((char(*)[Len + 1])pString->String, Input); return pString; }
3. 使用memcpy替代strcpy(手动管理终止符)
通过memcpy明确指定复制长度,再手动添加字符串终止符,编译器能识别内存操作的安全性:
mySTRING * CreateString(char * Input) { size_t Len = strlen(Input); int Needed = sizeof(mySTRING) + Len + 1; mySTRING * pString = malloc(Needed); if (pString == NULL) { return NULL; } pString->StringLength = Len; memcpy(pString->String, Input, Len); pString->String[Len] = '\0'; // 手动添加字符串终止符 return pString; }
内容的提问来源于stack exchange,提问作者Morag Hughson
相关产品推荐
相关产品推荐

