我的功能如下:
// string is a null-terminated char array. Replace all a in the string with b
void ReplaceCharInString(char *string, char a, char b)
{
// loop over the string char by char, to find all "a"s and replace them with "b"
}
我正在进行防御性编程。问题是客户端上的实现回复真正传递了一系列字符。如果传入单个char的地址,程序肯定会遇到错误状态(可能崩溃)。我该如何检查并避免这种情况? (我知道如果我传入std :: string对象,问题就会消失了)
答案 0 :(得分:12)
不,你不能检查这个,并且必须信任该函数的用户传递一个实际正确的以null结尾的字符串。
这也是所有C标准库函数的方式,如strcpy()
,strlen()
,......工作。
答案 1 :(得分:1)
如果字符串未终止,则无法检查是否用完了已分配的内存 正如sth所说的那样,标准库函数如strcpy(),strlen(),也依赖于字符串有效(空终止)的事实。
......但是,一个解决方案可能是Mudflap。 代价高昂(以performance表示)并且仅对GCC有效。
Mudflap是一个监视所有指针/数组操作的库。有了这个,您将能够检查特定位置是否是有效的内存。
事实上,我看到使用Mudflap的唯一原因是安全性对于您的应用程序来说是一个非常大的问题。但即使在这种情况下,GCC也提供了更好的缓冲区溢出替代方案(参见-fstack-protector [-all])。
答案 2 :(得分:0)
最好是使用std :: string。
答案 3 :(得分:0)
你真的不能完全信任用户输入的数据。使用std::string
是一种改进,因为它可以防止缓冲区溢出,但它仍然不是完全安全的。什么阻止用户输入消耗所有内存的10GB字符串?
当您第一次从用户收到输入时,您确实需要验证输入(无论是来自套接字还是来自stdin。)例如,您可以强制执行最大字节数读取或确保所有输入字符都在ASCII范围内。
如果必须使用C字符串,则可以memset
将输入缓冲区设置为0,并确保缓冲区中的最后一个字符始终为NULL字节,以确保字符串正确地以null结尾
答案 4 :(得分:0)
您可以通过正确记录代码来避免这种情况。除了C的完全初学者(谁不应该编写严肃的代码)之外,没有人会接受单个char
变量的地址(甚至声明这样的变量),所以你的恐惧不是很相关。
C99部分防范这种情况的一种方法是将函数声明为:
void ReplaceCharInString(char string[static 2], char a, char b);
这意味着string
必须是指向至少2个字符的有效指针,这将防止您提到的单字符指针问题,代价是使函数无法支持空字符串。但它仍然无法保护您免受不正确的malloc
等等。
编写安全的C实际上是一个正确的代码文档和审计问题,并解雇无能的人(或者更好,但从不雇用他们开始)。该语言设计轻巧高效。如果你试图在它上面放置你自己的巨大安全带层,你可能会比设计更高级语言的人做得更糟糕,然后你可能刚刚使用了更高级别的语言来开始
答案 5 :(得分:0)
使用C ++,您应该使用函数重载并确保没有人会向您发送char的地址。
void ReplaceCharInString(std::string & p_string, char p_a, char p_b)
{
for(size_t i = 0, iMax = p_string.size(); i < iMax; ++i)
{
// replace if needed : p_string[i]
}
}
void ReplaceCharInString(char & p_char, char p_a, char p_b)
{
// replace if needed : p_char
}
template<size_t size>
void ReplaceCharInString(char (& p_string)[size], char p_a, char p_b) ;
{
for(size_t i = 0; i < size; ++i)
{
// replace if needed : p_string[i]
}
}
只有这三个函数(并且没有函数将char *
作为其第一个参数),没有人能够使用原始char *
调用它们:
void foo()
{
char a[4] ;
std::string b ;
char c ;
char * d ;
// initialize a, b, c and d to some values
ReplaceCharInString(a, 'x', 'z') ; // compiles
ReplaceCharInString(b, 'x', 'z') ; // compiles
ReplaceCharInString(c, 'x', 'z') ; // compiles
ReplaceCharInString(d, 'x', 'z') ; // COMPILATION ERROR
}
所以没有人会用一个字符的地址来调用你的函数。
无论如何,在C ++中,很少有理由将char *
用作字符串(您可以使用std::string
),甚至更少使用char *
作为地址单个字符(您将使用char &
),因此没有人会抱怨上述函数中缺少对char *
的支持。
答案 6 :(得分:-1)
该函数只是获取指向内存中某处的指针。检查它不是NULL是防御性编程实践。如果调用者“忘记”分配内存或某些分配失败,这通常是一个值。
可能会对无效的char数组进行更多检查,但这些检查与平台有关。
至少你无法区分char数组的指针和char的指针。
答案 7 :(得分:-1)
你的原型是错的。将数组传递给函数时,总是必须传递数组的长度,因为在函数内部你永远不知道数组的大小(它总是指针的大小)。因此,您应该为数组的大小声明一个int变量,以便声明本身明确地使用数组。
void ReplaceCharInString(char *string, int len, char a, char b)
这样你就不依赖于读取字符串输入arg的NULL字符,只需使用长度。 如果你不能这样做,并且你必须按原样保留声明,那么你应该有一个明确的合同,关于函数的用户应该做什么,没有问题。然后责任被委托给函数的调用者。履行合同