Garbage Collection

塵も積もれば山

目次

Blog 利用状況

ニュース

コミケで受けていた通販をすべて発送しました。詳しくはこちらの記事にて
C++とかC#とか数学ネタを投下していく予定です。
それ以外の日々の四方山話を綴った日記はこちら

書庫

日記カテゴリ

[C++]終端のない文字列をうたおう

C言語で文字列を扱うときは、終端ありきでほとんどの関数が構成されています。
たとえば、最大で12文字入る場合は、13文字分のバッファを用意するのが普通です。
ですが、バッファを12文字しか取らず、最大長だった場合は
終端がないという形態で組む場合というのも往々にしてあります。

void foo(){
  //C++だとこの表記駄目らしい
  //char name[5] = "Izumi";
  char name[5] = {'I','z','u','m','i'};
  char message[20] = "hello";
  // NGパターン
  {
    // これだと後ろの文字と連結して10が入ってしまう可能性が!
    int len = strlen(name);
    // 当然これも駄目
    char str[100];
    strcpy(str, name);
    //stringに代入するときも当然アウト
    std::string s = name;
  }
  // OKパターン
  {
    // ちゃんと5がはいる
    int len = strnlen(name, sizeof(name));
    // コピーした先にも終端がつかないことがあるので注意
    char str[100];
    strncpy(str, name, sizeof(str));
    // stringに入れるときはコンストラクタで長さを指定
    std::string s(name, sizeof(name));
  }
}

この辺は簡単なのですが、何より困るのがprintf。
終端なしの文字列を流し込めればなぁ…と思う人に。

void foo(){
  char name[5] = {'I','z','u','m','i'};
  char message[20] = "hello";
  
  // 長さとして小数点以下の値を使って指定
  printf("%-.5s\n", name);
  // 長さを外部から与えたいときは*を使う
  printf("%-.*s\n", sizeof(name), name);
}  

なお、バッファオーバラン対策にもなりますので、
終端ありの場合もこういう造りにしておくと安心です。

#コメントの指摘を受けてソース部分修正

投稿日時 : 2008年8月13日 23:27

Feedback

# re: [C++]終端のない文字列をうたおう 2008/08/13 23:49 774RR

> char name[5] = "Izumi";
は C++ ではコンパイルエラーになりマッスル 8.5.2-2
C89 では規定なかったですな (C99 は未確認)

俺としては終端なし文字列を扱うことに抵抗ないのですが
後輩諸氏に扱わせることには大いに抵抗がありますな。
注意させてもさせても絶対に見落としがありそうで。

# re: [C++]終端のない文字列をうたおう 2008/08/14 0:12 あんどちん

細かいことですが…
上のプログラムの6行目
// これだと後ろの文字と連結して10が入ってしまう!
のコメント。
環境(コンパイラ)依存じゃないですか?ローカル変数がどのように取られるかは。

# re: [C++]終端のない文字列をうたおう 2008/08/14 0:27 出水

>コンパイルエラー
…なんですと!?
まじっすか…まじだぁ、VS2008じゃエラーでてる!

test.cのファイル名にしてC言語モードでのコンパイルではOKですね
たしか、Cの本にはっきりと終端がない文字列として書いていたと思ったんですが
規格として明文化されていないとは意外です

>環境依存
そうなんだけど…まぁその辺はスルー希望

# re: [C++]終端のない文字列をうたおう 2008/08/14 8:05 774RR

JIS X3010:1993 (C89) を再確認してみました。 6.5.7 初期化
|(ワイド)文字列リテラルの連続する文字
|(空きがある場合または配列の大きさを指定しない場合、終端0値コードを含めて)
|がその配列の要素を初期化する
っつーことで「終端文字のない文字の列」になりエラーにならない、でOK
# 終端がないので「文字列」とは言わないのが正しい

俺の先のコメント中の「規定なし」の意図は「エラーにする規定なし」でありました

> std::string s = name;
これは代入ではないので念のため。

タイトル  
名前  
Url
コメント