No.8ベストアンサー
- 回答日時:
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;
}
--------------------------------------------------------
No.10
- 回答日時:
> まったく、その通りですね。
> #8さんが、的を射た答えですね。
> #4は、言い過ぎです。すみません。<(_ _)>
いえいえ。。。
ポインタ渡しの関数を作るときに、私がよく使う手でしたので、ちょっとコメントさせてもらっただけです。
そこまで言っていただけると、ちょっと恐縮です。。。
ポインタを引数にとる関数のコメントに「IN/OUT」というのをたまに見かけますけど、そんなことをやるよりも const のありなしでやるほうがよっぽど効果があると思っています。
const なしの場合は、関数で中身を変更される可能性があるし、const つきの場合は絶対に変更されないことが保障されるわけですから。。。
# そもそも、私の場合コメントはあまり信用していませんし。
# あとで更新しようと思って、そのまま放置されていることが結構ありますし。。。
…やばい。。。関係ない話で雑談モードに入りそうなので、この辺で失礼します。
No.9
- 回答日時:
>引数の const はあくまで「渡された文字列に変更は加えません」という意思表示でしかないと思います。
>戻り値に関しては、特に問題ないと思います。
まったく、その通りですね。
#8さんが、的を射た答えですね。
#4は、言い過ぎです。すみません。<(_ _)>
実際の所、
同様の関数strchrのプロトタイプ宣言は、
char *strchr(const char *s, char c);
になってますしね。
結局の処は、そういうものは、(constで受けてconstで返すかどうかというのは)プログラマにゆだねられている(そういう状況が必要かどうか)ということですよね。(なんでもかんでもキャストすればいいってものでもないし)
C++で言えば、constのメンバ関数とでもいうところですか・
No.6
- 回答日時:
#2です。
失礼しました。5行目と6行目を見間違えた挙句に間違ったこと書きました。
で、5行目をreturn(str);に出来ないのは
戻り値の型がchar*であるため。
#4の方が書かれたように、戻り値をconst char*にすれば
return(str);と記述できます。
ちなみに、constは、ちゃんとしたライブラリを使うor作るのでしたら必ず意識が必要です。忘れていいわけがありません。
constがあれば、関数を呼び出した先で変更される可能性があるのか無いのかがはっきりとします。
特にWindowsAPI等は、かなり厳格にconstの有無で引数のIOが理解できます。
また、C++になりますが、クラスの内部のポインタを返すときに、
constをつけないで返すとライブラリの使用者にインスタンス内部を破壊される恐れもありますので必須です。
constは、つまり「間違った使い方をしないように」と口頭でお願いするのではなく、「間違って使おうとしてもつかえません」とするためのだと思ってください。
No.5
- 回答日時:
No.1の補足に対する回答。
まず、わたしのプログラムにある誤記を示しておきます。
while (str++)
↓
while (*str++)
が正解です。次に「5行目」の件ですが、「{」だけの行も行数として数えますので、N0.2さんの回答とは大いに違ったものになっています。
でも、「NULL Pointer」というのもアリですから、
return (NULL);
はそのままとしておきました。これは「6行目」ですからね。
さて本題に入ります。問題は"const"の使いかたにあるのですが、これは他の方が回答されていますね。
私が言いたいのは、20年も仕事でCを使い込みましたが、"const"を使用するチャンスはゼロでした。なので、"const"はお忘れになったほうがいいとおもいます。
No.4
- 回答日時:
#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 *
宣言する方がおかしいのです。
No.3
- 回答日時:
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);
}
No.2
- 回答日時:
別にいけなくはないですが。
ただし、見つからなかったときはNULLを返すと仕様が決まっているんでしたらNULLにしないとまずいですが。
逆に、見つからなかったときははヌル文字の位置を返す仕様ならstrじゃないとまずいですし。
No.1
- 回答日時:
すっきり書くとこうなります。
――――――――――――――――――――――――――
#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) にしなければいけない
ということでしょうか。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・ハマっている「お菓子」を教えて!
- ・最近、いつ泣きましたか?
- ・夏が終わったと感じる瞬間って、どんな時?
- ・10秒目をつむったら…
- ・人生のプチ美学を教えてください!!
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
char*を初期化したいのですが
-
小数点入りの文字列をfloat型に...
-
C言語でポインターで詰まってい...
-
CStringからchar*への型変換に...
-
C言語のintとcharの違いってな...
-
char型にint型の数値を代入する。
-
C++17で、unsigned char * 配列...
-
文字型配列に格納した空白の切捨て
-
csvファイルをfscanfで読み込む...
-
strcpy関数で文字型変数へのポ...
-
コマンドライン引数 *argv[]は...
-
コマンドライン引数
-
警告
-
C言語にて構造体のメンバがNULL...
-
new charとnew char[N]の違いは?
-
char 文字列型 の表現範囲が-12...
-
C言語での文字の連結
-
strcat関数を自作したいです
-
ゼロを含む文字列の連結方法
-
エクセルのMID関数は、C言語では?
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
CStringからchar*への型変換に...
-
char*を初期化したいのですが
-
C言語のintとcharの違いってな...
-
C言語にて構造体のメンバがNULL...
-
DWORDとcharの変換
-
小数点入りの文字列をfloat型に...
-
new charとnew char[N]の違いは?
-
char 文字列型 の表現範囲が-12...
-
C言語の文字リテラル中の16進文...
-
C++17で、unsigned char * 配列...
-
fstream型オブジェクトを関数の...
-
char型にint型の数値を代入する。
-
2次元配列の文字"列"の初期化方法
-
strcat関数を自作したいです
-
C++Builder 2009 テキスト...
-
動的メモリの初期化方法について。
-
C言語のプログラムについてです
-
C言語で文字列をかえす正しい書...
-
入力された文字列の中の数字を...
-
エクセルのMID関数は、C言語では?
おすすめ情報