C言語関係掲示板

過去ログ

No.126.文字列とデータの配列の違い


No.725

演習問題の10-3の問1
投稿者---piyoko(2001/12/25 10:18:36)


演習問題の10-3の問1を私は以下のようにしたのですが、
エラーを出してしまいました。
間違いの箇所をどう直せばよいのか分かりません。
どうぞお助けください。。。

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

int main(void)
{
int data1[10]={10,15,22,45,9,66,71,4,37,82};
int data2[10];
int len;

int *p1;
int *p2;

p1=data1;
p2=data2;

while(p1!='\0'){
if(((*p1)%2)==1);
*p2=*p1;
p2++;
}
printf("%d\n",p1);
printf("配列の中身=%d\n",p2);
→ len=strlen(p2);
printf("格納個数=%d\n",len);

return 0;
}

→C:\Program Files\Microsoft Visual Studio\MyProjects\enshu10_3\enshu10_3.c(26) : warning       C4133: 'function' : 'int *' と 'const char *' の間で型に互換性がありません。



No.727

Re:演習問題の10-3の問1
投稿者---通りすがり(2001/12/25 11:21:55)


>演習問題の10-3の問1を私は以下のようにしたのですが、
>エラーを出してしまいました。
>間違いの箇所をどう直せばよいのか分かりません。
>どうぞお助けください。。。
>
>#include <stdio.h>
>#include <string.h>
>
>int main(void)
>{
> int data1[10]={10,15,22,45,9,66,71,4,37,82};
> int data2[10];
> int len;
>
> int *p1;
> int *p2;
>
> p1=data1;
> p2=data2;
>
> while(p1!='\0'){
> if(((*p1)%2)==1);
> *p2=*p1;
> p2++;
> }
> printf("%d\n",p1);
> printf("配列の中身=%d\n",p2);
>→ len=strlen(p2);
> printf("格納個数=%d\n",len);
>
> return 0;
>}
>
>→C:\Program Files\Microsoft Visual Studio\MyProjects\enshu10_3\enshu10_3.c(26) : warning       C4133: 'function' : 'int *' と 'const char *' の間で型に互換性がありません。


まずエラーの原因は、strlen関数の引数は文字列へのポインタなのに、int型の配列を渡してるためではないかと・・・。

で、それ以外に気になった点がいくつか。

while(p1!='\0'){
if(((*p1)%2)==1);
*p2=*p1;
p2++;
}
の部分で、p1をインクリメントしてないために無限ループになるような。
それと、「p1!='\0'」だとアドレスを参照してるので「*p1!='\0'」に
したほうがいいような気がします。
あと、これは記述ミスだとは思いますが、if分にセミコロンが付いてるのも
まずいような気がします。

printf("配列の中身=%d\n",p2);
の部分で、配列の中身を参照するときは、*をつけたほうがいいような
気がします

最後に、繰り返しの終了条件として、配列の中身が'\0'かどうかで
判断するときは、変数宣言で初期化するときに最後に0を入れないと
まずいような気がします。
int data1[11]={10,15,22,45,9,66,71,4,37,82,0};
といった感じで。

<私なりの修正したソース>
#include <stdio.h>

int main(void)
{
int data1[11]={10,15,22,45,9,66,71,4,37,82.0};
int data2[10];
int cnt = 0;
int i;

int *p1;
int *p2;

p1=data1;
p2=data2;



while(*p1!= '\0'){
if(((*p1)%2)==1)
{
*p2=*p1;
p2++;
cnt++;
}
p1++;
}

p2 = data2;

for(i = 0;i<cnt;i++)
{
printf("配列の中身=%d\n",*p2);
p2++;
}

printf("格納個数=%d\n",cnt);

return 0;

}


私もかなりの初心者のため、いろいろと間違いはあるとは思いますが
その点を指摘していただけると幸いです。よろしくお願いします。




No.728

Re:演習問題の10-3の問1
投稿者---B.Smith(2001/12/25 12:20:34)


こんにちは。

エラーの原因を見る前に、説明しなければならないことがあります。
順を追って説明します。この説明は、通りすがりさんの解説された事を含みます。
すでにご存知の事もあるとは思いますが、「何故そんな事を言ったんだろう」ということの理由を考えてみてくださいね。

一般に「文字列の配列」と呼ばれているものは、char型の配列です。
    char     String[10 + 1];

文字コードは1〜255の値で表されるので、char型の要素で十分なのです。文字列データは、末尾にヌル値(ゼロ)をセットし、「ここがデータの終端ですよ」と印を付けます。
文字列をchar型の配列にセットする例
   char    Str[] = {'T','E','S','T','\0'};

(配列Stringの中身)
  Str[0]  Str[1]  Str[2]  Str[3]  Str[4]
   'T'     'E'     'S'     'T'     '\0' ← ヌル値(0)

(文字列に限らず)データの終端に付ける終了の印は「番人」(sentinel)と呼ばれます。通常表示できる文字データの種類にはヌルは含まれないので、配列内のデータ全部を見たい場合「参照している内容がヌルになるまで繰り返す」という処理を記述することができます。

一方、今回のようなint型の配列には、通常、整数値が格納されます。この場合、ゼロを含むすべての値(int型で表現できる範囲)がデータとして意味を持ちますので、ヌル値で区切ることはできません。「ゼロのデータが意味を持たない」と初めから分かっている場合は別ですが、今回は特に指定が無いので、やはりint型の配列にはヌルを適用するべきではないと思います。
そのため、データ全部を処理するループを作成する場合、「要素数分繰り返す」という条件でなければなりません。

・奇数データを別配列に抽出するループを見直してみてください。

・関数strlenは文字列を格納する配列に対して使用されるべきものなので、今回は使えません。

・ループ内で「データが奇数である」と判断できた時に行うべき事が4つあります。
     奇数データの別配列へのコピー
     奇数データ数のカウント
     コピーした奇数データの表示
     奇数データ格納先の移動(ポインタ、あるいはインデックスを+1する)


ちなみに、今回問題になっている警告メッセージは引数の型の不一致です。関数strlenの引数はconst char *なのに対し、渡そうとしている引数はint *なので、警告が出るのです。





No.732

新たな疑問点
投稿者---piyoko(2001/12/26 11:08:22)


助けてくださってありがとうございました。
お二人の丁寧な解説で理解することができました。
このホームページを利用できるなんてとても幸せです。
しかし、また疑問点が発生いたしました。

〃拗陲鬟悒襯廚慮〆で表示してみたのですが、

コンパイラの警告 (レベル 3) C4133
'type' : 'type1' と 'type2' の間で型に互換性がありません。
型の異なる 2 つのポインタを減算しようとしました。
この警告は、適切な型キャストを使うことによって回避できます。

と表示されました。
ということはキャスト変換を使えば、関数strlenが使用できるのでしょうか?

△汗睫世ださった表現について。
>ちなみに、今回問題になっている警告メッセージは引数の型の不一致です。
>関数strlenの引数はconst char *なのに対し、
>渡そうとしている引数はint *なので、警告が出るのです。

この”渡そうとしている”というイメージがいまいち分からないのですが。。。
どうやって理解したらよろしいのでしょうか??

ホントに初歩的な質問ですみません。。。。
よろしくお願いします。

No.733

Re:新たな疑問点
投稿者---B.Smith(2001/12/26 14:34:03)


こんにちは。

>この”渡そうとしている”というイメージがいまいち分からないのですが。。。
>どうやって理解したらよろしいのでしょうか??

「渡そうとしている」というのはNo.725のソースにある以下の部分の事です。
    len = strlen(p2);

p2は今回int型のポインタとして定義されています。関数strlenが必要としているconst char *の型とは違うため、警告がでますよ、という意味でした。

ヘルプには「この警告は、適切な型キャストを使うことによって回避できます。」とあります。確かにキャストすれば警告は出なくなりますが、今回はキャストしても期待する結果は得られません。この警告はキャストで解決できるものではなく、純粋に警告と見るべきなんですよ?

int型ポインタをchar型のポインタにキャストしても、実際にはポインタの指す実体がchar型の領域ではないため、使用することはできません。
関数strlenは1要素が1バイトというchar型の配列に格納されている文字数をカウントするためのものです。関数内部で1バイト単位でデータを参照しています。そのため、1要素が4バイトで構成されるint型の配列のデータ数をカウントすることはできないのです(Visual C++を使用されているようなので、int型=4バイト)。



No.735

Re:新たな疑問点
投稿者---shu(2001/12/26 16:45:05)
http://c2c-1.rocketbeach.com/~finder_s/ccc/


>>どうやって理解したらよろしいのでしょうか??

そもそもこの問題で、strlen を使うことが間違いなので、さらにその状況で、strlen について詳しく説明しても混乱します、できれば次の演習10‐4の方で、理解した方がいいんじゃないでしょうか?

No.747

Re:新たな疑問点
投稿者---piyoko(2002/01/07 02:02:17)


すぐに教えて頂いたのにお礼が遅れました。
どうもありがとうございました。
正月休みに自宅でよく理解することができました。

これからも助けていただきたいです。
分かりやすいご説明ありがとうございました。

戻る


「初心者のためのポイント学習C言語」 Last modified:2002.02.03
Copyright(c) 2000-2002 TOMOJI All Rights Reserved