我可以确定char *参数解决了多少有效内存?

时间:2010-10-30 10:48:37

标签: c++ c

我的功能如下:

// 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对象,问题就会消失了)

8 个答案:

答案 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字符,只需使用长度。 如果你不能这样做,并且你必须按原样保留声明,那么你应该有一个明确的合同,关于函数的用户应该做什么,没有问题。然后责任被委托给函数的调用者。履行合同