【问题标题】:s[j] = s[i]; segmentaion fault while trying to copy characterss[j] = s[是];尝试复制字符时出现分段错误
【发布时间】:2018-05-09 14:42:49
【问题描述】:

我从某个地方找到了这个程序,并试图理解它。

这一行:s[j++] = s[i]; 是导致崩溃的原因。我的理解是,至少程序第一次不应该崩溃,因为 j 稍后会增加。第一次 j 和 i 的值为 0。

所以,这就像 s[0] = s[0];

为什么会崩溃?

#include <iostream>

using namespace std;


void squeeze(char a[], char c);

int main()
{
    squeeze("qwiert", 'i');

    return 0;
}

void squeeze(char s[], char c)
{
    int i, j;

    for (i = j = 0; s[i] != '\0'; i++)
    {
        if (s[i] != c)
        {
            std::cout << "\ni: " << s[i];
            s[j++] = s[i];
            std::cout << "\nj: " << s[j];

            std::cout << "\nj : " << j;
            exit(0);
        }
    }

    s[j] = '\0';
}

输出:

i: q

此后程序崩溃。

我已经把exit语句停止了那个指向segfault的程序。

【问题讨论】:

  • 为s保留的地址空间可以写保护吗?因为在 main() 中它被定义为一个常量,编译器可以将它放入特殊的内存区域。但这只是猜测。
  • 是的,这就是原因。 @KillPinguin
  • 编译时收到以下警告:“ISO C++ 禁止将字符串常量转换为 'char*' [-Wwrite-strings]”。一些编译器会直接拒绝这个(技术上都应该)并给你一个错误。该程序将在编译时出现警告,但警告意味着有些东西不太符合 kosher 的规定,并且该程序很可能不会按照您预期的方式运行。
  • @user4581301,标准中实际上没有任何内容要求实现完全拒绝 UB。虽然这可能是一个很好的功能,但您可能应该区分什么是必需和什么是有用的。
  • @paxdiablo 字符串文字到 char* 的转换在 C++11 中被正式禁止,但是,是的,我不知道 Aquarius_Girl 编译的标准是什么。

标签: c++ arrays segmentation-fault


【解决方案1】:

您不能更改字符串文字,例如:

squeeze("qwiert", 'i');

标准的几乎所有迭代都涵盖了这一点(a)

  • C++03 2.13.4.String literals [lex.string] /2;
  • C++11 2.14.5.String literals [lex.string] /12;和
  • C++14 2.14.5.String literals [lex.string] /13

每个都有相同的措辞:

是否所有字符串文字都是不同的(即存储在不重叠的对象中)是由实现定义的。 尝试修改字符串文字的效果是不确定的。

最新的 C++17 标准中的措辞略有变化,但大致相同,目前为C++17 5.13.5.String literals [lex.string] /16

是否所有字符串文字都是不同的(即,存储在不重叠的对象中)以及字符串文字的连续评估是否产生相同或不同的对象是未指定的。 [注意:尝试修改字符串文字的效果是不确定的。 - 结束注释]

我建议你尝试一下:

char str[] = "qwiert"; // Make a writable copy.
squeeze(str, 'i');     //   then fiddle with that.

(a) 这个答案中的 ISO 引用实际上提供了关于为什么会出现这种情况的暗示。

我们并不总是拥有数 GB 的机器,而且通常情况下必须采取某些步骤在早期编译器中进行优化(C 主要是 C 语言,但由于它最初是作为 C 语言的前端实现而被带到 C++ 中) )。

为此,具有相同字符(或以某些方式重叠字符,例如"successful""unsuccessful")的两个字符串可以共享相同的内存以减少空间。

当然,这意味着您不能在不影响另一个的情况下更改其中一个,这就是制定此规则的原因。

【讨论】:

    【解决方案2】:

    其他答案已经指出了问题以及如何解决它。

    我想指出,您可以通过提高编译器的警告级别来检测此类错误。

    使用g++ -Wall,我收到以下警告消息:

    socc.cc: In function ‘int main()’:
    socc.cc:10:26: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
         squeeze("qwiert", 'i');
    

    【讨论】:

      【解决方案3】:

      您将字符串常量 "qwiert" 传递给 squeeze 函数。然后,此函数尝试修改该字符串常量,这是非法的。这会导致核心转储。

      为此,您需要传入一个数组:

      int main()
      {
          char str[] = "qwiert";
          squeeze(str, 'i');
      
          return 0;
      }
      

      【讨论】:

      • 刚刚试过了。问题已解决。非常感谢你。
      猜你喜欢
      • 2021-06-27
      • 2018-06-06
      • 2023-04-03
      • 2011-09-03
      • 2020-10-02
      • 2012-06-25
      • 2022-12-08
      • 2016-06-16
      相关资源
      最近更新 更多