
文字列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*)の意味が根本的にわかりません。
質問ばかりですみません。初心者でポインタがどうにも理解できないんです。
誰か詳しい人教えてください。
お願いします。
No.2ベストアンサー
- 回答日時:
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)です。
No.7
- 回答日時:
ANo.6です、質問の内容を早とちりして、勘違いしていました。
再回答します。
>このreturn strの部分はreturn (char*)strが正解のようです。
>意味がわかりません。
>strはポインタなのになぜまたわざわざchar型に変換しているのですか?
>といか(char*)の意味が根本的にわかりません。
質問者さんのコードをそのままコンパイルすると、
VC++では次のような「警告」がかえってきます。
warning C4090: 'return' : 異なる 'const' 修飾子です。
str_chr()引数で「const char *」型として渡された引数を、
「char *」型として戻そうとするために、エラーまたは警告となります。
引数の型を「const char *」→「char *」に変更すると、
エラーまたは警告がなくなることで、その意味がわかると思います。
No.6
- 回答日時:
柴田望洋さんのHP:「C」→「標準関数」→「strchr」
http://www.bohyoh.com/CandCPP/C/Library/strchr.h …
に、「■実装例■」として模範例が出ています。
ご自身のコードと比較してみてください。
No.5
- 回答日時:
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;
}
No.4
- 回答日時:
(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." ;
という定義も可能ですが、良いプログラムではありません。
No.3
- 回答日時:
No.2さんが示されているとおりです
私も間違ったことあるだけでなく なかなか理解できませんでした
私は次のように要約して理解しています
(1)const char * と char * とは 別の型である
(2)暗黙に変換される方向とそうでない方向がある
(3)const char * は書き込み不可 char * 書き込み可 だが
キャストは変数を変えることはしないその場限りである
うーん うまく表現できないな
No.1
- 回答日時:
コンパイラが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()の見直しですね。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- 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# プログラミングの授業の課題です 1 2023/01/17 22:15
- 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#テキストボックスの文字を配列にいれてその後表示する 4 2022/07/17 04: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言語のエラーについて 2 2022/07/11 13:56
関連するカテゴリからQ&Aを探す
今、見られている記事はコレ!
-
弁護士が語る「合法と違法を分けるオンラインカジノのシンプルな線引き」
「お金を賭けたら違法です」ーーこう答えたのは富士見坂法律事務所の井上義之弁護士。オンラインカジノが違法となるかどうかの基準は、このように非常にシンプルである。しかし2025年にはいって、違法賭博事件が相次...
-
釣りと密漁の違いは?知らなかったでは済まされない?事前にできることは?
知らなかったでは済まされないのが法律の世界であるが、全てを知ってから何かをするには少々手間がかかるし、最悪始めることすらできずに終わってしまうこともあり得る。教えてgooでも「釣りと密漁の境目はどこです...
-
カスハラとクレームの違いは?カスハラの法的責任は?企業がとるべき対応は?
東京都が、客からの迷惑行為などを称した「カスタマーハラスメント」、いわゆる「カスハラ」の防止を目的とした条例を、全国で初めて成立させた。条例に罰則はなく、2025年4月1日から施行される。 この動きは自治体...
-
なぜ批判コメントをするの?その心理と向き合い方をカウンセラーにきいた!
今や生活に必要不可欠となったインターネット。手軽に情報を得られるだけでなく、ネットを介したコミュニケーションも一般的となった。それと同時に顕在化しているのが、他者に対する辛らつな意見だ。ネットニュース...
-
大麻の使用罪がなかった理由や法改正での変更点、他国との違いを弁護士が解説
ドイツで2024年4月に大麻が合法化され、その2ヶ月後にサッカーEURO2024が行われた。その際、ドイツ警察は大会運営における治安維持の一つの方針として「アルコールを飲んでいるグループと、大麻を吸っているグループ...
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
プログラミングについて。 1つ...
-
gccを行ってもexeファイルが生...
-
c言語
-
visual studio 2022でのC#プロ...
-
C# DatagridviewにExcelシート...
-
mallocについて
-
C言語って古いですか?
-
C言語関数違いについて。
-
逆コンパイルと逆アセンブルの...
-
プログラムの実行時に'<'でリダ...
-
パソコン
-
CPUが16bitでも32bitOSでコンパ...
-
Python、プログラミングについ...
-
だれがとけるの?
-
バッチファイルで以下のような...
-
Notepad++の関数リスト表示の変...
-
VisualStudio2022でC言語プログ...
-
License='MIT' ってなんでmitな...
-
C言語 ストリームについて。
-
c言語でイベントフラグを使った...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
c言語
-
gccを行ってもexeファイルが生...
-
大量のデータを読み込んで表示...
-
visual studio 2022でのC#プロ...
-
C++でデスクトップGUIアプリ開...
-
【C言語】全角文字の配列を、全...
-
Windows Formアプリからコンソ...
-
VisualStudio2022でC言語プログ...
-
C#でログファイルにファイルパ...
-
C#でTreeViewのCheckBoxのサイ...
-
c#のTLS1.2での通信について
-
VisualStudioでC++クラスを追加...
-
C言語について。
-
int16_t の _t は何?
-
プログラマー達は何故、プログ...
-
逆コンパイルと逆アセンブルの...
-
C言語の関数のextern宣言
-
c言語でイベントフラグを使った...
-
C言語 関数、変数の宣言について
-
[C言語]fputsとfprintfの違い
おすすめ情報