![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?e8efa67)
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で質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# str[j++]の意味 2 2022/08/30 16:20
- C言語・C++・C# sprintf()の使い方について 1 2022/08/17 16:16
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# C# 浮動小数の数値文字列化 1 2022/04/18 15:15
- C言語・C++・C# C言語 少しの疑問 4 2022/11/08 02:48
- C言語・C++・C# C#テキストボックスの文字を配列にいれてその後表示する 4 2022/07/17 04:47
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
c言語でポインタ変数を用いた配...
-
C言語のintとcharの違いってな...
-
C言語にて構造体のメンバがNULL...
-
ポインタを使って回文かどうか...
-
文字列内の数字削除
-
char*を初期化したいのですが
-
CStringからchar*への型変換に...
-
【C言語】引数にファイルパスを...
-
fstream型オブジェクトを関数の...
-
csvファイルをfscanfで読み込む...
-
DWORDとcharの変換
-
Cの関数の引数のconst *charに...
-
char 文字列型 の表現範囲が-12...
-
C言語で文字列をかえす正しい書...
-
C言語のプログラムについてです
-
char型にint型の数値を代入する。
-
VBAのプログラムで、DIAG = 1# ...
-
C言語 配列の長さの上限
-
関数から配列を返すには?
-
構造体のextern方法
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
char*を初期化したいのですが
-
CStringからchar*への型変換に...
-
C言語のintとcharの違いってな...
-
C言語にて構造体のメンバがNULL...
-
小数点入りの文字列をfloat型に...
-
strcat関数を自作したいです
-
C言語のプログラムについてです
-
const char* s1とただのchar s1...
-
DWORDとcharの変換
-
char型にint型の数値を代入する。
-
文字列の途中から途中までを抽出
-
new charとnew char[N]の違いは?
-
文字列内の数字削除
-
csvファイルをfscanfで読み込む...
-
fgetc( )の戻り値はなぜ整数??
-
char 文字列型 の表現範囲が-12...
-
fstream型オブジェクトを関数の...
-
エクセルのMID関数は、C言語では?
-
ポインタを使って回文かどうか...
-
ポインタ配列
おすすめ情報