2015-11-22 24 views
5

私はC++の初心者です。現在は文字列を扱っています。 私の質問は、以下のようにスクリプトをコンパイルするときに、インデックス表記を使用すると文字列の文字を取得できますが、coutを使用して文字列自体を取得できないのはなぜですか。私はこのスクリプトを実行するとC++:インデックスで文字列を変更

#include <iostream> 
#include <string> 

using namespace std; 

int main() 
{ 
    string original; // original message 
    string altered; // message with letter-shift 

    original = "abc"; 
    cout << "Original : " << original << endl; // display the original message 

    for(int i = 0; i<original.size(); i++) 
     altered[i] = original[i] + 5; 

    // display altered message 
    cout << altered[0] << " " << altered[1] << " " << altered[2] << endl; 
    cout << "altered : " << altered << endl; 

    return 0; 
} 

は、「変更」列内の文字は、この行で正しく表示されます:

cout << altered[0] << " " << altered[1] << " " << altered[2] << endl; 

しかし、文字列自体はこれで表示されていない これはコードです行:

cout << "altered : " << altered << endl; 

なぜこのようなことが起こりますか。

+1

下記の回答がありますが、プログラムで診断を有効にする方法を調べてみてください。あなたが持っているコードは実際にはバグですが、C++コンパイラが提供する診断モードを使用すると、そのエラーが簡単に検出された可能性があります。 –

+0

今すぐすべての回答を行って、あなたの質問に最もよく答えるものをマークしてください。 –

+0

C++コードは* *スクリプト*(https://en.wikipedia.org/wiki/Scripting_language)ではなく*ソースコード*であることに注意してください。スクリプトは解釈され、C++のソースコードは[コンパイル]されています(https://en.wikipedia.org/wiki/Compiled_language)。 –

答えて

5

あなたは、このようにあなたのコードの展示、ループの前未定義の動作original文字列の長さに合わせてaltered文字列のサイズを変更していない:ループの前にalteredのサイズを変更し、これを修正するには

altered[i] = original[i] + 5; // UB - altered is empty 

を:

altered.resize(original.size()); 

それともalteredに追加するstd::string::operator+=または類似を使用します。

altered += original[i] + 5; 

この方法では、ループの前に空にすることができます。自動的に追加された文字を含むようにサイズが変更されます。


説明

UBがここに起こっている方法は、あなたがしている場合std::string::operator[]は何のチェックもしません(短い文字列を最適化するために使用していますstd::string静的配列にデータを書き込むことに成功しているということですこの配列をstd::string::size()を超えてアクセスします)、std::string::size()0のままで、std::string::begin() == std::string::end()のままです。考慮

cout << altered[0] << " " << altered[1] << " " << altered[2] << endl; 

が、cout << alignedを何も印刷されません、std::stringためoperator<<定義は、このような機能的に見える簡素化:あなたは(UBと、再び)個別データにアクセスすることができる理由です

std::ostream &operator<<(std::ostream &os, std::string const& str) 
{ 
    for(auto it = str.begin(); it != str.end(); ++it) // this loop does not run 
     os << *it; 

    return os; 
} 

std::stringは、その基礎となる配列に行ったことを認識せず、文字列の長さを増やすことを意味していました。

std::transform(original.begin(), original.end(), 
    std::back_inserter(altered), // or altered.begin() if altered was resized to original's length 
    [](char c) 
    { 
     return c + 5; 
    } 

(必要なヘッダ:<algorithm><iterator>)、この変換を行うための<algoritm>道締結する


あなたのプログラムの文字列altered

+1

ありがとうございます。だから私は文字列の最後にそれぞれの文字を追加することを提案したことをすることで、右か? – theodor

+0

またはaltered.resize(original.size())を追加できます。の前に。 –

+0

forループの前にaltered.size()を変更するには、どうでしょうか? ありがとうございます。 – theodor

2

は空です。要素はありません。あなたは

altered[i] = original[i] + 5; 

をやっているよう したがって、あなたはあなたが新しい文字で文字列を追加することができ、文字列の存在しない要素にアクセスするための添字演算子を使用することはできません。これを行うにはいくつかの方法があります。例えば

altered.push_back(original[i] + 5); 

または

altered.append(1, original[i] + 5); 

またはあなたはレンジ・を使用することをお勧めし、その値を割り当てるために、空の文字列のための添字演算子を適用しないことのよう

altered += original[i] + 5; 

なぜなら実際にはインデックス自体が使用されていないからです。例

for (char c : original) altered += c + 5; 
+0

@BenjaminR char型の参照を使用する必要はありません。参考にならずに、コードをさらに効率的にすることができます。 –

+0

@BenjaminRコンパイラがオブジェクトコードを生成する方法を理解できません。 –

+0

@BenjaminR生成されるオブジェクトコードを見てください。 –

1

についてalteredの大きさは常にゼロである - インデックスを使用することによって、あなたは、インデックスでoriginalからalteredに値をコピーしようとしているalteredを持っていません。 LogicStuffによると、これは未定義の動作です。std::stringのインデックスを使用すると、実際にはstd::stringの演算子を呼び出して文字列のdataフィールドにアクセスするため、エラーは発生しません。 []を使用すると、演算子はC++標準での範囲チェックなしと定義されているため、エラーが発生しませんでした。それが「現代のC++」のアプローチですので、altered.size() <= i

は、しかし、私は私の解決策としてこれを与えるつもりだ場合altered.at(i)ではなく範囲エラーがスローされます:インデックスにアクセスするための安全方法はat(i)メソッドを使用することです(短い方と完全な方)。

これは私が上に与えられたものにするだろう代替です:

string original = "abc"; 
string altered = original; 
for (auto& c : altered) c += 5; // ranged for-loop - for each element in original, increase its value by 5 
cout << altered << endl; 

:-)コードの大幅な削減、私はそれをLogicStuffの仕方をしていた場合でも、私はまだだろう

string original = "abc" 
string altered = ""; // this is actually what an empty string should be initialised to. 
for (auto& c : original) altered += (c+5); 

しかし、私は実際にpush_back()と文字列添付/文字列concatenatio方法のため、この方法をお勧めしません。このようにそれを行いますn仕事。この小さな例では問題ありませんが、originalが最初の10ページ分の解析対象の文字列だった場合はどうなりますか?それとも、100万人の生の入力であればどうでしょうか?そしてaltereddataフィールドが限界に達するたびに、システムコールによって再割り当てする必要があり、の内容がコピーされ、dataフィールドの事前割り当てが解放されます。これは、originalのサイズに比例して大きくなる重大なパフォーマンスの障害です。これは悪い習慣です。コピーした文字列を必要に応じて調整することで、完全なコピーを行ってから繰り返し処理する方が効率的です。 std::vectorも同様です。

関連する問題