重要なお知らせ

「教えて! goo」は2025年9月17日(水)をもちまして、サービスを終了いたします。詳細はこちら>

【GOLF me!】初月無料お試し

#include <stdio.h>
#include <string.h>int main(void)
{
char str[] = "12345""\0""67890";

char* p = strchr(str, '\0');

printf("|%s|\n", str);
printf("|%s|\n", p + 1);

*p = '!';
printf("|%s|\n", str);
}

上のプログラムの
char* p = strchr(str, '\0');
のpに*がついていますが、これはpに掛かっているのではないですよね?
個人的にヌルになる手前から最初の文字列の先頭のアドレスが渡せれば良いと考えたので char p = strchr(str, '\0');で良いとしましたがエラーが起きます。
なぜ今回char*が必要なのでしょうか?
strchrのせいでしょうか?なぜstrchrを使うだけ*pになるのか知りたいです。

質問者からの補足コメント

  • ちなみに、char* p = strchr(str, '\0');とは
    文字列からヌルを探すために一アドレスずつ進み、ヌルを見つけたらヌル手前から文字列のはじめまでの文字列の先頭の文字を*pに送るのですか?
    文字列の先頭のアドレスを渡すならわかるのですが、それとも例外で*をつけても文字列の先頭のアドレスを受け取れるように出来ているのでしょうか?

      補足日時:2021/02/18 15:25

A 回答 (5件)

あー、ちなみに、だ。



例えば

char* p, q;

って宣言したとする。
pはポインタになるんだけど、じゃあqはどうか、と言うと実はこれはポインタにならない。単なるchar型の変数になる。
フツーに考えればqもポインタになりそうなモンなんだけど、それが「ならない」ってのがCのポインタ周りの文法の腐ってるトコ。

こういう「直感に反した」事柄がやたら起こるのがCって言語で、結局、実際問題、Cを最初に設計した時に実は「真面目に設計しなかった」と言う事です。
元々「他人に使ってもらおう」とか「普遍性があるように設計しよう」と言う意図では作られてなく、要するに「一時凌ぎで作ってみて」、あとで色々とアドホックに追加が行われた、ってのがC言語の正体です。

ちなみに、文字列の最後は'\0'で〆る、ってのも実装で考えると殆ど最悪の決定なんだけど、これも元々は、オリジナルのC言語が実装されたPDP-7と言うミニコンが扱う文字列がそういう仕様だったからそうなった、ってだけ、です。実は特に意味がない。
と言うか「プログラミング言語の設計に関する深淵なる考慮」でそうなったわけでも何でもない。ぶっちゃけ、やっつけ仕事です。
要するに「別のミニコンで動かす」とか、今みたいにパソコンで使用する、って前提は全く考えてなかった。ですが、PDP-7で扱うには丁度良かった、ってだけですね。
世の中には別の文字列の実装法があり、例えば有名なのが、通称「Pascal文字列」と呼ばれる実装法です。これはかつてCのライバルだったプログラミング言語、Pascalで採用されてる文字列実装法です。
Cで言うと、例えば

char str[] = {'\013', 'H', 'e', 'l', 'l', 'o', ',' ,' ', 'W', 'o', 'r', 'l', 'd', '!'};

みたいな実装方法で、配列の最初に「文字の長さ」を明示させて入れる方式。そうすると終端に'\0'みたいな文字は必要ないし、長さを知る為にシーキングする必要もない。配列の最初の数値をチェックすれば自ずと文字列の長さが分かるわけです。
まぁ、この方法も実は欠点があって、それは原理的には文字列の長さは最大で255しか許容出来ない、と言うモノ(まぁ、バイト設定に依りますが)。
ルーピングでシークして時間がかかっても、原理的にはどんな長さの文字列も(メモリが許す限り)使えるようにするべきか、あるいは長さを制限しても速さを優先すべきか、ってのはなかなか難しいトコですがね。
余談ですが、Microsoft Excelの文字列はこの方法(Pascal文字列)で実装されている、と言う話です。
    • good
    • 0

> これはpに掛かっているのではないですよね?



pにかかってんの。

char* p



char *p

も同じよ?
だから言ったでしょ。「Cのポインタ周りの文法は腐ってる」って。
あと、仕様書読みました?読んで質問してる?

> なぜ今回char*が必要なのでしょうか?

平たく言うとstrchrの返り値がアドレスだから、です。
これもstrchrの定義見てみれば一発でしょう。

strchr():
https://www.k-cube.co.jp/wakaba/server/func/strc …

自分で少しは調べんかなぁ。

> 文字列からヌルを探すために一アドレスずつ進み、ヌルを見つけたらヌル手前から文字列のはじめまでの文字列の先頭の文字を*pに送るのですか?

全然違う。

> ヌルを見つけたらヌル手前から文字列のはじめまでの文字列の先頭の文字を*pに送る

って何が言いたいんだろ。
ポインタpに代入出来るのは「一つのアドレス」のみ。それ以上でもそれ以下でもない。

> char* p = strchr(str, '\0');

で分かるのはpに入ってるのはstrの「\0の位置情報」だけ。
それ以上何も無いですよ。

もう何度言っても理解してないみたいだけど、もう一回書いておきます。

1. C言語には文字列なんぞ無い。
2. 従って、C言語には文字列を纏めて扱うような機能もない。

以上です。

余談:
気の利いたCの入門書だと「文字列」なんて記述は一切せず、「文字配列」と言う言い回しをしてる。
事実上、Cにあるのはそのまま「文字」の「配列」だからだ。
printf("%s", str)が「文字列を扱えるように見える」のは、実際は内部で一文字一文字ルーピングしながら表示してるから。終了条件がstr[i] == 0で動作を止めるからに他ならない。
    • good
    • 0

>個人的にヌルになる手前から最初の文字列の先頭のアドレスが渡せれば良いと考えたので char p = strchr(str, '\0');で良いとしましたがエラーが起きます。



C言語では変数は必ず宣言する必要があります。
宣言することでその変数の型と必要なメモリ領域が確保されます。

今回の場合、文字型変数のポインタを代入する必要がありますので
char* p;
と宣言しなければなりません。このように宣言することでpは文字型変数へのポインタとして決められ、そのデータを格納するために必要なメモリ領域が割り当てられます。
そうしてから
p=strchr(str, '\0');
とアドレスを入力することで初期化します。

で、今回の場合、宣言と初期化を同時にやろうとしています。
この場合はポインタであると宣言するため左辺は
char* p
でなければなりません。*がついていないとpがポインタ型にならないのです。
で、同時に初期化するには、=アドレス、と入力することになります。
一見宣言のためのcharを除くと
*p=アドレス;
となり、おかしく見えますが、これは宣言と初期化を同時にやっているために仕方なく許された書き方なのです。例外的なものなので納得するしかありません。
    • good
    • 0

>ポインターへ返すことが決まりとなってます



訂正
ポインター変数へ返すことが決まりとなってます
    • good
    • 0

Cの関数を解説してるサイトなどで仕様をよく読むようにしてください



char *strchr(const char *s, int c);

となってますのでポインターへ返すことが決まりとなってます

strchr
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/ …
    • good
    • 0

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