質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

string G[h]に対して、G[x][y]という1文字ごとの埋め方をするとsegmentation faultになる理由を知りたい

解決済

回答 5

投稿

  • 評価
  • クリップ 0
  • VIEW 318

ratera

score 18

知りたいこと

string G[h]に対してG[x][y]という1文字ごとの入力をすると、20×20くらいのサイズからsegmentationfaultになる。

その理由が知りたいです。宜しくお願いします。

試したこと

  • 文字列をまとめて入力する場合には同じサイズでセグフォにならなかった。e.g. for (int i = 0; i < h; i++) cin >> G[i]

以下、考察

  • stringでの入力ではなく、G[y][x]のようなcharの入力だと問題があるようだ
  • G[y][x]のセグフォは、(x,y)=(0,1)の時に起きたが、この配列は存在するはずなので、なぜセグフォになるのか想像がつかない
  • G[y][x]のようなcharの入力だとデバッガ上はG[h]に値が入力されていないが、g[y][x]で値が標準出力出来て変なことをしている気がする

エラー画面

イメージ説明

コード

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int, int>;


int main() {
  int h, w;
  cin >> h >> w;

  string G[h];
  //for (int i = 0; i < h; i++) cin >> G[i];

  //入力の生成
  srand(time(NULL));
  for(int y = 0; y < h; ++y) {
    for(int x = 0; x < w; ++x) {
            if(rand()%10==0) G[x][y]='.';
            else G[x][y]='#';
    }
  }
    cout << endl;
    rep(i,h){
        rep(j,w) {cout << G[i][j];}
        cout << endl;
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+2

C++のstring::operator[]は、文字列の長さ以上の箇所にアクセスして、それを書き換えた場合は正しく動作することが保証されません(cpprefjp)。

空文字列の[0]に書き込んで文字を追記する、ようなことはできません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 10:35

    ありがとうございます!
    []の定義もされているのは知りませんでしたが、読んでいて面白いです。

    バッファはあるというご回答と、空文字列(なのでバッファがない?という解釈をしました)というご回答で若干混乱気味ではあります。

    (初心者目線ですが、たぶんstringの実装のコードを読めないとダメな気がしており、現段階では「空文字に書き込むのはできない」と覚えたい気持ちにかられております。)

    キャンセル

  • 2020/07/08 10:38

    > バッファはあるというご回答と、空文字列(なのでバッファがない?という解釈をしました)というご回答で若干混乱気味ではあります。

    バッファはあるけど、デフォルトコンストラクタで初期化しただけの状態では長さゼロです。

    キャンセル

  • 2020/07/08 10:43 編集

    > 初心者目線ですが、たぶんstringの実装のコードを読めないとダメな気がしており

    いえ、それは「やってはいけません」。C++の規約で決まっている範囲でコードを書いてください。規定されていない実装の詳細に依存したコードを書くと、何かの拍子に実装が変わって動かなくなる危険があります。

    キャンセル

  • 2020/07/08 10:51

    あ!バッファはメモリのサイズで、長さは\0(NULL)までの文字列の数ということですね!
    なので、空文字は最初に\0が来るもの。両立しますね!

    ちょっとズレている気もしなくもありませんが、解決しました!

    キャンセル

checkベストアンサー

+1

次のようにすれば、G[y][x] = '.'; と書けます。

#include <iostream> // cin, cout
#include <string>   // string
#include <vector>   // vector
#include <cstdlib>  // srand, rand
#include <ctime>    // time
using namespace std;

int main()
{
    int h, w;
    cin >> h >> w;

    vector<string> G(h);

    srand(time(NULL));
    for (int y = 0; y < h; ++y) {
        G[y].resize(w);
        for (int x = 0; x < w; ++x) {
            if (rand() % 10 == 0) G[y][x] = '.';
            else G[y][x] = '#';
        }
    }
    cout << endl;
    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) cout << G[i][j];
        cout << endl;
    }
}


追記
最近の gcc(64ビット) では、sizeof(std::string) は 32 です。
std::string のソースは見ていませんが、stringオブジェクトのダンプを見て、
次のように妄想してみました。

#include <iostream>
#include <string>
using namespace std;

struct String {
    char *data;
    uint64_t size;
    union {
        char here[16];
        uint64_t capacity;
    };
};

void dump(string &s)
{
    cout << "size() = " << s.size() << ", capacity() = " << s.capacity()
         << ", s.data() = [" << s.data() << "]\n";
    String *p = reinterpret_cast<String *>(&s);

    char *data;
    uint64_t capacity;
    if (p->size < 16)  data = p->here, capacity = 15;
    else data = p->data, capacity = p->capacity;
    cout << "String: size = " << p->size << ", capacity = " << capacity
         << ", data = [" << data << "]\n\n";
}

int main()
{
    string s;
    dump(s);
    s = "abc";
    dump(s);
    s = "abcdefghijklmnopqrstuvwxyz";
    dump(s);
}


実行結果

size() = 0, capacity() = 15, s.data() = []
String: size = 0, capacity = 15, data = []

size() = 3, capacity() = 15, s.data() = [abc]
String: size = 3, capacity = 15, data = [abc]

size() = 26, capacity() = 30, s.data() = [abcdefghijklmnopqrstuvwxyz]
String: size = 26, capacity = 30, data = [abcdefghijklmnopqrstuvwxyz]

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 10:57 編集

    resizeといものがあるのですね!ありがとうございます。
    > G[y].resize(w)

    裏の事情に通じていないとstringの書き込みは問題が起きるのが今回の件で感じたので、おとなしく二次元配列を使おうという気になっています。(今回はstringでなければならない理由はないので)

    resizeは以下のサイトで理解できました!便利!
    https://suzulang.com/c-stdvector-resize%E3%81%A8reserve%E3%81%AE%E9%81%95%E3%81%84-%E5%8A%A0%E7%AD%86%E4%B8%AD%EF%BC%9F/

    キャンセル

+1

string G[h];という宣言をしたので空のstringh個用意されたことになります。

空のstringは適当なサイズのバッファを確保してそのポインタを持っているような実装で、
string[]演算子はどうやらただ単にそのバッファを参照しているだけで、
バッファの長さを超えるような範囲を参照してもバッファを拡張するなど気の利いたことはしてくれないようです。


そもそも
string G[h];
としておきながら
forループ内で

    for(int x = 0; x < w; ++x) {
            if(rand()%10==0) G[x][y]='.';
            else G[x][y]='#';
    }

x=0...w-1でアクセスしているのが間違っているような

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 10:31 編集

    バッファがあるのですね!
    てっきり、stringを宣言した際はアドレスだけでバッファはないものだと思いこんでおりましたが、手を動かしてみるとバッファありました!

    手元でstring[1]を定義して、string[0]にcinで"<1万文字>"投げてみたところ、アドレスは変わらないくらいバッファがとてもあるようでした。
    (string[0]かstring[1]のアドレスは変わることを期待していましたが、なぜかアドレスは変わらないのはちょっと不思議でしたが。16^2くらい文字列いれたら溢れると思ったので)

    その状態で、[1][0]でバッファを超えるのはどのような状態なのか少々気になる所ですが、
    謎は深まるばかりで、たぶんstringの実装のコードを読めて裏の振舞を理解しないとダメな気がしてきました。

    ありがとうございます。

    ```
    &string[0]=0x7fffe125d300
    &string[1]=0x7fffe125d320

    ※ここでstring[0] = "1万文字くらいの文字列をいれる"

    &string[0]=0x7ffff01dcaa0 // アドレスは変わらない
    &string[1]=0x7ffff01dcac0
    ```

    キャンセル

  • 2020/07/08 10:54

    > G[y][x]のセグフォ

    コードはG[x][y]になってしまっています

    キャンセル

  • 2020/07/08 11:08 編集

    完全に想像ですけど
    stringはバッファへのアドレスを持っているでしょうから
    何万文字入れようがstring型自体のサイズは変わらないかと

    キャンセル

  • 2020/07/09 12:19

    >コードはG[x][y]になってしまっています
    鋭いご指摘ありがとうございます..!仰る通りでした。
    ただ、うまくいくかと思ったのですが、別の箇所でセグフォになりました。

    キャンセル

+1

string G[h];

この時点で空文字列がh個。
空文字列だから長さ0、それのy番目の文字は存在しない
存在しないとこに無理くり代入したら怒られますやろフツー

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 09:43

    カブリマクリー

    キャンセル

  • 2020/07/08 10:39

    ご回答沢山で、初心者質問に、非常に申し訳ない気持ちになっています。><

    空文字列だからという発想に至れなかったので、全ての敗因です(クヤシイー)

    stringを扱う時の代入は気を付けようと思いました。
    (配列と同じように参照する際の問題は見えていませんので、ちょっとどう気を付けようか考え中です)

    キャンセル

-3

stringとchar[] とは全く別もんなんで、そういう使い方はできません

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 09:32

    > stringとchar[] とは全く別もんなんで、そういう使い方はできません

    いいえ、std::string は[]がオーバーロードされているので、charの配列のような使い方ができます。
    $ cat t7.cpp
    #include <iostream>
    #include <string>

    int main() {
    std::string s = "abcdefg";
    std::cout << s[1] << " " << s[3] << std::endl;
    return 0;
    }
    $ g++ -Wall t7.cpp; ./a.out
    b d
    $

    キャンセル

  • 2020/07/08 11:05

    ご回答、補足いただき、ありがとうございます!

    何ができて、何ができないのか、裏の処理を理解しないとダメな点が大事という理解ができました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • C++に関する質問
  • string G[h]に対して、G[x][y]という1文字ごとの埋め方をするとsegmentation faultになる理由を知りたい