0

为了测试我的技能,我正在尝试编写我自己版本的一些标准库函数。strlen()我为,写了一个替换strlength()

int strlength(const char *c){
    int len = 0;
    while (*c != '\0') {
        c++;
        len++;
    }
    return len;
}

包括空终止符,我正在尝试编写一个函数来反转字符串。这:

char *reverse(const char *s){
    char *str = (char *)malloc(sizeof(char) * strlength(s));
    int i = 0;
    while (i < strlength(s)) {
        str[i] = s[(strlength(s) - 1) - i];
        i++;
    }
    str[strlength(s)] = '\0';
    return str;
}

适用于每个字符串,除了一个具有 32 个字符(不包括空终止符)的字符串,例如foofoofoofoofoofoofoofoofoofoofo. 它挂在reverse()函数while循环中。对于所有其他数量的字符,它都有效。为什么会这样?

4

5 回答 5

6

您的缓冲区str关闭了 1。您的写入溢出到堆的其余部分。

至于为什么它适用于 32 以外的值,我的猜测是它与堆的内存对齐有关。编译器正在为较小的缓冲区大小添加额外的填充,但是 32 字节很好地对齐(它是 2 的幂、8 的倍数等),因此它不会添加额外的填充,这会导致您的错误出现。尝试其他 8 的倍数,您可能会得到相同的行为。

于 2013-06-25T16:34:50.337 回答
4
char *reverse(const char *s){

在这里您分配 N 个字符(其中 N 是不带 s 的长度\0):

    char *str = (char *)malloc(sizeof(char) * strlength(s));

然后你对 s 的所有字符迭代 N 次

    int i = 0;
    while (i < strlength(s)) {
        str[i] = s[(strlength(s) - 1) - i];
        i++;
    }

最后你添加\0N+1 个字符

    str[strlength(s)] = '\0';
    return str;
}

所以你应该这样做:

    char *str = malloc(sizeof(*str) * strlength(s) + 1); // +1 for final `\0`

有趣的是,我刚刚测试了你的代码,它对我来说很好(去掉一个字符)和你的 32 个字符串。正如@JoachimPileborg 所说,“这就是未定义行为的有趣之处”

正如其他人所建议的那样,问题肯定是由于内存对齐,当您将数据与内存对齐时,它会溢出,而当它未对齐时,它会覆盖填充值。

于 2013-06-25T16:37:26.533 回答
2

您询问:

但这适用于所有其他字符串长度。为什么不是32?

很可能是因为运行时以 32 字节的块为单位分配内存。因此,当缓冲区大小为 22 字节时,1 个字符的缓冲区溢出不是问题。但是当您分配 32 个字节并尝试写入第 33 个字节时,问题就出现了。

我怀疑您会在 64 个字符、96、128 等字符串中看到相同的错误。. .

于 2013-06-25T16:42:37.980 回答
1

代替

malloc(sizeof(char) * strlength(s))

经过

malloc(sizeof(char) * (1+strlength(s)));

该行:

str[strlength(s)] = '\0';

通常作为 malloc 库例程保留字边界对齐块并仅分配调用中请求的部分,即。2 的幂,但是当溢出的数据覆盖超出分配的部分时,通过调试器检查反汇编是了解特定于目标行为的构建工具链的最佳方法。由于该行跟随 while 循环而不是在其中,因此如果不进行反汇编,while 循环如何变为无限是不可预测的。

于 2013-06-25T16:39:27.090 回答
0

其他人所说的关于缓冲区溢出和运行时内存分配实现/策略的变幻莫测。由于 NUL 终止八位字节,C 字符串的大小比其长度大 1。

这样的事情应该对你有用(更简洁,更容易阅读):

#define NUL ((char)0) ;

char *reverse( const char *s )
{
  int   len = strlen(s) ;
  char *tgt = ((char*)malloc( 1+len )) + len ;
  char *src = s ;

  *(tgt--) = NUL ;
  while ( *src )
  {
    *(tgt--) = *(src++) ;
  }

  return tgt;
}

您的strlen()实现也比它需要的更复杂。这就是您所需要的:

int string_length( const char *s )
{
  char *p = s ;
  while ( *p ) ++p ;
  return p - s ;
}
于 2013-06-25T19:08:43.723 回答