🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

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

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

Q&A

解決済

3回答

1388閲覧

char型のポインタについて。の基礎勉強。

carnage0216

総合スコア194

C

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

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

0グッド

0クリップ

投稿2021/02/18 10:08

編集2021/02/18 17:02

基礎勉強をしたく、以下のプログラムにコメントを付けました。
黒い★のついている部分は自信がない疑問のある部分です。どうか正しいかどうかを問いたいです。

#include <stdio.h> #include <string.h> int main(void) { char str[] = "12345""\0""67890", * p;//★ここでpを先頭のアドレスの文字コードを扱う定義にした。 //しかしなぜこのように書けたのか正直わかりません。というのもchar str[]は「文字列の文字コード」のデータを扱う //わけじゃないですか。ってことは* p自体もアドレスではなくアドレスの「先頭の文字コード」のデータを扱うため //これでもうまくいくのかなと自己解釈しています。 p = strchr(str, '\0');//strchrを扱う際はstrの文字列の先端のアドレスが入る。そして、アドレスを+1していき //'\0'を見つけたら、その'\0'のアドレスをポインタpに渡す。 printf("|%s|\n", str);//文字列の最初のアドレスから'\0'となった部分までがstrにとなるので //printfより先頭のアドレスから'\0'までの文字列が入る。そして表示される。 printf("|%s|\n", p + 1);//そのポインタpが(printfのp + 1により)+1されたことで'\0'が今現在の //ポインタの指すアドレスとなり、そのアドレスからnullまでを表示した。 *p = '!';//そして、今現在のポインタが指すアドレスは'\0'になったアドレスであるため、 *p = '!' //により'\0'は'!'に置き換えられた。 printf("|%s|\n", str);//そのため結果が|12345!67890|となる。 }

以下は元のプログラムです。

*#include <stdio.h> #include <string.h> int main(void) { char str[] = "12345""\0""67890"; char* p = strchr(str, '\0');//★strchr関数によりchar* pはポインタpとして扱われ、上と同じような処理になる //しかしstrchr関数によりchar* pでありながらポインタのアドレス、ではなくアドレスの指す数値を扱うというのに違和感があります。 //これはstrchr関数がそういうものだからと納得するしかないのでしょうか? printf("|%s|\n", str); printf("|%s|\n", p + 1); *p = '!'; printf("|%s|\n", str); }

元のプログラムに関しては**char* p よりpはアドレスの「数値」を扱う変数だと認識しています。
そしてchar
p = strchr(str, '\0');は文字列を検索する中でnullに当たったら、そのnullとなる「アドレス」を*pに渡す意味していますが、*pはアドレスの「数値」を受け取る変数なのに、なぜnullとなる「アドレス」を渡せるのかわかりません。**色々調べましたがそういうものだからという感じの説明が多く納得が出来ませんでした。
個人的には先頭のアドレスを渡すのでchar p = strchr(str, '\0');が正しいと思っていました。

プログラムに書いた黒い★の疑問と黒い太い線で書いた疑問に詳しく優しく教えてくださると大変ありがたいです。

編集

#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); }

をcやc++でデバックしたら、char* p = strchr(str, '\0');の部分がcharとpになると思ったらcharとpに分かれていました。

理由がわかりません。
なぜこのように分かれたのでしょうか?strchr(str, '\0');からの返り値のアドレスを受け取るためでしょうか?
また、char*型とはポインタ型のcharだとわかりました。しかし、だからなんだという感じで、ポインタ型のcharとはなんなのですが?

どうか基礎を固めたいのでどうかよろしくお願い致します。

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

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

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

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

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

Zuishin

2021/02/18 10:10

これは基礎じゃなくて応用。
Zuishin

2021/02/18 10:21

https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10239054510 > 単語も文法も分からずにどうやって文章を読むのさ?? > 悪いことは言わんよ、Cの文法を1から学び直しなさい。 > やっぱり問題に取り掛かる前に基礎をやったほうが良いです。 > 足し算が分からないのに、高校の数学の問題集をいくら眺めても、効率が悪すぎます。 コードを書く前に文法を学べとここでも言われている。
fana

2021/02/19 01:48 編集

別の質問でも str[i++] みたいな記述にさんざん振り回されていたようですが…… せめて,自身が現在相手にできるようなコードを相手にしてはどうか. 例えば,今回, > char str[] = "12345""\0""67890", * p; とかいう,「可読性を犠牲にしてでも1行に収めるぜ!」みたいな記述を2行に分解するところから回答されているけども,これは主題たる「ポインタの基礎がどうの」とは外れた事柄. 何故毎度わざわざ,こういったXXXな(←うまい表現がわからんが)コードをどこからか選んで持ってくるのか? もっと素直なコードを相手にしてればそういう余計な手間も無いだろうに. …とか思う. (多分,strchr とかを使うのも早すぎる.そういう余計なものが入っているコードは今相手にすべきじゃない)
Zuishin

2021/02/19 07:13 編集

> 何故毎度わざわざ,こういったXXXな(←うまい表現がわからんが)コードをどこからか選んで持ってくるのか? https://teratail.com/questions/322641#reply-447751 > char s[] = "abc", *str = s; ではコンパイルエラーになりません。 これだと思います。コンパイルエラーにならないから持ってきたのではないでしょうか。
guest

回答3

0

基礎を勉強したいのであれば、きちんと基礎を勉強した方が良いです。

元のプログラムに関してはchar* p より*pはアドレスの「数値」を扱う変数だと認識しています。

宣言文の*はあくまでもその変数がポインタ変数であることを示すためのものです。
*pという変数があるわけではなく、あくまで変数名はpであり、その型がchar型へのポインタとなります。
(通称char*型(そういう型があるわけではないのですが))

アドレスの「数値」

という言い方に一抹の不安があるのですが…それは、ポインタが指す先にある「数値」という意味ではないですよね?もしそうであれば、違います。
ポインタはアドレスを扱う変数です。アドレスも一種の数値であるため、アドレスの「数値」という表現も間違いではないのですが…

*pはアドレスの「数値」を受け取る変数なのに、なぜnullとなる「アドレス」を渡せるのかわかりません。

というわけで、どうやら前者のようですので、それは違います。
上で書いたように、ポインタ変数であるpはアドレスを受け取るための変数ですので、
strchr()が返すアドレスを受け取って格納するのには、何の問題もありません。

cやc++でデバックしたら、char* p = strchr(str, '\0');の部分がcharとpになると思ったらcharとpに分かれていました。

理由がわかりません。

最初に書いたことがまさにその理由です。
char *pという宣言は、char型へのポインタ(char*)型の変数pを宣言するためのものであり、
char型の変数*pを宣言するためのものではないので、そのような表示になっています。

また、char*型とはポインタ型のcharだとわかりました。しかし、だからなんだという感じで、ポインタ型のcharとはなんなのですが?

いや、だからなんだと開き直ったところでコンピュータ相手にはまったく意味が無いですよ。

ポインタ型のcharではありません。char型へのポインタという型です。
ポインタは確かにアドレスを格納する変数ですが、それはポインタの機能の内のほんの一部です。
ポインタにはこのほかにも、格納したアドレスの先へとアクセスする間接参照
アドレス計算をアシストするポインタ演算という重要な役割があります。
その為に、ポインタには、**「基となる型」**というものが必要なのです。
この場合は、charがそれになります。

こういうことを質問から断片的に知るよりも、
まずは、入門書等で、体系的に基礎を勉強した方が、何倍も速く理解ができると思います。

投稿2021/02/18 18:20

amiya

総合スコア1218

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

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

m.ts10806

2021/02/19 07:57

>入門書等で、体系的に基礎を勉強した方が、何倍も速く理解ができると思います。 この質問者はこの手の指摘をすべてスルーし続けてここに至っています。過去質問を見渡すだけでも、何年も成長してないのがわかります。 (マルチポスト、複数アカウント所持の常習犯です。手を差し伸べる必要ありません)
guest

0

C

1char str[] = "12345""\0""67890", * p;

C

1char str[12] = {'1', '2', '3', '4', '5', '\0', '6', '7', '8', '9', '0', '\0'}; 2char *p;

と同じです。ここまでは理解できますか?なぜ同じなのかを説明できますか?できるようであれば、続きを書きます。できないのであれば、なぜ同じになるのかを説明します。

投稿2021/02/18 10:27

raccy

総合スコア21737

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

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

carnage0216

2021/02/18 11:18 編集

ご協力ありがとうございます。 char str[] = "12345""\0""67890", * p; も char str[12] = {'1', '2', '3', '4', '5', '\0', '6', '7', '8', '9', '0', '\0'}; char *p;も char型であるため同じです。すなわちchar型は文字コードを扱います。そして*pはアドレスの指すメモリに入った値(一つの値、例えば1や36や100など)や文字(AやSなどの一文字)などを扱います。そのためchar str[] = "12345""\0""67890", * p;のように書くことが可能です。
kazuma-s

2021/02/18 11:10 編集

> どちらもchar型であるため同じです。 「どちらも」とは、何と何ですか? raccyさんが同じと言っているのは 2 つのコードの意味が同じだと言っています。
carnage0216

2021/02/18 11:19

修正しました。二つのプログラムと言う意味でどちらもと伝えたのですがわかりにくくてすいません。
raccy

2021/02/18 11:26

「二つのプログラムのどちらもプログラム自体がchar型である」という意味ですか?
carnage0216

2021/02/18 12:11

理解が甘くてすいません。 なぜ同じになるのかを説明して頂きたいです。どうかよろしくお願いいたします。
raccy

2021/02/18 14:02

「文字列リテラル」はわかりますか?「はい」か「いいえ」でお答えください。
raccy

2021/02/18 15:14

申し訳ありませんが、いくら解説しようにもそれを理解するための基礎知識が足りません。「文字列リテラル」について最低限下記に書いてある知識が必要です。 https://docs.microsoft.com/ja-jp/cpp/c-language/c-string-literals?view=msvc-160 また、「文字列リテラル」を「文字」の「配列」の「初期化子」に使用した場合の動作についても最低限下記に書いてあることが理解できる知識が必要です。 https://docs.microsoft.com/ja-jp/cpp/c-language/initializing-strings?view=msvc-160 これらについて知らない人に、理解できるように説明することは私にはできません。
guest

0

ベストアンサー

次のすべての質問にお答えください。

Q1: char s[4] = { 97, 98, 99, 0 }; のように変数の宣言と同時に初期化できることを理解していますか?

Q2: char s[4] = { 'a', 'b', 'c', '\0' };
char s[] = { 'a', 'b', 'c', '\0' }; と書けることを理解していますか?

Q3: char s[] = { 'a', 'b', 'c', '\0' };
char s[] = "abc"; と書けることを理解していますか?

Q4: "abc""ab" "c" と書けることを理解していますか?

Q5: int a; int b;int a, b; と書けることを理解していますか?

Q6: char s[] = "ab""c"; char *p;char s[] = "ab""c", *p; と書けることが理解できませんか?

追記
質問に書かれている記述が解読不能なので、私独自の説明をします。

char str[] = "12345""\0"67890";
これは、変数宣言である。
メモリ上に 12バイトのデータ領域を確保する。
その領域に str という名前を付ける。
str[0] = '1', str[1] = '2', str[2] = '3', str[3] = '4', str[4] = '5'
str[5] = '\0',
str[6] = '6', str[7] = '7', str[8] = '8', str[9] = '9', str[10] = '0'
str[11] = '\0'
このように初期化される。
配列変数 str には、文字列が入っている。
文字列は '\0' で終わるデータである。
str には、"12345" という文字列と "67890" という 2つの文字列が入っている。

str は変数である。
str の型は、「char [12]」という配列型である。
str のサイズは、sizeof(str) で求まり、12 である。
配列の要素 str[0] の型は char である。

str を式の中で使うとき、その値は &str[0]、すなわち str[0] のアドレスとなる。
アドレスは値である。
値に、別の値を入れることはできない。
str = 'a'; はできない。
&str[0] = 'a'; もできない。
str[0] = 'a'; はできる。
str[0] は名前ではないけれど変数のようなものである。
変数であるから値を持つ。最初は '1' の値を持っていたが、
代入により 'a' の値を持つようになった。

「char str[]は「文字列の文字コード」のデータを扱うわけじゃないですか。」
文字列の各文字を扱うには str[i] という式でアクセスするしかない。

Q7: ここまでは全部理解できましたか?

char *p;
これは変数宣言である。
メモリ上に 8バイトのデータ領域を確保する。
その領域に p という名前を付ける。
初期化は行われないので、ポインタ変数 p の値は、ゴミ(不定) である。
p は変数である。
p の型は、「char *」というポインタ型である。
p のサイズは、sizeof(p) で求まり、8 である。

p を式の中で使うとき、まず、
どこかにある char型の変数のアドレスを入れないといけない。

p = str; とすれば、*p は str[0] である。
p = &str[0]; としても、*p は str[0] である。
p = &str[5]; とすれば、*p は str[5] である。
p = str + 5; としても、*p は str[5] である。
p = &str[5]; p++; とすると、*p は str[6] である。

p = strchr(str, '\0'); を実行すれば、
関数 strchr は、配列変数 str の中に入っている文字列 "12345" の
終端文字 '\0' を探し、&str[5] を返す。
代入演算子 = は、strchr が返した &str[5] の値、
すなわち「str[5] のアドレス」をポインタ変数 p に入れる。
p の値は &str[5] になり、*p は str[5] である。

Q8: ここまでは全部理解できましたか?

追記2
いつのまにか質問が編集されて、新たな質問が追加されていました。

char* p = strchr(str, '\0');の部分がcharとpになると思ったらcharとpに分かれていました。理由がわかりません。なぜこのように分かれたのでしょうか?

この場合スペースには何の意味もありません。
char* p = strchr(str, '\0'); と書いても、
char *p = strchr(str, '\0'); と書いても、
char*p=strchr(str,'\0'); と書いても、
char * p = strchr ( str , '\0' ) ; と書いても全部同じです。

strchr(str, '\0');からの返り値のアドレスを受け取るためでしょうか?

char *p = strchr(str, '\0'); は、
char *p; p = strchr(str, '\0'); と同じ動作です。
char *p; *p = strchr(str, '\0'); ではありません。
関数 strchr は型「char *」の値を返します。すなわちアドレスを返します。
ポインタ変数 p にアドレスを入れることができますが、
*p は char型なので、アドレスを入れることはできません。

また、char*型とはポインタ型のcharだとわかりました。しかし、だからなんだという感じで、ポインタ型のcharとはなんなのですが?

「char *」型は、「ポインタ型の char」ではなく、「charへのポインタ」型です。

ポインタ型は、基本型ではなく、派生型なので、
「charへのポインタ」、「intへのポインタ」、「doubleへのポインタ」のように
「~型へのポインタ」という形になります。

int i; i = 12345; int *p; p = &i; というコードがあれば
p は iのアドレスを持っているので、*p という式の評価結果は、変数 i です。
printf("%d", *p);printf("%d", i); と同じです。
*p = 67890;i = 67890; と同じです。

投稿2021/02/18 16:40

編集2021/02/18 19:00
kazuma-s

総合スコア8224

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

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

carnage0216

2021/02/18 16:58

Q6以外は知っていました。 なぜアドレスの持つ値を扱える*pのかについて、*pは一文字の文字や、一つの数値を扱えます。なので 文字を扱うchar s[]の配列に入れても正しく動くのかなと推論しました。
carnage0216

2021/02/18 20:18

ありがとうございます。 >>ポインタ変数 p にアドレスを入れることができますが、 *p は char型なので、アドレスを入れることはできません。 ポインタ変数とはchar *pのことですか?
kazuma-s

2021/02/18 20:25

「char *p」は変数ではありません。 「char *p;」は宣言です。変数宣言です。 変数は p です。 p はポインタ変数です。 「ポインタ変数 p」と書いてあったら、 ポインタ変数は p です。 ポインタ変数が「char *p」であるはずがない。 日本語が読めませんか?
carnage0216

2021/02/18 20:29

分かりました。やっと少し理解できました。ただ質問があります。
carnage0216

2021/02/18 20:47

#include <stdio.h> int main(void) { int arr[5] = { 10, 20, 30, 40, 50 }; int* ip; int i = 0; printf("arr: %d, %d, %d, %d, %d\n", arr[0], arr[1], arr[2], arr[3], arr[4]); ip = &arr[1]; printf("(a) %d (ip = %p)\n", *ip, ip); printf("(e) %d \n", *(&(*ip)));//↓ return 0; } について、int* ip;は変数宣言で、ipは変数です。また*が付いているためipはポインタ変数です。なので*ipで何かしあらのアドレスにある文字や数値を扱ったり、ipの時はアドレスを扱えるわけでしょうか? よくよく考えたら、char* p = strchr(str, '\0');自体の左辺部で変数宣言していて、*が付いているためpはポインタ変数で、なので*pならアドレスの数値や文字が扱えるが、pの時はstrchr(str, '\0');からのアドレスが扱えるわけですね。要はポインタ変数はchar* p の場合で言うpで、ポインタ変数はアドレスを扱える。なのでpと置けてstrchr(str, '\0');からのアドレスを受け取れる。それと同時に変数宣言char* pもしたため、まるで*pがアドレスを受け取るかのような頓珍漢な内容になってしまい、私はそれに振り回されていたわけだと気が付きました。
carnage0216

2021/02/18 20:51

いっそのこと、char *p = strchr(str, '\0'); は、 char *p; p = strchr(str, '\0'); と同じ動作らしいので char *p; p = strchr(str, '\0');書いてくれていれば、char型でpをポインタ変数と定義した。そのためpはポインタとして働き、pはポインタはアドレスを表すため、strchr(str, '\0');からのアドレスが受け取れるとした方がもっと早く解決していたかもしれません。どうもありがとうございました。
carnage0216

2021/02/19 07:24

本当にどうもありがとうございます。今回の件で他のここには上げていない質問も自力で解決できました。
m.ts10806

2021/02/19 07:53 編集

raccyさんの指摘をスルーしてますが、「前提基礎知識の修得」という大問題が解決していません。もう何年もずっと。 「質問して自力で解決」はおかしいですね。赤の他人にアドバイスもらっただけですよね。それは自力ゼロですよね。 本当に自己解決なら自分で回答書いたんですよね。どこですか、幾つか挙げてください。
carnage0216

2021/02/19 10:32

あのちなみに、なぜ #include <stdio.h> int main(void) { char str[] = "123456789";//char str[9] = { 1,2,3,4,5,6,7,8,9 }; int i = 0; for (i = 0; i <= 8; ++i) { str[i]; printf("str[%d]=%p\n %c\n", i, &str[i], str[i]); } } の部分のchar str[] = "123456789";をchar str[9] = { 1,2,3,4,5,6,7,8,9 };に変えてみたところ str[0]=000000C0A072FC10 1 str[1]=000000C0A072FC11 2 str[2]=000000C0A072FC12 3 str[3]=000000C0A072FC13 4 str[4]=000000C0A072FC14 5 str[5]=000000C0A072FC15 6 str[6]=000000C0A072FC16 7 str[7]=000000C0A072FC17 8 str[8]=000000C0A072FC18 9 だった結果が str[0]=000000AED69FFCA0  str[1]=000000AED69FFCA1  str[2]=000000AED69FFCA2  str[3]=000000AED69FFCA3  str[4]=000000AED69FFCA4  str[5]=000000AED69FFCA5  str[6]=000000AED69FFCA6 str[7]=000000AED69FFCA7 str[8]=000000AED69FFCA8 となりました。なぜでしょうか?
m.ts10806

2021/02/19 10:34

「解決した」と「理解した」を別にしてしまってる人には難しいのではと。 これだけ丁寧な解説とやりとりをしてもらっててなぜ分からないかが分からない。私、C言語未経験ですけど、今回の説明で十分すぎるほど分かりましたよ。
fana

2021/02/19 10:38

'1' と 1 は違う.
m.ts10806

2021/02/19 10:41

結局raccyさんの指摘に戻る。基礎やり直しですね。 いや、基礎やったことがないのは明白だから「直し」じゃなく「やる」です。 早く入門してください。ずっと門の前で自分勝手に絵を描いてるだけです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問