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

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

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

5回答

1259閲覧

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

ratera

総合スコア54

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/07/08 00:03

知りたいこと

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]で値が標準出力出来て変なことをしている気がする

エラー画面

イメージ説明

コード

c++

1#include <bits/stdc++.h> 2#define rep(i, n) for (int i = 0; i < (n); ++i) 3using namespace std; 4using ll = long long; 5using P = pair<int, int>; 6 7 8int main() { 9 int h, w; 10 cin >> h >> w; 11 12 string G[h]; 13 //for (int i = 0; i < h; i++) cin >> G[i]; 14 15 //入力の生成 16 srand(time(NULL)); 17 for(int y = 0; y < h; ++y) { 18 for(int x = 0; x < w; ++x) { 19 if(rand()%10==0) G[x][y]='.'; 20 else G[x][y]='#'; 21 } 22 } 23 cout << endl; 24 rep(i,h){ 25 rep(j,w) {cout << G[i][j];} 26 cout << endl; 27 } 28}

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

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

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

投稿2020/07/08 00:37

編集2020/07/08 00:39
maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ratera

2020/07/08 01:35

ありがとうございます! []の定義もされているのは知りませんでしたが、読んでいて面白いです。 バッファはあるというご回答と、空文字列(なのでバッファがない?という解釈をしました)というご回答で若干混乱気味ではあります。 (初心者目線ですが、たぶんstringの実装のコードを読めないとダメな気がしており、現段階では「空文字に書き込むのはできない」と覚えたい気持ちにかられております。)
maisumakun

2020/07/08 01:38

> バッファはあるというご回答と、空文字列(なのでバッファがない?という解釈をしました)というご回答で若干混乱気味ではあります。 バッファはあるけど、デフォルトコンストラクタで初期化しただけの状態では長さゼロです。
maisumakun

2020/07/08 01:44 編集

> 初心者目線ですが、たぶんstringの実装のコードを読めないとダメな気がしており いえ、それは「やってはいけません」。C++の規約で決まっている範囲でコードを書いてください。規定されていない実装の詳細に依存したコードを書くと、何かの拍子に実装が変わって動かなくなる危険があります。
ratera

2020/07/08 01:51

あ!バッファはメモリのサイズで、長さは\0(NULL)までの文字列の数ということですね! なので、空文字は最初に\0が来るもの。両立しますね! ちょっとズレている気もしなくもありませんが、解決しました!
guest

0

ベストアンサー

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

C++

1#include <iostream> // cin, cout 2#include <string> // string 3#include <vector> // vector 4#include <cstdlib> // srand, rand 5#include <ctime> // time 6using namespace std; 7 8int main() 9{ 10 int h, w; 11 cin >> h >> w; 12 13 vector<string> G(h); 14 15 srand(time(NULL)); 16 for (int y = 0; y < h; ++y) { 17 G[y].resize(w); 18 for (int x = 0; x < w; ++x) { 19 if (rand() % 10 == 0) G[y][x] = '.'; 20 else G[y][x] = '#'; 21 } 22 } 23 cout << endl; 24 for (int i = 0; i < h; i++) { 25 for (int j = 0; j < w; j++) cout << G[i][j]; 26 cout << endl; 27 } 28}

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

C++

1#include <iostream> 2#include <string> 3using namespace std; 4 5struct String { 6 char *data; 7 uint64_t size; 8 union { 9 char here[16]; 10 uint64_t capacity; 11 }; 12}; 13 14void dump(string &s) 15{ 16 cout << "size() = " << s.size() << ", capacity() = " << s.capacity() 17 << ", s.data() = [" << s.data() << "]\n"; 18 String *p = reinterpret_cast<String *>(&s); 19 20 char *data; 21 uint64_t capacity; 22 if (p->size < 16) data = p->here, capacity = 15; 23 else data = p->data, capacity = p->capacity; 24 cout << "String: size = " << p->size << ", capacity = " << capacity 25 << ", data = [" << data << "]\n\n"; 26} 27 28int main() 29{ 30 string s; 31 dump(s); 32 s = "abc"; 33 dump(s); 34 s = "abcdefghijklmnopqrstuvwxyz"; 35 dump(s); 36}

実行結果

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 00:50

編集2020/07/08 04:06
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ratera

2020/07/08 01: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/
guest

0

string G[h];

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

投稿2020/07/08 00:40

episteme

総合スコア16612

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

episteme

2020/07/08 00:43

カブリマクリー
ratera

2020/07/08 01:39

ご回答沢山で、初心者質問に、非常に申し訳ない気持ちになっています。>< 空文字列だからという発想に至れなかったので、全ての敗因です(クヤシイー) stringを扱う時の代入は気を付けようと思いました。 (配列と同じように参照する際の問題は見えていませんので、ちょっとどう気を付けようか考え中です)
guest

0

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

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


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

C

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

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

投稿2020/07/08 00:40

編集2020/07/08 01:53
ozwk

総合スコア13553

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ratera

2020/07/08 01:41 編集

バッファがあるのですね! てっきり、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 ```
ozwk

2020/07/08 01:54

> G[y][x]のセグフォ コードはG[x][y]になってしまっています
ozwk

2020/07/08 02:08 編集

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

2020/07/09 03:19

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

0

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

投稿2020/07/08 00:11

y_waiwai

総合スコア88042

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2020/07/08 00: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 $
ratera

2020/07/08 02:05

ご回答、補足いただき、ありがとうございます! 何ができて、何ができないのか、裏の処理を理解しないとダメな点が大事という理解ができました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問