• ベストアンサー

C言語 変換指定%sについてです。

現場での経験もあるPGなのですが、C言語の基礎を復習していたら疑問に思うことが出てきてしまったので、質問させて下さい。 printf関数などに使われる変換指定%sについてですが、 char word[] = "test"; char *pointer; pointer = word; とした場合、pointerの値は文字列wordの先頭アドレスになるので、 printf ("%x", pointer); とすれば、そのアドレス値が表示されるのはとてもよくわかるのですが、 printf ("%s", pointer); とした場合に、"test"と表示されるのがイマイチ納得できないんです。 printf ("%s", *pointer); なら、まだわかるんですけど・・・ 変換指定の%sというものは、 「アドレスを受け取って、受け取ったアドレスにある文字列を\0がくるまで表示する」 というものなのでしょうか? int型のポインタで同じように printf ("%d", pointer); とすると、pointerの値であるアドレスが10進数表示されて、pointerが指している変数の値を表示するには、 printf ("%d", *pointer); としなければならないわけで、そういうことをいろいろ考えていたら、収拾がつかなくなってしまって(^_^;) 「とにかく%sはそういうものなの!!」と丸暗記すれば困るようなことはないのですが、どうにもモヤモヤしっぱなしなので、%sの動きについて詳しくお分かりの方がいらっしゃいましたら、ご教授下さい。 よろしくお願いしますm(__)m

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

  • ベストアンサー
  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.4

C言語の規格上では。 %につづく変換指定子sは空白類でない文字の並びに相当し、 長さ修飾が存在しない場合、対応する実引数は文字型配列の 先頭要素へのポインタで無ければならない 配列内の終端ナル文字の直前まで書き込み精度が指定された場合、 精度を超える書き込みは行わない。精度が指定されない場合、 または精度が配列より大きい場合その配列はナル文字を含まなければ ならないとされています。 つまり配列より小さい精度が指定されなければ受け取ったポインタを 文字配列の先頭要素とし\0が出てくるまで表示します。

その他の回答 (8)

  • 1839cc
  • ベストアンサー率54% (12/22)
回答No.9

> かなり特殊な変換指定だったんですね。 私の説明が分かりづらかったかもしれません。 「%sは、なんら特殊ではない」という結論に行き着いて欲しかったのですが・・・ char* という型は、正確には文字列を指し示すものではなく、文字を指し示すものです。 ですので、*pointer と記述してしまうと、「文字列」ではなくて「文字」になってしまうんですね。 つまり "test" の先頭文字である 't' です。 't' とだけ言われても、printf は "test" と表示することはもちろん出来ません。 > puts関数の自作はかなり難しそうな気がするんですけど、とりあえず頑張ってみます(^_^;) まずは、「文字」「文字列」「配列」「ポインタ」について復習してみてください。 基礎的な関数ですので、自作できることよりも、仕組みを理解することが重要だと思います。

tadasuke2002
質問者

お礼

再度のご返答ありがとうございます。 No.8さんへのご返答にも書いたのですが、%s以外の変換指定がその値そのもののビットの並びをint型などに変換するのに対して、%sはその値のアドレスが指しているビットの並びを変換しているという部分が、少し疑問に感じてしまったんです。 いろいろとご教授くださってありがとうございました。 基礎的な部分にはなるのですが、これからも勉強してみます。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.8

C言語で『文字列』はどう定義しますか? ・ここが理解できれば  『char word[] = "test";』  と  『char *pointer = word;』  が同じ文字列として扱えることが分かりますけど。 ・『word』は char 型の配列ですが、配列名は実はアドレスを意味しています。  よって『pointer』が char 型のポインタ(アドレス)を持っていれば char 型の配列と  同じような扱いが出来るということです。  だから配列とポインタは別物でも似たような使い方が出来るのです。 例えば: char word[] = "test"; char *pointer = word; // 配列の場合 word[0]⇒'t' word[1]⇒'e' word[2]⇒'s' word[3]⇒'t' // ポインタの場合 pointer[0]⇒'t' pointer[1]⇒'e' pointer[2]⇒'s' pointer[3]⇒'t' 上記の2つは同じようにアクセスできるのです。 なぜ、ポインタなのに [ ] 演算子でも配列と同じようにアクセスできるか分かりますか? ここがポイントですね。これを理解するには文字列の表現と配列とポインタを十分に理解 しないと駄目でしょうな。 ちなみにポインタには *(pointer + 0)⇒'t' *(pointer + 1)⇒'e' *(pointer + 2)⇒'s' *(pointer + 3)⇒'t' としてもアクセス出来ます。 これは記述こそ違いますが [ ] 演算子と同じことですから。 余談: printf( "test[0] = '%c'\n", "test"[0] ); printf( "test[1] = '%c'\n", "test"[1] ); printf( "test[2] = '%c'\n", "test"[2] ); printf( "test[3] = '%c'\n", "test"[3] ); ↑ この実行結果を簡単に予想できますか? もし正しく予想できたらこの問題を質問しなくても良かったはずです。 最後に: ・『%s』は char 型配列を NULL 文字で終わる文字列だと仮定して処理します。  よって配列を受け取る   ↓  アドレス(ポインタ)を受け取る   ↓  文字列のポインタでも受け取れる(正しく)   ↓  ちゃんと文字列として表示も出来る    となります。  決して『%s』が特別だったわけではありません。  重要なのは『配列名はアドレス(ポインタ)を意味する』を理解していなかっただけです。 ・以上。

tadasuke2002
質問者

お礼

ご返答ありがとうございます。 配列とポインタについては理解していたつもりだったんですけど(^_^;) ただ、他の変換指定、例えば%dなどは、その値(ビットの並び)をint型に変換するものなのに対して、%sはその値をアドレスだと認識して、そのアドレスにある値を\0が出現するまで文字列として変換するという、他の変換指定とは違う動きをしている部分が疑問に感じてしまって。 それが、Cの規格としてどのように定義されているのかを知りたかったんです。 わかりにくい質問ですみませんでしたm(__)m

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.7

>そもそも C に「文字列」なんてものは存在しない 文字列も一応規格に用語が以下のように定義されています。 最初のナル文字で終わり、かつそれを含む連続した文字の並びを「文字列」という。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.6

って~か, そもそも C に「文字列」なんてものは存在しない (いや, 規格には「文字列リテラル」ってあるけど...). あれは「'\0' で終わる文字の配列」だ. で, 「配列名」は一部の場面を除いて全て「その配列の先頭要素をさすポインタ」に変換される. だから, pointer = word; を実行すると pointer の値は「配列 word の先頭要素のアドレス」になる. で, printf("%s\n", word); の printf の第2引数は配列名なので「その配列の先頭要素をさすポインタ」に変換される. つまり「配列 word の先頭要素のアドレス」である. 一方, printf("%s\n", pointer); における printf の第2引数は pointer であるが, その値は (上の代入により) 「配列 word の先頭要素のアドレス」である. だから, この 2つは同じ結果でなければならない. ちなみにポインタの値を %p 以外で変換するのは未定義動作なので, 「突然鼻歌を歌いだす」「どこかの原発をメルトダウンさせる」ということがあったとしても規格は一切関知しない.

tadasuke2002
質問者

お礼

配列名がその配列の先頭アドレスを指すことまではわかっていたんですけど、「"%s"がそのアドレスの指す値から\0が出てくるまでを文字列にして表示する」なんて処理をやってくださっているというのが、はっきりわかっていなくて。 今まで何気なく使っていた%sさんに感謝することにします(^^ゞ ご返答ありがとうございました。

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.5

#ANo.4です。 自分の書き込みで何が言いたいか分かりにくかったので補足。。。  char str[]= "test";  char* pointer;  pointer = str;  printf ("%s", *pointer); ・上記の場合の「*pointer」は既にC規格からみて  「文字型配列の先頭要素へのポインタ」にはならない。 ・精度を指定すれば、\0まで表示されない事もある。  (精度とはprintf( "%10s", str)などの10のこと) などなど。

tadasuke2002
質問者

お礼

規格として、「%sはアドレスを受け取るもの」だったんですね。 今まで何となく使っていたんですけど、こうしてはっきりわかると安心しました。 ご返答、ありがとうございました。

  • 1839cc
  • ベストアンサー率54% (12/22)
回答No.3

そういうものです。 ためしにご自分で puts 関数を作ってみればよくお分かり頂けると思います。 内部で使っていい関数は putchar だけとし、stdout に対して出力してみてください。 (*pointer) の型は char 型なので、引数は char 型にして見ましょう。 その場合、どのように二文字目以降を表示すればよいのでしょうか。 実現できない事がお分かり頂けると思います。

tadasuke2002
質問者

お礼

やっぱりそうですよね。 %sは参考書などでかなり最初のほうから出てきますし、特に説明もないのであまり深く考えていなかったんですけど、かなり特殊な変換指定だったんですね。 これからは意識して使ってみることにします。 puts関数の自作はかなり難しそうな気がするんですけど、とりあえず頑張ってみます(^_^;) ご返答ありがとうございました。

  • php504
  • ベストアンサー率42% (926/2160)
回答No.2

質問の意味取り違えてたかな *pointerはあくまでその型のサイズ1個分の内容でしかありえないですよね

  • php504
  • ベストアンサー率42% (926/2160)
回答No.1

文字列は特殊と考えるほかないでしょう char型のポインターchar *pointer;には 文字 printf ("%c", *pointer); 数字 printf ("%hhu", *pointer); とか用意してありますので

tadasuke2002
質問者

お礼

ご返答ありがとうございます。 やっぱり、そう考えるしかないですよね(^_^;) %hhuというのは初めて知りまして、実際にプログラムを書いて動かしてみたんですけど、これは文字コードを表示する変換指定なんですか?

関連するQ&A

  • C言語 ファイルの意味

    C言語 文字列で分からない部分があるのでご教示お願いします。 内容は「文字列の検索」です。 #include<stdio.h> #include<string.h> int main(void) { char word[] = "abcabdabe"; char word2[] = "abd"; char *word3 = "ABD"; char *word4 = NULL; printf("処理前の文字列:%s\n", word); word4 = word; word4 = strchr(word4, *word2); while (word4 != NULL){ printf("'a'が見つかった場所からの文字列: %s<\n", word4); if (strncmp(word4, word2, strlen(word2)) == 0) { strncpy(word4, word3, strlen(word3)); } word4 += 1; word4 = strchr(word4, *word2); } printf("処理後のword4: %s\n", word); return 0; } このプログラムの場合、word3とword4ではなぜ、*word3, *word4とポインタで設定するのでしょうか? また、「word4 = strchr(word4, *word2)」と *word2のようにlここでポインタ指定するのは何故なのでしょうか?

  • C言語

    (a) キーボードから入力された文字列をそのままディスプレイに表示するプログラムを作成しなさい。 という問題 #include<stdio.h> main() { char word[1000]; /*文字型の変数の宣言*/ scanf("%s", word); /*キーボードから文字列を入力*/ printf("%s\n", word); /*入力した文字列を出力*/ } と作りましたがこれではコンソール中で文字を打ってからエンターを押さないといけないからといわれ再提出になってしまいました。 エンターを押さないでそのまま出力するということは、できるのでしょうか? あと自分の作ったプログラムではスペースや改行を使うことができないから使えるようにしろと言われてましたがそれわできますか? 変換仕様をかえればいいのですか ほかにもかえる所はありますか?

  • C言語について教えてください。

    #include <string.h> #include <stdio.h> void print_all_char(char *str) { while(*str! = '\0') { printf("%c\n", *str); str++; } } int main(void) { char astr[5]; char *pstr; strcpy(astr, "ABCD"); pstr = "EFGHI"; puts("「配列で実現する文字列」を表示する"); puts(astr); puts("「配列で実現する文字列」のすべての文字を表示する"); print_all_char(astr); puts("「ポインタで実現する文字列」を表示する"); puts(pstr); puts("「ポインタで実現する文字列」のすべての文字を表示する"); print_all_char(pstr); return 0; } 自分でつくった上のプログラムではエラーが発生します。 なぜだか分かりません。教えてください。 ちなみに、実行結果は 「配列で実現する文字列」を表示する ABCD 「配列で実現する文字列」のすべての文字を表示する A B C D 「ポインタで実現する文字列」を表示する EFGHI 「ポインタで実現する文字列」のすべての文字を表示する E F G H I のようにしたいです。 よろしくお願いします。

  • C言語 説明文

    C言語(文字列のコピーについて) /* 文字列のコピーを行う関数の実現例 動作原理のコメントを書き入れる */ #include <stdio.h> /*-----文字列のコピー(1)-----*/ char *scpy1 (char *d, const char *s) { int i = 0; while ((d[i] = s[i]) != '\0') { i++; } return(d); } /*-----文字列のコピー(2)-----*/ char *scpy2 (char *d, const char *s) { char *p = d; while ((*d = *s) != '\0') { d++; s++; } return(p); } /*-----文字列のコピー(3)-----*/ char *scpy3 (char *d, const char *s) { char *p = d; while ((*d++ = *s++) != '\0') { ; } return(p); } /*-----文字列のコピー(4)-----*/ char *scpy4 (char *d, const char *s) { char *p = d; while (*d++ = *s++) { ; } return(p); } int main(void) { char astr[] = "ABC"; char bstr[8], cstr[8], dstr[8],estr[8]; scpy1 (bstr, astr); scpy2 (cstr, astr); scpy3 (dstr, astr); scpy4 (estr, astr); printf("astr = %s\n" , astr); printf("bstr = %s\n" , bstr); printf("cstr = %s\n" , cstr); printf("dstr = %s\n" , dstr); printf("estr = %s\n" , estr); return (0); } ----------------------- このプログラムに、この行で何をやっているのかわかるように 一言くらいの説明文を入れる問題です。 ところどころはわかるのですが、わからないところの方が多いです。 プログラムが長くて大変かとは思いますが、どうかよろしくお願いいたします。 このプログラム自体はちゃんとコンパイルでき、実行もできたので おそらく間違いないと思います。

  • %sの使い方について教えてください。

    %sの使い方について教えてください。 char c[4] = "abc" printf("%s",c); この関数は%sでcのアドレス(&c[0])を読み込み、stdoutにそのアドレスを書込み、文字列abcが表示されると認識しています。 printf("%s",stdin); とすればstdinのアドレスがstdoutに書き込まれて標準入力が表示されると思っていたのですが、実際はprintf("%s",*stdin)としなければ出力されませんなぜでしょうか? 回答をよろしくお願いします。 もう一つ別件なのですが、printf("%d",sizeof *stdin); と入力すると、32と出力されます。 なので、stdinは32バイトの大きさだと認識したのですが、char型の文字が32文字以上を書き込むことができます。(scanf()で入力が失敗して32文字以上の文字をバッファに保管できるということです。) これはどういうことでしょうか、分かる方是非、回答をよろしくお願いします。

  • C言語の質問です><

    C言語の質問です>< 次のような実行結果が得られるプログラミングをしたいのですが・・ ちなみにポインタや標準関数のstrシリーズは使用不可です。 文字列1:ABCDEFGHIJ 開始位置:0 文字数:3 文字列2:ABC 開始位置が7で文字数が5とかの場合は'¥0'の位置まで表示するようにしたいのですが、自分の以下のプログラムだと開始位置が0で文字が3だとABCと表示できるのですが、開始位置が7で文字数が5とかだとできません>< #include<stdio.h> void main(void) { char m1[]="ABCDEFGHIL"; char m2[11]; int i,j,start,mozikazu; printf("文字列1:%s\n",m1); printf("開始位置:"); scanf("%d",&start); if(start >=0 && start <11) { printf("文字数:"); scanf("%d",&mozikazu); } if((start+mozikazu)<11) { for(i=0;i<mozikazu;i++) { m2[i]=m1[i]+start; } m2[i]='\0'; } printf("文字列2:%s\n",m2); return; } どうか教えてください><

  • \"%s\"の使いかたについて

    C言語のプログラムで例えば、 #include<stdio.h> #include<string.h> main() { char word[]="ura"; int i=2; printf("文字列\"%s\"は%d個\n",word,i); } というプログラムがあった場合、printf("文字列"%s"は%d個\n",word,i);では、エラーが出ます。なぜ、\が必要なのですか?使いかたを教えて下さい。

  • 次の問題でC言語で、どうプログラムを作れば・・・・

    次の問題でC言語で、どうプログラムを作ればよいか教えていただけないでしょうか。。。 printfの表示処理を関数化してください。 関数名: VOID fc_str_Printf( CHAR *str, BYTE hst ) 機能: (1)strにセットした文字列を表示する (2)CALLされるたびに文字列を記録しておき、  hstに指定された過去の文字列もstrと合わせて表示する  ※過去分以上の値を設定された場合は無視する hstのMAXは8 使用例 (1)fc_str_Printf("test1", 0); (2)fc_str_Printf("test2", 0); (3)fc_str_Printf("test3", 1); (4)fc_str_Printf("test4", 3); を実行すると出力結果は・・・ test1 ←(1)の結果 test2 ←(2)の結果 test2 ←(3)の結果 test3 ←(3)の結果 test1 ←(4)の結果 test2 ←(4)の結果 test3 ←(4)の結果 test4 ←(4)の結果 よろしくお願いします。。。

  • C言語の問題で困っています。

    C言語の問題で困っています。 途中までできたのですが、この先が分かりません。 教えて頂くようお願いいたします。 【問題】 文字列の長さを求めるプログラムです。このプログラムを、入力した文字列の文字列長を求めるように変更してみましょう。  ただし、入力する文字列は半角で最大 20 文字までとし、指定された範囲外の値( 21 以上)が入力された場合は、正しい値が入力されるまで入力処理を繰り返すこと。 #include <stdio.h> int main(void) { char str[256] = "Hello"; int length, i; printf("文字列:"); scanf ("%s",str); length=0; i=0; while (str[i]!='\0') { i++; length++; } printf("\n文字列長:%d\n",length); }

  • C言語の変換する関数について教えてください。

    キーボードからローマ字で入力された名前の英文字を変換する関数を定義し、その関数の機能を確認するプログラムを作成する問題について教えてください。 (1)英小文字であればそれを英大文字に変換する関数 (2)英大文字であればそれを英小文字に変換する関数 (3)英小文字であればそれを英大文字に、英大文字であればそれを英小文字に変換する関数 ただし、キーボードから入力された名前を格納する配列と、変換後の名前を格納する配列を別にする。 また、名前は関数main()内で表示する #include <ctype.h> #include <stdio.h> void name_toupper(char str[]) { unsigned i = 0; while (str[i]) { str[i] = toupper(str[i]); i++; } } void name_tolower(char str[]) { unsigned i = 0; while (str[i]) { str[i] = tolower(str[i]); i++; } } int main(void) { char str[100]; printf("文字"); scanf("%s", str); name_toupper(str); printf("大文字: %s\n", str); name_tolower(str); printf("小文字: %s\n", str); return 0; } 自分で作った上のプログラムではKa siと入力すると(1)ではKA、(2)ではkaと表示されsiが消えてしまいます。原因がよくわかりません。 あと(3)ができないし、ただしを満たしているのかもあいまいです。 文字列の入力の形式:char *gets(char *buffer)を用いればどうにかなるのではと思っていますがどうですか? 説明が長くなって申し訳ありませんが教えてください。 よろしくお願いします。

専門家に質問してみよう