文字列とポインタについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
gumi

文字列とポインタについて

#1

投稿記事 by gumi » 7年前

以前、文字列の分割に関して質問させていただいた者です。
<URL:http://dixq.net/forum/viewtopic.php?f=3&t=19779>
前回教えていただいた際は気づかなったのですが、新たに2点わからない点がでてきたため、再度質問させていただければと思います。
読みづらく恐縮ですが、質問はコード内★箇所です。
念のため先に、質問箇所を抜粋します。

★「printf("%s\n", word); 」がエラーになるのはなぜか?

以下は手持ちの書籍のサンプルコートですが、ポインタによる文字列であるptrはちゃんと
 「printf("ptr = \"%s\"\n", ptr);」
でも出力できているため、疑問に思いました。
 ******************************
 int main(void)
 {
char *ptr = "123";
printf("ptr = \"%s\"\n", ptr);
}
******************************

★「result = word[0];」とすると、
 「warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 2 has type ‘int’ [-Wformat=]」が出てしまう。
  printf("%d\n", result)で試すと、分割した文字列要素の先頭文字のアドレスが入っていることは確認できるが、
 printf("%S\n", result)として文字を直接出力できないのはなぜか?resultはchar型なので、そもそもintは入りようがないのではないか?

★ 「puts(result);」ではなく、「printf(result)」にできないのはなぜか?

====================
#include <stdio.h>
#include <string.h>

#define SIZE 64

int main(void)
{
int num = 0;
int i;
char string[SIZE]; //入力した文字列
char *word[SIZE]; //単語の位置が入る文字列。ポインタ配列なので直接文字代入可能。
char result[SIZE]; //最終的に出力する文字列

printf("文字列を入力してください\n");

string[0] = '\0'; //初期化
fgets(string, sizeof(string), stdin); //stringが入る

//strtokの使い方に注意
word[0] = strtok(string, " ");
while(word[num] != NULL)
{
word[++num] = strtok(NULL, " ");
}

printf("%s\n", word); //★エラーになるのはなぜか?
printf("%s\n", word[0]); //OK
printf("%s\n", *(word+1)); //OK

for (i = 0; i < num; i++)
{
result = word[0];
/*★ 「warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 2 has type ‘int’ [-Wformat=]」が出る。
    printf("%d\n", result)で試すとアドレスが入っていることは確認できるが、
   printf("%S\n", result)として文字を直接出力できないのはなぜか? resultはchar型ではないのか? */
}
result[num] = '\0';
puts(result);  /*★ printf(result)にできないのはなぜか

return 0;
}


お手数ですが何卒よろしくお願いします。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 文字列とポインタについて

#2

投稿記事 by みけCAT » 7年前

ソースコードを提示する際は、BBCodeが有効な(無効にしない)状態で、
BBCodeのcodeタグの開始タグと終了タグの組(開始タグが先)で囲んでいただけると、
見やすくてありがたいです。
gumi さんが書きました:★「printf("%s\n", word); 」がエラーになるのはなぜか?
%sを使う時は有効なNUL終端の文字列を指すポインタ(char*型のデータ)を与えないといけないのに対し、
wordはchar*型のデータを要素とする配列であり、先頭要素へのポインタ(char**型のデータ)に変換されるので、
型が間違っており、未定義動作になります。
使用しているコンパイラがわかりませんが、GCCでは警告が出る場合があり、警告をエラーとして扱うオプションを使えばエラーになるでしょう。
gumi さんが書きました: 以下は手持ちの書籍のサンプルコートですが、ポインタによる文字列であるptrはちゃんと
 「printf("ptr = \"%s\"\n", ptr);」
でも出力できているため、疑問に思いました。
 ******************************
 int main(void)
 {
char *ptr = "123";
printf("ptr = \"%s\"\n", ptr);
}
******************************
ptrはchar*型であり有効な文字列を指しているので、問題ありません。
gumi さんが書きました: ★「result = word[0];」とすると、
 「warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 2 has type ‘int’ [-Wformat=]」が出てしまう。

この文には%Sが含まれていないので、この文を原因としてこのメッセージが出るのは不自然です。
こうしない場合はこれが出ないのですか?

gumi さんが書きました:   printf("%d\n", result)で試すと、分割した文字列要素の先頭文字のアドレスが入っていることは確認できるが、

resultには文字が入っており、通常この文だけではデバッガを使用しない限りアドレスが入っていることは確認できないはずです。

gumi さんが書きました:  printf("%S\n", result)として文字を直接出力できないのはなぜか?

書式%S (N1570では見つけられませんでした) が文字ではなくwchar_t *型のデータを要求する仕様だからでしょう。
printfで1文字出力するには、書式%cを使うといいでしょう。

gumi さんが書きました: resultはchar型なので、そもそもintは入りようがないのではないか?

printfの不定長の引数の部分は型がわからないので、integer promotionが適用され、int型に変換されます。

gumi さんが書きました:★ 「puts(result);」ではなく、「printf(result)」にできないのはなぜか?

printfは書式の処理を行うので、入力に%を含む文字列が与えられた時、想定しない場所の読み書きが行われ、危険になります。
また、C言語では式文の最後にはセミコロンが必要です。

コード:

printf("%s\n", result);
なら大丈夫です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

gumi

Re: 文字列とポインタについて

#3

投稿記事 by gumi » 7年前

みけcat様

回答ありがとうございます。また、タグで囲い込むのを失念していました。申し訳ありません。

>%sを使う時は有効なNUL終端の文字列を指すポインタ(char*型のデータ)を与えないといけないのに対し、
>wordはchar*型のデータを要素とする配列であり、先頭要素へのポインタ(char**型のデータ)に変換されるので、
>型が間違っており、未定義動作になります。
>使用しているコンパイラがわかりませんが、GCCでは警告が出る場合があり、警告をエラーとして扱うオプションを使えばエラーになるでしょう。

すみません、初心者過ぎて理解が追いつきませんでした・・・。
ptrは「単なるchar*型の要素で配列ではない(例:123)」が、wordは「char*型の配列(例: Nippon Hoso Kyokai)」だからNGということでしょうか?
また、手持ちの書籍から以下のコードを見つけたのですが、なぜ以下の場合は、ptrの前に*をつけなければいけないのでしょうか??
int型だからでしょうか…?

コード:

#include <stdio.h>

int main(void)
{
    int n;
    int *ptr;

    n = 57;
    ptr = &n;

    printf("ptrの値 =%d\n", *ptr); /*なぜこれには*がつくのか??*/
    return 0;
}
>この文には%Sが含まれていないので、この文を原因としてこのメッセージが出るのは不自然です。
>こうしない場合はこれが出ないのですか?

書き方が不正確でした。「printf("%s\n", result);」と書くと、「warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 2 has type ‘int’ [-Wformat=]」が出ます。

>resultには文字が入っており、通常この文だけではデバッガを使用しない限りアドレスが入っていることは確認できないはずです。
なぜか78, 72, 75のような数字が出たのでアドレスだと思っていました。
よくよく考えたらアドレスにしては短いですね。

>printfの不定長の引数の部分は型がわからないので、integer promotionが適用され、int型に変換されます。
ありがとうございます、裏側で起きている事象については理解しました。
ただ、不定長の引数、というのはサイズがわからない引数、ということですよね?
resultには1要素(連結文字列で、入力値がNippon Hoso Kyokai だったら NHK)しか入っていないのではないですか?

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 文字列とポインタについて

#4

投稿記事 by みけCAT » 7年前

gumi さんが書きました:ptrは「単なるchar*型の要素で配列ではない(例:123)」が、wordは「char*型の配列(例: Nippon Hoso Kyokai)」だからNGということでしょうか?
そうなりますね。
printfにchar*型を要素とする配列の場所(ポインタ)を渡しても、そこにあるのは文字列の位置(ポインタ)であって文字列ではないので、ダメです。
gumi さんが書きました:また、手持ちの書籍から以下のコードを見つけたのですが、なぜ以下の場合は、ptrの前に*をつけなければいけないのでしょうか??
ptrに入っているポインタの値ではなく、ptrが指しているint型の値が欲しいからですね。
出力している文字列の「ptrの値」というのはまぎらわしいですね。
gumi さんが書きました:書き方が不正確でした。「printf("%s\n", result);」と書くと、「warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 2 has type ‘int’ [-Wformat=]」が出ます。

%Sではなく書式%sを使っているのに、%Sに関する警告が出るのは不自然ですね。

gumi さんが書きました:>resultには文字が入っており、通常この文だけではデバッガを使用しない限りアドレスが入っていることは確認できないはずです。
なぜか78, 72, 75のような数字が出たのでアドレスだと思っていました。
よくよく考えたらアドレスにしては短いですね。

%dは与えられたint型の整数を十進数で出力するので、文字コードが出力されていますね。

gumi さんが書きました:ただ、不定長の引数、というのはサイズがわからない引数、ということですよね?

「サイズ」の定義がはっきりしませんが、引数の個数がわからないということです。

gumi さんが書きました:resultには1要素(連結文字列で、入力値がNippon Hoso Kyokai だったら NHK)しか入っていないのではないですか?

「要素」の定義がはっきりしませんが、resultには1要素が入り、resultに入るのは'N', 'H', 'K', '\0'の4要素であるといえます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

gumi

Re: 文字列とポインタについて

#5

投稿記事 by gumi » 7年前

みけcat様

回答いただきありがとうございます。
ポインタについて、徐々に理解が整理されてきました。

>%Sではなく書式%sを使っているのに、%Sに関する警告が出るのは不自然ですね。
小文字なのが大文字だったのが間違いだったのですね。
繰り返しコードを修正しているうちに変なものが紛れ込んでしまったようです。

丁寧に回答いただけて本当に助かりました。
是非今後も質問を見かけたらお力添えいただけますと幸いです。

返信

“C言語何でも質問掲示板” へ戻る