質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

2回答

10731閲覧

C言語 文字列の比較と文字列の置き換え

aufheben

総合スコア24

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

0クリップ

投稿2017/11/05 16:04

7つの文字列newsからSadとBadで始まる文字列を探して,文字列ごとCool Newsに置き換えるプログラムを作りたいです。

lang

1#include <stdio.h> 2#include <string.h> 3 4int check(char *); 5 6int main() 7{ 8 int i; 9 char news[7][15] = { "Good News", "Cool News", "Bad News", 10 "Sweet News", "Sad News", "Happy News", "Wonderful News" }; 11 12 for (i = 0; i < 7; i++) { 13 14/*14行目*/ if (check(news[i]) == 1)*news[i] = "Cool News"; 15 } 16 printf("--\n"); 17 for (i = 0; i < 7; i++) printf("%2d %s\n", i, news[i]); 18 printf("--\n"); 19 20 return 0; 21} 22 23 24int check(char *str) 25{ 26/*27行目*/ if (strncmp(*str, "Sad", 3) == 0 || strncmp(*str, "Bad", 3) == 0) { 27 return 1; 28} 29 else return 0; 30 31} 32

初心者なりにいろいろ考えてみたのですが、
(14): warning C4047: '=': 間接参照のレベルが 'char' と 'char [10]' で異なっています。
(27): warning C4047: '関数': 間接参照のレベルが 'const char *' と 'char' で異なっています。
(27): warning C4024: 'strncmp': の型が 1 の仮引数および実引数と異なります。
この3つのエラーが出てしまいます。
このエラーについて検索してみたりしたのですが納得できる説明は得られませんでした

ポインタや文字列等については知識がまだ浅いのでご教授いただければありがたいです。
あと、諸事情でcheck関数内とforループの中以外には手を加えられません

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

まず確認

C言語では、文字列は文字の配列として表現されます。
基本的には、文字列と言うと末尾にヌル文字がある、ヌル終端文字列を指します。

C

1char str[] = "Hello"; // ← 追加されるヌル文字を含めると6文字 2for(int i = 0; str[i] != '\0'; i++) { 3 printf("%c", str[i]); 4} 5printf("\n");

このプログラムは、標準出力に『Hello』と表示します。


C言語では、配列の変数名を評価すると先頭の要素のアドレスになります。
実例を出すと、strと&str[0]の返すアドレス値は同じです。

C

1char str[] = "Hello"; 2printf("%s, address=%p\n", str, str); 3for(int i = 0; str[i] != '\0'; i++) { 4 printf("%5c, address=%p\n", str[i], &str[i]); 5}

実行結果(出力は実行ごとに変わり得ます)

Hello, address=0061FF26 H, address=0061FF26 e, address=0061FF27 l, address=0061FF28 l, address=0061FF29 o, address=0061FF2A

C言語では、ポインタ型変数に単項演算子*を適用することで、実体を参照できます。
これは説明不要だと思いますが、一応コードを載せます。

C

1char moji = 'A'; 2char *p_moji = &moji; 3 4printf("%c, adress: %p\n", moji, &moji); 5printf("%c, adress: %p\n", *p_moji, p_moji);

実行結果(出力は実行ごとに変わり得ます)

A, adress: 0061FF2B A, adress: 0061FF2B

C言語では、文字列リテラルは先頭文字のアドレスを返します。
リテラルという言葉は聞きなれないかもしれません。
日本語では即値といい、つまり『直接プログラム内に書かれた値』です。"Hello"とか。

C

1const char *str = "Hello"; 2printf("%s, address: %p\n", str, str);

配列とポインタの類似性について気付いたかもしれませんね。
これらは不可分の関係です。配列を扱う上ではポインタは必須なのです。

また、constは定数の意味で、『この値は変わりませんよ』ということを指します。

確認したことを総括すると

次のプログラムを実行すると、Hが出力されます。挙動が理解できるでしょうか。

C

1char str[6] = "Hello"; 2printf("%c\n", *str);

最近似たような質問があったので、そちらを見ると理解が深まるかもしれません。
teratail - 文字列とポインタについて(コード短いです)

ミニマムプログラム

ご提示のコードは少し煩雑なので、全く同じ警告を出すミニマムプログラムを提供します。

C

1#include <stdio.h> 2#include <string.h> 3 4int check(char *str) 5{ 6 if (strncmp(*str, "Sad", 3) == 0) return 1; 7 if (strncmp(*str, "Bad", 3) == 0) return 1; 8 else return 0; 9} 10 11int main() 12{ 13 char news[15] = "Good News"; 14 15 if (check(news) == 1) { 16 *news = "Cool News"; 17 } 18 19 printf("--\n"); 20 printf("%s\n", news); 21 printf("--\n"); 22 23 return 0; 24}

これを用いて、警告について考えていきましょう。


warning C4047: '=': 間接参照のレベルが 'char' と 'char [10]' で異なっています。

*news = "Cool News";の個所に対する警告です。
両辺の型がそれぞれどうなっているのか、しっかり考えてみるといいです。

  • 左辺: newsは先頭文字のアドレスを返し、*で解決するので、char型。
  • 右辺: "Cool News"は先頭文字のアドレスを返すので、*char 型。

また、配列はそれ自体をごっそり入れ替えることが出来ません。
指し示す先を変更したいなら、文字通りポインタを使うのがよいです。

よって、以下のように書けばよいです。

C

1const char *news = "Good News"; 2... 3 news = "Cool News";

warning C4047: '関数': 間接参照のレベルが 'const char *' と 'char' で異なっています。
warning C4024: 'strncmp': の型が 1 の仮引数および実引数と異なります。

この二つはひっくるめて考えた方が自然でしょう。
strncmpの使い方(引数の与え方)を誤っているという警告です。

strncmpの引数は次の通りです。引用元:C言語関数辞典 - strncmp

C

int strncmp(
const char *s1,
const char *s2,
size_t n
);

まず、ご提示のコードのstrncmp(*str, "Sad", 3) == 0は誤りです。
第一引数をよく見てください。char型の値を渡していますよね?

また、strncmpは第一引数にconstな文字列を要求しています。
それではstrの型をどこで調整すればいいかと言うと... checkの定義です。
check内でstrの内容を変更しないのですから、const char*を受け取ってしまえばいいです。

C

1int check(const char *); 2... 3int check(const char *str) {

結局どう書けばいいのよ?

次のように書けばよいです。gccで動作確認済みです。

C

1#include <stdio.h> 2#include <string.h> 3 4int check(const char *); 5 6int main() 7{ 8 const char *news[7] = { 9 "Good News", "Cool News", "Bad News", 10 "Sweet News", "Sad News", "Happy News", "Wonderful News" 11 }; 12 13 for (int i = 0; i < 7; i++) { 14 if (check(news[i]) == 1) { 15 news[i] = "Cool News"; 16 } 17 } 18 printf("--\n"); 19 for (int i = 0; i < 7; i++) { 20 printf("%2d %s\n", i, news[i]); 21 } 22 printf("--\n"); 23 24 return 0; 25} 26 27int check(const char *str) 28{ 29 if (strncmp(str, "Sad", 3) == 0) return 1; 30 if (strncmp(str, "Bad", 3) == 0) return 1; 31 return 0; 32}

実行結果

-- 0 Good News 1 Cool News 2 Cool News 3 Sweet News 4 Cool News 5 Happy News 6 Wonderful News --

ここまで指摘した箇所以外では、次の箇所を書き換えています。
(変更できませんとおっしゃっている部分も含みます。ちょっと見てられないからです。)

  • newsの宣言を変更し、ポインタ配列としています。

先ほど申し上げた理由の通り、必須です。
しかし、strcpyを利用すれば、この限りではありません。

  • iのスコープを変更しています。

カウンタ変数はfor文の初期化部分で宣言することを強くお勧めします。
よほど古いコンパイラでなければ問題なく使えるはずです。

  • check関数内のロジックを少し変更しています。

経験を積むうちにわかると思いますが、論理和が可読性を落とす状況はままあります。
早期リターンを適切に用いることで、『条件のふるい落とし』が出来て便利です。

おわりに

いかがでしょうか。
ずらずら書いておいて難ですが、たぶんすぐには理解できないと思います。

C言語のポインタは、慣れるまでは非常に難解に感じるでしょう。
しかし、システムの本質的な挙動を垣間見ることが出来るので、非常に役に立ちます。
大変だと思いますが、頑張ってくださいね。

おまけ

一般に配列の要素を巡回する場合、次のように書くと保守性が高い。

C

1for(int i = 0; i < sizeof(arr)/sizeof(*arr); i++) { 2}

配列とポインタを混同すると上記記法が理解できないので、今回は割愛した。

投稿2017/11/06 04:12

編集2017/11/06 04:54
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aufheben

2017/11/06 05:50

#include <stdio.h> #include <string.h> int check(char *); int main() { int i; char news[7][15] = { "Good News", "Cool News", "Bad News", "Sweet News", "Sad News", "Happy News", "Wonderful News" }; for (i = 0; i < 7; i++) { if (check(news[i]) == 1) { news[i][0] = 'C'; news[i][1] = 'O'; news[i][2] = 'O'; news[i][3] = 'L'; news[i][4] = ' '; news[i][5] = 'N'; news[i][6] = 'e'; news[i][7] = 'w'; news[i][8] = 's'; } } printf("--\n"); for (i = 0; i < 7; i++) printf("%2d %s\n", i, news[i]); printf("--\n"); return 0; } int check(char* str) { if (strncmp(str, "Bad News", 3) == 0||strncmp(str, "Sad News",3)==0) { return 1; } else return 0; } CoolがCOOLになっていますがこれで正しいです。 上記のようにして欲しかった結果を得て、自己解決しました。 丁寧な回答ありがとうございました。
LouiS0616

2017/11/06 05:54

結局どこまで理解しているのか不明瞭ですが、動けばいいのならそれでいいでしょう。
guest

0

自己解決

14に対しては*news[i]=*news[1]とすることで解決しました

投稿2017/11/05 16:08

編集2017/11/05 16:10
aufheben

総合スコア24

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

LouiS0616

2017/11/06 04:37

確かに警告は消えますが、抜本的な解決になっていないです。 私の回答の当該部分を*news[i]=*news[1]に置き換えると、次のように出力されます。 -- 0 Good News 1 Cool News 2 Cad News 3 Sweet News 4 Cad News 5 Happy News 6 Wonderful News -- なんでCoolじゃなくてCadになるかは、回答を見て考えてみてください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問