アプリ版:「スタンプのみでお礼する」機能のリリースについて

文字列strの中から文字cを探すプログラム(C言語)がわからない
柴田望洋さんの「[新版]明解C言語」という本の演習11-2なんですがどうしてもわかりません。間違いは無いと思うのにコンパイルすると警告を吐かれます。

僕が書いたプログラムを載せます。

/*
文字列strの中に、文字cが含まれていれば(複数ある場合は、最も先頭側とする)、
その文字へのポインタを返し、含まれていなければNULLを返す関数
char *str_chr(const char *str, int c) {}
を作成せよ。
*/
#include<stdio.h>
char *str_chr(const char *str, int c){
while(*str){
if(*str==c) return str;
str++;
}
    return NULL;
}
int main(){
char *str;
char c;

scanf("%s",str);
scanf(" %c",c);
    printf("%d",str_chr(str,c));

return 0;
}

コンパイラは「関数str_chrのif分の中のreturn strの型変換に問題がある」と言っているんです。
型変換はしるつもりは無いのにコンパイラはなぜそのように認識するのでしょうか。
またネット答えを探しましたがどうやらこのreturn strの部分はreturn (char*)strが正解のようです。意味がわかりません。strはポインタなのになぜまたわざわざchar型に変換しているのですか?といか(char*)の意味が根本的にわかりません。

質問ばかりですみません。初心者でポインタがどうにも理解できないんです。
誰か詳しい人教えてください。
お願いします。

A 回答 (7件)

char *str_chr(const char *str, int c) {}



という宣言では
戻り値: char *型(charへのポインタ)
引数: str (cosnt char *型/const charへのポインタ)
となります。

const char というのは、「変更できないchar」と言う意味です。
char c='A' ;
const char cc='A' ; /* 初期化によってのみ代入可能 */

c ='C' ; /* OK */
cc ='C' ; /* エラー */

const char * はconst char への(普通の)ポインタです。ポインタが指す実体はconst charなので変更できませんが、ポインタ自身は通常の変数なので変更できます。
char str[] = "abcde" ;
char * cp = str;
const char* ccp ="ABCDE" ;

cp ++ ; /* OK */
*cp = 'A' ; /* OK */

ccp ++ ; /* OK */
*ccp = 'A' ; /* エラー */

このように、振舞いが違うので、char/char *型とは別の型として扱われます。
char → const char,char * → const char * は暗黙の型変換ができます。変更を禁止するだけですから。
逆方向は、(本来は)基本できません。const の意味を失ってしまうし、文字列リテラルのような本当に変更してはいけない(致命的なエラーになりかねない)ものもあるからです。

型変換(キャスト)は、暗黙の変換ができない場合でも、強制的に型変換を行うもので、変換したいものの前に
(変換したい型)
と付けます。 (char *)str はstrの型を強制的にchar *型にします。
なお、この変換は、キャストを書いた箇所だけのもので、変数そのものを変換するわけではありません。


さて、問題のプログラムですが。
return str としていますから、char * 型をreturnしなければならないところに、const char *型を指定している、という警告です。
const char * → char *の暗黙の型変換は無いので、char *として使えない、ということです。
そのため、戻り値の型を合わせるために(char *)でキャストしています。


> strはポインタなのになぜまたわざわざchar型に変換しているのですか?

char型には変換していません。
char型に変換するときは(char)です。
    • good
    • 0

ANo.6です、質問の内容を早とちりして、勘違いしていました。


再回答します。

>このreturn strの部分はreturn (char*)strが正解のようです。
>意味がわかりません。
>strはポインタなのになぜまたわざわざchar型に変換しているのですか?
>といか(char*)の意味が根本的にわかりません。

質問者さんのコードをそのままコンパイルすると、
VC++では次のような「警告」がかえってきます。
warning C4090: 'return' : 異なる 'const' 修飾子です。

str_chr()引数で「const char *」型として渡された引数を、
「char *」型として戻そうとするために、エラーまたは警告となります。

引数の型を「const char *」→「char *」に変更すると、
エラーまたは警告がなくなることで、その意味がわかると思います。
    • good
    • 0

柴田望洋さんのHP:「C」→「標準関数」→「strchr」


http://www.bohyoh.com/CandCPP/C/Library/strchr.h …

に、「■実装例■」として模範例が出ています。
ご自身のコードと比較してみてください。
    • good
    • 0

char *str_chr(const char *str, int c) {}


を作れといっているのだから,「誤り」といわれてもネ。
//はその文字が何番目にあるかにしたいと思っただけで,質問とは関係ない

#include<stdio.h>

char *str_chr(const char *str, int c)
{
while(*str){
if(*str==c) return (char *)str;/*一時的に型キャスト,キャストしなくてもエラーではない。constといっているのに変える気か,というような警告*/
str++;
}
return NULL;
}

int main()
{
char str[100]={0};/*scanfで文字列を取りたいのだから配列にして,初期化しておく*/
char c;
//char *p;

scanf("%s",str);
scanf(" %c",&c);/*scanfの引数はアドレス*/

//p=str-1;
printf("%p\n",str_chr(str,c));/*ポインタを返すのだから,書式は%p*/
//printf("%d\n",str_chr(str,c)-p);/*ポインタで返されてもわからないから,何番目かわかるようにすればこうなるというだけ*/

return 0;
}
    • good
    • 0

(1)


関数の宣言で、

誤:char *str_chr(const char *str, int c)
正:const char *str_chr(const char *str, int c)

です。
str_chrの内容は合っていると思います。


(2)
mainにおいて、

printf("%d", str_chr(str,c));

は、間違っています。

------------
誤:printf("%d", str_chr(str,c));
正:printf("%s", str_chr(str,c));
です。
改行するなら、printf("%s\n", str_chr(str,c));と\nをつけます。



(3)
mainにおいて、

char *str ;
scanf("%s", str) ;
は間違いです。

実体あってのポインタです。
[ポインタ]----->[実体]

char *str ;
は、ポインタサイズの容器strを確保しますが、容器の中身は不定です。
そのような値を関数に渡してはいけません。
-----
char enoughBuffer[1024] ;
enoughBuffer[0] = '\0' ; /* 空文字列として初期化 */
char *str = &enoughBuffer[0] ; /* ポインタサイズの容器strを確保し、バッファの先頭文字へのポインタを格納 */
scanf("%s", str) ;

とか、

char enoughBuffer[1024] ;
enoughBuffer[0] = '\0' ; /* 空文字列として初期化 */
scanf("%s", enoughBuffer) ; /* バッファの先頭へのポインタを渡す */

とすべきです。
ちなみに、C言語では、&enoughBuffer[0]とenoughBufferは、同じ値(同じポインタ)です。

-------------------------
以下、ご参考
-------------------------
ご存じのとおり、C言語には文字列型は存在しません。代わりに文字配列と'\0'の組み合わせで表現します。

(4)
文字へのポインタも、文字配列へのポインタも、char *で表します。

a. [ポインタ(char *)]------>(char)[文字]
b. [ポインタ(char *)]------>(char配列)[文字][文字]...[文字][文字]

たとえば、
char *p ;
という定義において、pがa,bいずれのタイプを指しているかは、プログラムを書いた人にしかわかりません。そのため実務上は、char *pMoji, *pNameなど、変数名で区別します。


(5)
ポインタが指している実体を書き換えてほしくないときは、constを使います。

たとえば
char moji = 'X' ;
char *p1 = &moji ;
const char *p2 = &moji ;
と定義されているとすると、

p1は、将来、実体を書き換えることを示唆しています。(たとえば、*p1 = 'S')
p2は、実体を参照はするかもしれないが、書き換える意図はないことを示唆しています。


(6)
文字列定数は、内部的に文字配列(最後の要素が'\0')です。
定数は書き換えてほしくないので、constを使います。

(例)
const char *pcstr = "Good Morning." ;

コンパイラが許せば、テクニックとしては、
 char *pstr = (char *)"Good Morning." ;
という定義も可能ですが、良いプログラムではありません。
    • good
    • 0

No.2さんが示されているとおりです


私も間違ったことあるだけでなく なかなか理解できませんでした
私は次のように要約して理解しています
(1)const char * と char * とは 別の型である
(2)暗黙に変換される方向とそうでない方向がある
(3)const char * は書き込み不可 char * 書き込み可 だが
   キャストは変数を変えることはしないその場限りである
うーん うまく表現できないな
    • good
    • 0

コンパイラがwarningを出す理由は関数の宣言が


char *str_chr(const char *str, int c){
と、strがconstと宣言されていて、関数の戻り値の定義、char *と一致しないからです。

ちなみにgccでは以下のworningが出力されています。
warning: return discards qualifiers from pointer target type
これは、「リターンでポインターの型宣言が破棄されている」ということです。

const宣言は、「変数の内容が変更できない」と意味なので、誤って変更してしまうバグを予防するために使用されます。この場合は、scanf()で読み取る文字列ですから、内容が変更され使用方法が矛盾しています。

const宣言は一般的にメッセージや定数などの内容を変更してはならない変数(記憶領域)に対して使用されます。特に変数がROMにある場合には、その変数に書き込むとプログラムの実行時にCPUの動作が異常になります。
(割り込み処理に移行したり停止したりする)
したがってconstの使用はバグの予防対策として実装されているのです。


あとは、main()の
scanf()とprintf()の見直しですね。
    • good
    • 0

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