• ベストアンサー

文字列についていい方法はありますか?

マイコンでプログラムを作っています。 配列aに文字を入力し、それを7セグに表示したいです。 表示自体は出来ています。 なので、文字列を配列に入力する方法を教えてください。 (数字以外を表示します。) まず、現在は char *a[6]; a[0]="12345"; seg(*a);//7セグ表示用関数 このようなソースを書きました。これは希望通りの動作してます。 しかし、例えば a[0] = "12"; a[2] = "345"; や、 a[0] = "12" + "345" のように、文字列どうしをくっつけるような使い方が出来ないものかと悩んでいます。 最悪の場合 char a[5]; a[0] = '1'; a[1] = '2'; a[2] = '3'; a[3] = '4'; a[4] = '5'; と、このような方法を使おうと思ってますが、あまり好きじゃないです。 出来ないのは無知なせいなのか、方法は有るが最後の方法が一番マシなのか、もっといい方法があるなら何を使えばいいのか、等アドバイスをお願いします。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.8

#4,#5です。とりとめなくいろいろ書きます。 > その危険と言うのが > strcpy( a, "1234567" ); > となる事があるようにプログラムを書いてしまった時のみの危険なのか、 こっちです。 文字数が分かっているのならstrcpyよりもmemcpyを使用した方が速いかも しれません。 > memcpy( a, "12345", 5 ); 一般的にライブラリ関数は、エラーチェック等のさまざまなオーバヘッドが 含まれるものなので、 スピードを気にするならエラーチェック無しの自作のシンプルな代替関数を 作って速度比較すると良いかもしれません。 #2さん回答のお礼部分を見ると、 「char a[ 6 ];」と「char *a[ 6 ];」の違いが分かっていないような気がします。 二者は明らかにデータ構造が違います。 > 俺も良く分からないんですが、例えば > a[0] = "12"; > a[2] = "345"; > printf("%s",*a); > とやると実行結果は > 12 > となってしまい、345が表示されません。 > > マイコンの場合は > 12??? > と、?の部分には何が入るか分からないような状態になってしまいます。(とりあえず今やったの > でした。) > > たぶんですが、a[0]に"12"のアドレスを渡しただけなので、seg()が受け取った配列にはa[0]の先 > スから5文字分が入力されていて、"345"が行方不明になってるんじゃないかと思います。 これは「char *a[6];」の場合で、「printf( "%s", a[2] );」とすると"345"が表示されると思います。 a[0] → "12" a[1] → 不明 a[2] → "345" a[3] → 不明 a[4] → 不明 a[5] → 不明 です。 やはり意図しているのは、「char a[6];」のほうで、 a[0] → '1' a[1] → '2' a[2] → '3' a[3] → '4' a[4] → '5' a[5] → '\0' こういう構造を望んでいるんじゃないかという気がします。

taunamlz
質問者

お礼

ありがとうございます。 memcpyを使えばやりたい事が出来ました。 速度もstrcpyとstrcatに比べるとかなり速いのでこれで行こうと思います。 memcpy(a,"12345",5);//9クロック memcpy(&a[0],"1",1);memcpy(&a[1],"234",3);memcpy(&a[4],"5",1);//25クロック strcpy(a,"1");//32クロック strcat(a,"2");//62クロック >「char a[ 6 ];」と「char *a[ 6 ];」の違いが分かっていないような気がします。 違いが分かっていませんでした。 と言う事は、「char *a[6];」でやってた事は「char *a;」でも同じ動作をしてたと言う事ですね。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (11)

回答No.12

#4,#5,#8,#11です。本題からそれちゃってすみません。 推測ですが、組み込みのmemcpyは以下のような方法(の組み合わせ)で コンパイラが最適化(高速化)しているのではないかと思います。 (1)関数のインライン展開 関数呼び出し(call)せずに、呼び出し部分に処理を直接展開することで、 関数呼び出しによるオーバヘッドが無くなります。(あまり差は出ないですが) (2)ループ展開 ループ回数が少なく、かつ固定回数の場合、ループ展開してしまうことが 出来ます。memcpyの場合、以下のようにコンパイラが展開してしまうことで 高速化できます。 (これは、ループ回数を、大きい数にしたり、変数で与えたりすることで 無効になるかもしれません。) |memcpy( a, "12345", 5 ); ↓ |a[0] = '1'; |a[1] = '2'; |a[2] = '3'; |a[3] = '4'; |a[4] = '5'; (3)アセンブラのメモリ転送命令に置き換え memcpyに対応するアセンブラのメモリ転送命令があるのかもしれません。

全文を見る
すると、全ての回答が全文表示されます。
回答No.11

#4,#5,#8です。 > >「char a[ 6 ];」と「char *a[ 6 ];」の違いが分かっていないような気がします。 > 違いが分かっていませんでした。 > と言う事は、「char *a[6];」でやってた事は「char *a;」でも同じ動作をしてたと言う事ですね。 そうです。 #8で書いた、 > 一般的にライブラリ関数は、エラーチェック等のさまざまなオーバヘッドが > 含まれるものなので、 > スピードを気にするならエラーチェック無しの自作のシンプルな代替関数を > 作って速度比較すると良いかもしれません。 ですが、 ちょっと作ってみました。 void my_memcpy( char* dst, char* src, int count ) { while ( --count >= 0 ) { *dst++ = *src++; } } int main( void ){ char *a = "0123456789"; char b[ 10 ]; my_memcpy( b, &a[ 2 ], 3 ); printf( "a=%.10s\n", a ); printf( "b=%.10s\n", b ); return 0; }

taunamlz
質問者

お礼

ありがとうございます。 作っていただいたmy_memcpyを試してみました。 すると memcpy(a,"1",1);//5クロック my_memcpy(a,"1",1); //45クロック my_memcpy(a,"12345",5); //145クロック となり、strcpyよりも遅いという結果になってしまいました。 my_memcpyのソースを見て思ったんですが、memcpyの5クロックと言うのはc言語の関数の限界を超えていると思います。 実際もっと細かく見てみても mainから関数へジャンプ→4クロック 関数からmainへジャンプ→4クロック と、これだけで既にmemcpyに負けてます。 コンパイラが悪いのか、memcpyがすごいのか、ハード的に特殊な処理をしてるのか分かりませんが、memcpyより早くするのは難しそう(アセンブラで書けば良い?)と言う事がわかりました。

全文を見る
すると、全ての回答が全文表示されます。
回答No.10

#7です。 >初期化しておくと何かメリットがあるんでしょうか? char *a[6]; は文字列6個のアドレスを確保したという意味ですから。文字列領域を確保したわけではありません。宣言と同時に初期化をすると、その文字数で領域を確保してくれます。strcpyは確保されている文字数を超えてのコピーは保障してません。エラーの原因となります。 いっそのこと、char a[6][10];とでもしておけば、9文字(1文字は文字列の終了'\0'で使用)の文字列を6個確保したことになります。各文字列はa[0],a[2],・・・a[5]で使えますので、strcpyなりstrcatなり9文字以内なら自由に使えます。他の方も指摘してますがポインタ操作は気をつけないとエラーの原因となります。以上です。

taunamlz
質問者

お礼

ありがとうございます。 >文字列6個のアドレスを確保した と >文字列領域を確保した をごっちゃにしてたみたいです。 char a[6][10]; だと、今後表示のバリエーションが増える、とメモリ不足になりそうなので、今回は char a[6]; で行こうと思います。

全文を見る
すると、全ての回答が全文表示されます。
noname#168973
noname#168973
回答No.9

こんばんわ。 私も質問者さんは、ポインタと配列の事、少々誤解されているように思いますが。それはさておき... もし、表示される文字列がコンパイル時で定数なら、何も a を介す必要は無いのではと思います。seg("12"); や seg("345"); で良いじゃないでしょうか? これなら コンパイル時の演算と言うことで seg("12" "345"); とすば、"12345" と結合もしてくれますよ。 もし、実行時に動的に文字列を構築したいなら、他の方も仰せの通り、strXXX()系か、Xprintf()系の関数を使うのが簡単ですが、質問者さんも仰せの通り、組み込み関係では Xprintf()系自体実装されてない場合もあるようですね。 で、そうなると自分で文字列を構築する関数を用意すると言うのが多分妥当かと思うのですが、真面目に Xprintf() 系の実装なんかはやってられないと思います。 ですが、もしも構築される文字列が数字列に限定されるならこんな安直な実装は如何でしょうか? -- char *to_str(char *dest /* 6byte 以上あること */, int val) {  char *p;  int d, n;  for (p = dest, d = 10000 /* 5桁限定 */ ; d > 0; d /= 10)  {   n = val / d;   *(p++) = (char)n + '0';   val -= n * d;  }  *dest = '\0';  return dest; } -- こうすれば、char a[6]; とでもしておいて、seg(to_str(a, 123)); とかで、 "123" が得られると思います。

taunamlz
質問者

お礼

ありがとうございます。 >表示される文字列がコンパイル時で定数なら、何も a を介す必要は無いのではと思います。 定数ではない場合と、定数の場合があるんです。 全点灯「88888」 バージョン表示「-1.00-」 エラー「E-00 」~「E-99 」 等です。 バージョンとエラーを同じ関数で走らせたいなと思って質問しました。 数字だけの表示は既に作ったのですが、例に挙げていただいたプログラムの方が短いので、参考にさせていただきたいと思います。

全文を見る
すると、全ての回答が全文表示されます。
回答No.7

初期化で6つの文字列が代入できます。最大文字数で仮に入れておいたらどうですか。 #include<stdio.h> #include<string.h> int main() { char *a[6]={"12","345"}; printf("%s\n",a[0]); printf("%s\n",a[1]); //連結 strcat(a[0],a[1]); printf("%s\n",a[0]); return 0; }

taunamlz
質問者

お礼

ありがとうございます。 >最大文字数で仮に入れておいたらどうですか。 初期化しておくと何かメリットがあるんでしょうか? strcatとstrcpyで何とかなりそうなんですが、初期値を入れておいたほうがいいのであれば入れておこうと思います。

全文を見る
すると、全ての回答が全文表示されます。
noname#22058
noname#22058
回答No.6

#2です。 文字列の連結には、sprintf関数を使います。 #include <stdio.h> int main(void) { char *a[] = { "12", "", "345" }; char str[20]; sprintf(str, "%s%s", a[0], a[2]); printf("%s\n", str); return 0; }

taunamlz
質問者

お礼

ありがとうございます。 sprintf関数はBC++で動かしたら動きましたが、マイコンではコンパイルができませんでした。 原因がちょっと分からないのですが、どうもRAMが足りなくなっているような雰囲気です。 sprintf関数自体はまさに求めていた動作をしていたのでとても残念ですが、これを使うのは諦めようと思います。

全文を見る
すると、全ての回答が全文表示されます。
回答No.5

#4です。補足です。 「char a[6];」で正しいとしたら、ソースはこのようになります。 > char a[6]; > strcpy( a, "12345" ); > seg(a);//7セグ表示用関数 厳密にいうと「strcpy」は、配列境界を越えてしまう危険があり、 それを避けるためには、このように置き換えた方が良いです。 > strncpy( a, "12345", sizeof( a ) ); > a[ sizeof( a ) - 1 ] = '\0';

taunamlz
質問者

お礼

ありがとうございます。 とりあえずstrcpyとstrcatを使ってやろうと思います。 そこで一つ質問があります。「配列境界を越えてしまう危険」というのは、ソースの方で5文字を越えないように注意して作ったとしても超えてしまう事があるということでしょうか? 出来れば動作時間の節約、ソースの見易さ、RAM・ROMの節約の為に、ソースで注意すれば何とかなるならやらない方向で行きたいです。 文字列を入力するという事は今のところ無く、今後も無いであろうと思います。 現在の状態を表示する時などに使う予定なので、5文字以上にしないつもりです。(というか、7セグが5桁しかないので必然的にそうする事になります。) その危険と言うのが strcpy( a, "1234567" ); となる事があるようにプログラムを書いてしまった時のみの危険なのか、 strcpy( a, "12345" ); としていても、時々おかしな動作になってしまうほどの危険なのかが知りたいです。 よろしくお願いします。

全文を見る
すると、全ての回答が全文表示されます。
回答No.4

> まず、現在は > char *a[6]; > a[0]="12345"; > seg(*a);//7セグ表示用関数 > このようなソースを書きました。これは希望通りの動作してます。 「char *a[6];」では、ポインタの配列を宣言していますがそれは意図どおりですか? (char型変数を指すポインタ変数が、配列で6個宣言されています。 ) もしかしたら、こうじゃないでしょうか? > char a[6]; (char型変数が、配列で6個宣言されます。) このようにすれば、質問の後半部分も、普通に質問者さんの意図通りに行くんじゃないでしょうか?

taunamlz
質問者

お礼

ありがとうございます。 意図通りというか、ポインタにしないと a="12345"; がエラーになってしまったので、とりあえずポインタにしてみたという感じです。 error: a value of type "char *" cannot be assigned to an entity of type "char" こんなエラーが出てしまいます。 Borland C++でもやってみてるんですが、同様に 「移植性の無いポインタ変換(関数 main)」と言うエラーが出ます。

全文を見る
すると、全ての回答が全文表示されます。
  • chirubou
  • ベストアンサー率37% (189/502)
回答No.3

私だったら、 void segall( char *str ) { while( str != NULL ) seg( str++ ); } として、 char *mojiretu[] = { "hoge", "moge", NULL }; segall( mojiretu ); とします。これで "hogemoge" というように表示されるハズです。 こういうことがやりたかったのかな?

taunamlz
質問者

お礼

ありがとうございます。 とりあえずやってみたのですがうまく動きませんでした。 で、ソースを見る限りだとcharで宣言と同時に文字列を入力してしまっているのでちょっと違うかな?と言う印象です。 ただ、どうにか解読して、少なくとも"hogemoge"と表示させてみたいと思います。

全文を見る
すると、全ての回答が全文表示されます。
noname#22058
noname#22058
回答No.2

> a[0]="12345"; > seg(*a);//7セグ表示用関数 これが正しく動くのであれば、 > a[0] = "12"; > a[2] = "345"; これも、seg関数の引数として適切な内容を渡せば 正しく動くのではないでしょうか? 引用した2箇所の違いがよくわかりません。

taunamlz
質問者

お礼

ありがとうございます。 俺も良く分からないんですが、例えば a[0] = "12"; a[2] = "345"; printf("%s",*a); とやると実行結果は 12 となってしまい、345が表示されません。 マイコンの場合は 12??? と、?の部分には何が入るか分からないような状態になってしまいます。(とりあえず今やったのだと12003でした。) たぶんですが、a[0]に"12"のアドレスを渡しただけなので、seg()が受け取った配列にはa[0]の先頭のアドレスから5文字分が入力されていて、"345"が行方不明になってるんじゃないかと思います。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 文字列の入れ替え

    2つのchar型配列x[],y[]に格納された文字列を入れ替えるプログラムを作成せよ。ただし入替え処理には、関数swap_array(char [],char [])を作成して用いること。という問題なのですが、下記の実行例では、2つの文字列xとyをキーボードから入力し、入れ替えた後に表示している。 <実行例> % ./irekae 文字列x (20字まで)? Mojiretsu 文字列y (20字まで)? Irekae 関数swap_array()を呼び出しました 入替後の x: "Irekae" 入替後の y: "Mojiretsu" % となっているのですが文字列を格納するところまでしかわかりません。 どなたか教えていただけないでしょうか? #include<stdio.h> int main(void) { char x[21]; char y[21]; printf("文字列 (20字まで)? "); scanf("%s", x); printf("文字列 (20字まで)? "); scanf("%s", y);

  • 文字列を配列化する方法を教えてください!

    1列の文字列を配列化する方法を教えてください。 メモリの確保らしいのですが、参考になるソースが拝見できれば嬉しく・・・main から参照したいです。 char Text[] = "AAAA\nBBBB\nCCCCCCCC\nDDD\n";  ↓ char str[0] = "AAAA";    str[1] = "BBBB";    str[2] = "CCCCCCCCC";    str[3] = "DDD"; どうやったらいいのか全くイメージがわかないので、 よろしく御願いします!

  • 文字列の問題

    入力された文字列(char型配列に格納)について、1文字ずつその文字の種類を表示し、最後に文字数を表示するプログラムを作成せよ。文字の種類は、大文字、小文字、数字、その他の文字の4種類とすること。 という問題なのですが、実行例は↓になっています。 % ./mojiretsu 文字列(20文字まで)? Char-1 ←文字列を入力 1文字目は C で、 大文字です。 2文字目は h で、 小文字です。 3文字目は a で、 小文字です。 4文字目は r で、 小文字です。 5文字目は - で、 その他の文字です。 6文字目は 1 で、 数字です。 以上6文字です。 % ですが↓から先をどう書いたら良いのかわかりません。どなたか教えてください。 #include<stdio.h> main() { char ch[20]; printf("文字列(20文字まで)? "); scanf("%s", ch);

  • C#で文字列から数値だけ取得する方法

    C#で文字列から数値だけ取得する方法 stringの文字列をcharの文字配列に入れなおして 数値の場合だけ、別の配列(例:a[3])にコピーする。 そんな方法で数値を抜き出そうかと思っています ですが、文字列から数値だけを見つけ出して取得する方法がわかりません。 詳しくのっているサイトなどありましたら 教えてください…!!

  • 文字列扱い方

    構造体の中の配列へ文字列を入れる方法がわからなくてこまっています! #include <stdio.h> struct TEST{     char str[32]; }; int main(){     TEST test;     test.str = "test";     return 0; } このように入力するとconst char [5]' から 'char [32]' に変換できません。と出てしまいます。 何か良い方法がありましたらぜひご教授お願いいたします。

  • 文字列の結合、改行、空白削除

    複数のセルを参照して文字列を結合し、2つずつでCHAR(10)で改行し、未入力のセルの空白を削除する方法はありませんか? =SUBSTITUTE(TRIM(A1&" "&B1&CHAR(10)&A2&" "&B2&CHAR(10)&A3&" "&B3&CHAR(10) &A4&" "&B4&CHAR(10)&A5&" "&B5),"","") 現在、上記のような数式を入れて一つのセルに表示させています。 しかし、これだと後半のセルが未入力の場合、空白で表示されるため、表示させるセルの中で結合した文字列が上の方に行ってしまいます。 表示させるセルは縦横共に中央揃えにしたいのですが、何かいい方法はありませんか? まだまだ初心者ですが、いろいろ調べた結果、とりあえず上記の式にたどり着きました。 マクロではなく関数でなんとかなればと考えております。 分かりにくい文章になってしまったかもしれませんが、よろしくお願いします。

  • 実数→文字列の方法

    文字列を実数に変換するのに関数atofを使いますが、それとは逆に、 プログラム内で算出された実数を文字列にどう変換したらよいのでしょうか? 算出された実数を配列Xに納め、それをfopenで書き込み用として開いてるファイル(ポインタ名はfp)に fputs(x,fp) としたら error: cannot convert `double*' to `const char*' for argument `1' to `int fputs(const char*, FILE*)' と表示されてしまいました。実数を文字列に変換してからでないとダメかと思っています。

  • 【c++】文字列の操作

    お世話になります。 テキストの操作について質問があります。 getline関数を使用してテキストの1行分を読み込み その文字列をstrtok関数を使用し区切り文字ごとに配列に入れたいと考えています。 ですがgetlineはstd::string型の変数が必要でstrtok関数はchar型しか受け付けないため 関数同士で型が合わず困っています。 何とか型を合わせる方法は無いでしょうか。 以下にソースコードを記載しますのでご指導お願いします。 int main(){ using namespace std; string strText; char chArray[100]; ifstream fs("test.txt"); //パスで指定されたファイルから1行分の文字列を取得する while( getline(fs , strText , '\n') ){ //区切りごと配列に入れたい chArray = strtok(strText , ","); } }

  • 文字列を表すための配列とポインタ

    文字列を表すための配列とポインタ  配列とポインタは同様に扱えるもの、と思って、次のプログラムを作りました。処理系は、Visual Studio 2010 コマンドプロンプトです。 #include <stdio.h> void main(void) { char a[256]; char *b; printf("文字列を入力してください。\n"); printf("例「abcde」\n\n"); printf("配列型文字列を使います。\n"); scanf("%s", a); printf("文字列は%sです。\n\n", a); printf("ポインタ型文字列を使います。\n"); scanf("%s", b); printf("文字列は%sです。\n", b); }  すると、まずコンパイル時に、 「warning C4700: 初期化されていないローカル変数'b'が使用されます」 と表示されました。そして、実行すると、「配列型文字列」の方は問題ないのですが、「ポインタ型文字列」の方の実行後に、 「x.exeは動作を停止しました。 問題が発生したため、プログラムが正しく動作しなくなりま した。プログラムは閉じられ、解決策がある場合は Windowsから通知されます。」 と表示され、エラーとして終了してしまいます。 「char *b;」 と宣言するところが問題のようですが、なぜなのかが分かりません。どなたか、解説をお願いします。

  • 文字列

    ・文字列をキーボードから入力する関数を作成する。 書式:char *StrInp(char *pDefStr, int nLen); 引数:char *pDefStf; 初期文字列 int nLen; 入力可能文字数(1~79) 戻り値:正常ならば、入力した文字列の先頭ポインタ、エラー時はNULL。 処理:pDefStrに与えた文字列を初期値とする文字入力を行う。    nLenで指定した文字数まで入力可能とし、その範囲は1~79    までする。入力時の初期カーソル位置は与えた文字列の最後    になります。初期文字列が必要ない場合はヌル文字を与えます。    初期文字列を与えられた場合は、その文字列も更新可能とする。   ・入力の終了は「リターン」キーとする。   ・「BS」キーを押すと、カーソルの1文字前の文字前の文字を    消去する。 という、問いです。難しくてわかりません。どなたかたすけてください。