電子書籍の厳選無料作品が豊富!

char *str_char(const char *str, int c)
{
while (*str++)
if (*str == c)
return ((char *)str);
return (NULL);
}
/*文字列strから文字cを検索し最初に存在する文字へのポインターを返す*/

上記関数なのですが
5行目を return (str); にしてはなぜいけないのでしょうか。

A 回答 (10件)

const は難しいですね。


でも、使い方は覚えておいたほうが良いですよ。

char *str_char(const char *str, int c);

という関数の場合、第一引数 str は「const な char型を指すポインタ」ってことですよね。
# ここまでは問題ないんですよね?

ということは、この関数は「第一引数に渡された文字列は変更しません」ということを表明していることになります。

char型 → const char型の暗黙の型変換は、コンパイラが勝手に行います。
読み書き可能な char型から読み取り専用の char型への変換は、条件が厳しくなる方向の変換ですから問題ないわけです。

でも、const char型 → char型の暗黙の型変換は、コンパイラによってエラーになります。
読み取り専用の char型から読み書き可能な char型への型変換は、条件がゆるくなる方向の変換ですから、使う側が意図しない動作になってしまう可能性があるわけです。

そう考えるとご質問の、「return (str); にしてはなぜいけないのでしょうか。」の答えもお分かりになるのではないでしょうか。

const char型 → char型の型変換にエラーが出るのであれば、それぞれのポインタ同士の型変換でもエラーが出るのは当然でしょう。

ところで、No4 で BLUEPIXY さんが「戻り値も const char* 型にすべき」というような回答をされておりますが、私はちょっと違う見解です。
# そういうことではない、という場合、私の勘違いです。
# 申し訳ありません。。。

str_char() の目的は「文字列中のある文字と最初に一致する場所を返す」ことです。
ということは、この関数のコール元ではそこの場所にある文字になんらかの操作を加えたい可能性も十分にあると思います。

引数の const はあくまで「渡された文字列に変更は加えません」という意思表示でしかないと思います。
戻り値に関しては、特に問題ないと思います。

# でも、const char* → char* のようなキャストは極力しないほうが良いのは
# 確かです。

私のサンプルコードはこんな感じです。
# 中括弧つける派なものでして。。。


--------------------------------------------------------
#include <stdio.h>

char *str_char( const char *str, int c )
{
int i;

for ( i=0; str[i] != '\0'; i++ )
{
if ( str[i] == c )
{
return (char*)&str[i];
}
}
return NULL;
}

int main( void )
{
char hoge[] = "ABCDEFGHIJKLMN";
char *piyo;

piyo = str_char( hoge, 'C' );

if ( piyo == NULL )
{
printf( "Fail to match...\n" );
}
else
{
printf( "Success to match! ->%c\n", *piyo );
/* piyo 以降の文字列を書き換えるのもあり! */
}

return 0;
}
--------------------------------------------------------
    • good
    • 0

> まったく、その通りですね。


> #8さんが、的を射た答えですね。
> #4は、言い過ぎです。すみません。<(_ _)>

いえいえ。。。
ポインタ渡しの関数を作るときに、私がよく使う手でしたので、ちょっとコメントさせてもらっただけです。
そこまで言っていただけると、ちょっと恐縮です。。。

ポインタを引数にとる関数のコメントに「IN/OUT」というのをたまに見かけますけど、そんなことをやるよりも const のありなしでやるほうがよっぽど効果があると思っています。
const なしの場合は、関数で中身を変更される可能性があるし、const つきの場合は絶対に変更されないことが保障されるわけですから。。。

# そもそも、私の場合コメントはあまり信用していませんし。
# あとで更新しようと思って、そのまま放置されていることが結構ありますし。。。

…やばい。。。関係ない話で雑談モードに入りそうなので、この辺で失礼します。
    • good
    • 0
この回答へのお礼

とてもよくわかりました
本当にありがとうございました

お礼日時:2005/06/12 00:20

>引数の const はあくまで「渡された文字列に変更は加えません」という意思表示でしかないと思います。


>戻り値に関しては、特に問題ないと思います。
まったく、その通りですね。
#8さんが、的を射た答えですね。
#4は、言い過ぎです。すみません。<(_ _)>
実際の所、
同様の関数strchrのプロトタイプ宣言は、
char *strchr(const char *s, char c);
になってますしね。
結局の処は、そういうものは、(constで受けてconstで返すかどうかというのは)プログラマにゆだねられている(そういう状況が必要かどうか)ということですよね。(なんでもかんでもキャストすればいいってものでもないし)

C++で言えば、constのメンバ関数とでもいうところですか・
    • good
    • 0

>while (*str++)


質問者は、既に判っておられると思いますが
正解ではありません。

結局の処、他人の20年の経験なんてあてにはなりません。
    • good
    • 0

#2です。


失礼しました。5行目と6行目を見間違えた挙句に間違ったこと書きました。

で、5行目をreturn(str);に出来ないのは
戻り値の型がchar*であるため。
#4の方が書かれたように、戻り値をconst char*にすれば
return(str);と記述できます。

ちなみに、constは、ちゃんとしたライブラリを使うor作るのでしたら必ず意識が必要です。忘れていいわけがありません。
constがあれば、関数を呼び出した先で変更される可能性があるのか無いのかがはっきりとします。
特にWindowsAPI等は、かなり厳格にconstの有無で引数のIOが理解できます。

また、C++になりますが、クラスの内部のポインタを返すときに、
constをつけないで返すとライブラリの使用者にインスタンス内部を破壊される恐れもありますので必須です。

constは、つまり「間違った使い方をしないように」と口頭でお願いするのではなく、「間違って使おうとしてもつかえません」とするためのだと思ってください。
    • good
    • 0

No.1の補足に対する回答。



まず、わたしのプログラムにある誤記を示しておきます。

while (str++)
 ↓
while (*str++)

が正解です。次に「5行目」の件ですが、「{」だけの行も行数として数えますので、N0.2さんの回答とは大いに違ったものになっています。
でも、「NULL Pointer」というのもアリですから、

return (NULL);

はそのままとしておきました。これは「6行目」ですからね。

さて本題に入ります。問題は"const"の使いかたにあるのですが、これは他の方が回答されていますね。
私が言いたいのは、20年も仕事でCを使い込みましたが、"const"を使用するチャンスはゼロでした。なので、"const"はお忘れになったほうがいいとおもいます。
    • good
    • 0

#include <stdio.h>



/* 文字列strから文字cを検索し最初に存在する文字へのポインターを返す */
const char *str_char(const char *str, int c)
{
for (; *str; str++)
if (*str == c)
return (str);

return (NULL);
}

int main(void)
{
const char *p;
char str[10];
char uletter[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

printf("検索する英大文字を入力してください:");
scanf ("%s", &str);

p = str_char(uletter, str[0]);
if (p != NULL)
puts(p);

return (0);
}
としてみたら?
---------------------
なぜいけないかといわれたら、
const char *str
ということは、
*str='a';
とかしてはいけないということです。
それを
char *p;

p=(char *)str;
とかして、
無理やりキャストして代入したとすると
*p='a';
を(やるかどうかは別として可能性として)許すということですが、
もともと*strの部分は、constだから変更してはいけないのに、変更を許すということです。
だから
いけないのです。
もし、そうした変更を許すなら、
引数として
const char *
宣言する方がおかしいのです。
    • good
    • 0

const 文字列へのポインタなのに、


const じゃないポインタとして返したら
ダメなような気がします。

このプログラムって先頭にある文字cを見つけることはできますか?

この回答への補足

回答を確認すると以下のようになっていました
関数str_char のstrは mainのstr[0]の番地を格納していて
そのstrがインクリメントされていきcと同じであれば
str(cと同じ文字を格納しているアドレス)を返すのですよね?
関数str_char の返却値もポインタなのですから
わざわざ return ((char *)str) とcharへのポインター型にキャストしなくても
そもそも str 自体がアドレスを格納しているポインタ変数だから return (str) でいいのではないでしょうか。

return (str) のままでコンパイルすると、やはり
エラーがでます。

難しい><


/*
  読み込んだ英大文字以降のアルファベットを表示
*/
#include <stdio.h>

/* 文字列strから文字cを検索し最初に存在する文字へのポインターを返す */
char *str_char(const char *str, int c)
{
  for (; *str; str++)
    if (*str == c)
      return ((char *)str);

  return (NULL);
}

int main(void)
{
  char str[10] , *p;
  char uletter[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  printf("検索する英大文字を入力してください:");
  scanf ("%s", &str);

  p = str_char(uletter, str[0]);
  if (p != NULL)
    puts(p);

  return (0);
}

補足日時:2005/06/11 00:58
    • good
    • 0

別にいけなくはないですが。


ただし、見つからなかったときはNULLを返すと仕様が決まっているんでしたらNULLにしないとまずいですが。
逆に、見つからなかったときははヌル文字の位置を返す仕様ならstrじゃないとまずいですし。
    • good
    • 0

すっきり書くとこうなります。


――――――――――――――――――――――――――
#include <stdio.h>

char *str_char(char *str, int c)
{
  while (str++)
    if (*str == c)
      return (str);

  return (NULL);
}

void main( void )
{
  static char *x="abcdefghij";

  printf("%s\n", str_char(x, 'd'));
}
――――――――――――――――――――――――――
ね、ちゃんとreturn (str);と書けますね。
元のプログラムと、文法書を引っ張り出して考えてください。難しく考えないことです。
(Visual C++6.0で検査済み)

この回答への補足

char *str_char(char *str, int c)
ここで引数と返却値の型をあわせてあげて、その後
return (str)にすると考えていいのでしょうか

例えば
const char *str_char (const char *str, int c)
の場合なら
(返却値にconstを使えるのかどうかわかりませんが・・)
文法的に return (str)にすることができて

const char *str_char (char *str, int c)
の場合だと
return ((const char *) str) にしなければいけない
ということでしょうか。

補足日時:2005/06/11 12:03
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!