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

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

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

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

Q&A

8回答

433閲覧

C言語 ポインタの宣言と代入について

qaz_sd

総合スコア0

C

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

0グッド

0クリップ

投稿2025/05/22 11:34

前提

最近C言語のポインターについて勉強していて、以下のようなことを学びました。

int型の変数aを定義したとき、
aはaに入っている値を、&aはaがあるアドレスを表す。

int型のポインタ*pを定義したとき、
pは自身が差しているアドレスを、
*pは自身が差しているアドレスにある変数の値を表す

さて、以下のソースコードを考えます

c

1#include <stdio.h> 2 3int main(void){ 4 int a = 10; 5 int *p = &a; 6 int *k; 7 k = &a; 8 printf("%d %d",*p,*k);//10 10 9 return 0; 10}

理解できていないこと

ポインタ変数 *kについては理解できます。

まず、 int *kでint型ポイント変数の*kを定義して、
つぎの行でaのアドレス&a*kが指すアドレス(k)に代入。
だから、最後のprintfで*kを出力すると、
*k = kが指しているアドレスにある変数(=a)の値(=10)を出力する。

ところが、*pについて理解ができていません。
プログラムを見ると、*pを宣言するのと同時に代入を行っています。

int *p = &a;
これを見ると、=の左辺 *p は、前提より "自身が差しているアドレスにある変数の値"
=の右辺&aは、前提よりaがあるアドレスを表すことになり、

変数の値にアドレスをぶち込んでいることなります。
これだとコンパイルエラーになるか、printfで変な値が表示されそうですが、
実際にやってみると普通に表示されます。

これはなぜなのでしょうか。

補足情報

C99

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

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

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

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

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

fiveHundred

2025/05/22 11:39

「int」は数値の型で「int *」はアドレスを代入する型です。 まず、その区別は出来ていますか?
jimbe

2025/05/22 21:14 編集

勉強中とのことで仕方ありませんが、質問文の表現に違和感があります。 cにおいてポインタとは『アドレスを入れる変数』のことです。従ってポインタ変数というのは "変数" が重複します。 また、 int *k において定義される変数は k であって *k ではありません。 >aのアドレス&aを*kが指すアドレス(k)に代入 は『aのアドレスをkに代入』としないと、違う意味に取られる可能性があると思います。
guest

回答8

0

int *p = &a;int *型の変数p&aを代入するというものです。

言い換えれば

C

1 int *p; 2 p = &a;

とほぼ同じです。

なので、

*pは自身が差しているアドレスにある変数の値を表す

とは違うものです。

投稿2025/05/22 11:45

fiveHundred

総合スコア10346

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

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

0

すでに多数回答が付いていますが,
*p という記述というのは(一律的に)こういう意味である」という捉え方が間違っているというだけです.
すなわち,

左辺 *p は、前提より "自身が差しているアドレスにある変数の値"

なる「前提」が間違い.

* とか & とかがどういう意味になるのかは文脈依存とでもいうか,そういうものなので.
個々の場所でそれらがどういう文法要素として記述されているのかが異なるということです.

……とはいえ,言うてもせいぜい数パターンくらいしかない(?)感じですから,過度に難しく考えることはないです.

投稿2025/05/23 01:12

fana

総合スコア12164

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

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

0

すでにある回答と重なる内容ですが、このあたりのCの仕様をちゃんと理解する前は、単純な型以外の変数の宣言と初期代入は、分けて書いた方が良い気がします(と言っても、読む場合はしょうがないですが)。

int 「ここに*やら[]やら()やらを含んだややこしい変数xの記述」= 初期値;は、初期値はあくまで変数に代入する物なので、int 「ここに*やら[]やら()やらを含んだややこしい変数xの記述」; x = 初期値;と読みます。
これはもうそう思うしか無い。変数名しか無いint x = 初期値;とかはそのまま同じですが。

Cの変数宣言が、「この変数の型はこれだ」じゃなくて「この変数にこういう記号をいろいろ付加したときに最終的に得られる値の型はこれだ」になっているのは、ちゃんと調べてませんが、おそらくコンパイラのサイズを小さくするための気がします(変数宣言の構文解析処理と式の構文解析処理の共通化)。

あるいは、実際にその式を評価するのと同じ書き方で宣言できる今の形式の方が分かり易いという判断からかも知れません。今のCでint *((*foo)());という変数宣言があったとして、もし「型 変数名」という形式の宣言にCの文法を変更すると、int*((*)()) foo;になりますが、こっちの宣言形式の方が分かり易いという気がしないです。

投稿2025/05/24 17:34

otn

総合スコア86303

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

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

0

=の左辺 *p は、前提より "自身が差しているアドレスにある変数の値"

“自身が指しているアドレスにある変数(値)の型”を示すのが左辺の*pです
「*pが指す値はint型だよ」ということです
このことを事前に明示します
だからこその”宣言”です
こうした例はポインタ以外にも適用されます
これはCにおける変数宣言の基本仕様です

int a=0, *b=&a, c(void), d[]={1,2,3}; int c(){return 0;};

取得される値の型が同一であれば宣言は一括で行えます
pもこの規則に従っているだけで、変数宣言時のpが何かの値を直接返す訳ではありません
尚この宣言形式が分かりにくい場合はtypedefを使用してください

typedef int *INTPTR; INTPTR a,b,c; //全ての変数がINTPTR(int *)型になる

投稿2025/05/24 08:43

編集2025/05/24 08:45
Manabu

総合スコア105

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

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

0

int *p;
という宣言は、ポインタ変数 p を宣言しています。
そして宣言した結果 p の型は int * 型になります。

int *p = &a;
これは宣言している p を &a つまり a のアドレスで初期化しています。
(細かいことを言うと代入ではありません)

なお、
int *p;
の宣言は(入門書で良く説明されている)
型名 変数名;
という構造ではありません。
型指定子(type-specifier) ポインタ(pointer) 変数名(identifier);
という構造です。
pointer は続く identifier がポインタであることを意味しています。

括弧を付けて書けば、あなたの感覚通り
int (*p);
であって
(int *) p;
ではありません。
(実際に C規格準拠コンパイラで確認して見ればわかるでしょう)
そのため
int *p, q;
では q は int 型になります。

参考までに int *p; における宣言部分の Syntax を提示すると次のようになっています。

declaration: declaration-specifiers init-declarator-list_opt ; declaration-specifiers: type-specifier declaration-specifiers_opt type-specifier: int init-declarator-list: init-declarator init-declarator: declarator declarator: pointer_opt direct-declarator direct-declarator: identifier pointer: * type-qualifier-list_opt

投稿2025/05/23 11:50

lehshell

総合スコア1180

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

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

0

いろいろな回答が付いていますが、僕なりの考えです。

int *p = &a;

この左辺で宣言しているのは、「int型ポインタの*pを定義」ではなく「int型ポインタのpを定義」しています。 変数はあくまで p です。
また 、代入という操作は左辺の値に右辺の値を入れることですから、この式を「*p = &a」と同等ととらえることはできなくて、左辺は「int *p」です。 このとき、「int *p」は宣言した変数であるところのpを返す(表す)と考えれば、整合性が取れるようになります。

まあ、複数並べたときにやっぱり変だよねとは思いますけどね。

投稿2025/05/23 01:54

編集2025/05/23 01:56
TakaiY

総合スコア14330

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

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

0

あ〜、これ。Cのクソ仕様の話かな。

当然、

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <inttypes.h> 4 5int main(void) 6{ 7 int16_t a = 10; 8 int16_t *p = &a; 9 int16_t *k = &a; 10 printf("%" PRId16 " %" PRId16 "\n", *p, *k); /* 10 10 */ 11 return EXIT_SUCCESS; 12}

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <inttypes.h> 4 5int main(void) 6{ 7 int16_t a = 10; 8 int16_t *p = NULL; 9 p = &a; 10 int16_t *k = NULL; 11 k = &a; 12 printf("%" PRId16 " %" PRId16 "\n", *p, *k); /* 10 10 */ 13 return EXIT_SUCCESS; 14}

は同じ結果になる、ってのは分かるかな。
まぁ当たり前だけどさ。

んで、実は(ポインタ変数)宣言時のアスタリスクと使用する際の本体でのアスタリスクの意味は違うんだ。
つまり、

int型のポインタ*pを定義したとき、
pは自身が差しているアドレスを、
*pは自身が差しているアドレスにある変数の値を表す

ってのは、だ。

int型のポインタ*pを定義したとき、 <- この時のアスタリスクと
pは自身が差しているアドレスを、
*pは自身が差しているアドレスにある変数の値を表す <- この時のアスタリスクは意味が違う

って事になる。
前者のアスタリスクは宣言時に「ポインタ型」を生成する為の機構、後者のアスタリスクを間接参照演算子っつーんだ。
Cの極悪仕様は、全く違う機能に同じ記号(アスタリスク)を採用している事となり、混乱の元となっている。

つまり、貴方のように(貴方が悪いわけじゃない)、

*pは自身が差しているアドレスにある変数の値を表す

って捉えてると、宣言時の

int型のポインタ*pを定義

の意味が分からなくなるんだ。分からなくなって当然、と言う。
厳密に言うと、この最初の宣言は

int型のポインタ変数pをアスタリスクを用いて定義

と言う意味なんだ。

なお、本当の事を言うと、僕はCは全然得意じゃないんだけど、細かい事を言う人は、与題のコードを次のように書くと思う。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <inttypes.h> 4 5int main(void) 6{ 7 int16_t a = 10; 8 int16_t *p = &a; 9 int16_t *k = NULL; /* ポインタ変数宣言時は必ずNULLで初期化する */ 10 k = &a; 11 printf("%" PRId16 " %" PRId16 "\n", *p, *k); /* 10 10 */ 12 return EXIT_SUCCESS; 13}

多分、「ポインタ変数だけ」で宣言しない。必ずNULLで初期化する。
基本的にCはアセンブリ言語に毛が生えた程度の言語なんで、ポインタ変数の初期値が「何でも良い」的なコーディングは、バグの元なんで避けるんじゃないだろうか。
結果、Cプログラマはこっちの記述形式

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <inttypes.h> 4 5int main(void) 6{ 7 int16_t a = 10; 8 int16_t *p = &a; 9 printf("%" PRId16 "\n", *p); 10 return EXIT_SUCCESS; 11}

を好むんじゃなかろうか。一々宣言時にNULLで初期化しといて、改めて代入、ってのは面倒くさいからね。
ただ、貴方のように初心者で、「書き方を学んでいる」最中なら、どうしてもポインタ変数「だけ」を宣言する場合、NULLで初期化するクセは付けておいた方がいいと思う。
その方が意味は分かりやすくなるしね。

あと、Cが得意で大好きな人とか、あるいは教科書だと圧倒的に与題のような「ポインタ変数宣言」の書き方をするんだけど、僕のようにCがあまり得意じゃない人間とか、あるいはC初心者には次のような書き方の方が向いてると思う。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <inttypes.h> 4 5int main(void) 6{ 7 int16_t a = 10; 8 int16_t* p = &a; /* アスタリスクはintの方に付ける */ 9 printf("%" PRId16 "\n", *p); 10 return EXIT_SUCCESS; 11}

宣言時のデータ型の方にアスタリスクを付ける。「機能が違うんだから書き方も違うべき」って事だな。
もっとも、これもC言語のクソ仕様で(笑)、int *p;と言う宣言とint* p;と言う宣言の意味が全く同じ、ってのはヘンなんだけどね(笑)。

なお、本当はこういう説明はご法度なんだけど、敢えて「教育用言語」として設計されたPascalを使うと、与題のようなコードはこう書く。
Pascalではポインタ絡みはキャレットを使う。

Pascal

1program foo(input, output); 2var 3 a : integer; 4 k : ^integer; {宣言時は前方から} 5 begin 6 a := 10; 7 new(k); 8 k^ := a; {使用時は後方から} 9 writeln(k^); 10 end. 11

Pascalはさすがに教育用、として開発された背景があるんで、この辺の「記法」はCに比べると紛れがない、です。
なお、フリーで使えるコンパイラとしてはFree Pascalが有名です。
(もっとも本当は、Pascal方言のDelphiのクローンが主眼ですが)

Free PascalのIDE、Lazarus

あと、Cのポインタに纏わる話だと、Web上だとこのページが詳しいかな。

以上。

投稿2025/05/22 15:39

編集2025/05/23 07:16
cametan

総合スコア17

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

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

winterboum

2025/05/22 23:33

/* アスタリスクはintの方に付ける */ <== ですよね!
maisumakun

2025/05/23 00:11 編集

> 宣言時のデータ型の方にアスタリスクを付ける。 ただし、これには「int* p, q;」としたときに、qがint型になるという大きなトラップがあります。「int *p, *q;」と書かないといけないのです。
cametan

2025/05/23 05:54

そうなんですよねぇ。 ホント、int* p, q;で書けりゃいいのに、って思います。 実際、人々がint *p;って書く「最大の原因」が、「int* p, q;と並べられないから」だろうし。 困った仕様だ(笑)。
guest

0

int *pという宣言は、int型のポインタを宣言する以上の意味はありません
int aに置き換えた場合、文脈的には以下と等価です

int a; a=10;

*pというのはあくまでintを返す書式の一つに過ぎません
例えば次のように書けます

int a,*p; a=10; p=&a;

これはaと書くか*pと書くとintを返すというルールを定めていると読めます
この内pはポインタとなるので、intを返すためにまずアドレスを取得する必要が生じます

ポインタ変数というのはあくまでアドレスを値として格納する変数であって、pk自身がaのアドレスの格納場所を示すシンボル、もとい識別子となっています
これはa10を格納するデータ領域のシンボルとなっていることと同義です

*pp自身のアドレスを指しているわけではありません
paのアドレスを指しており、int *pはただの変数宣言です
型部分を()で括るなら

(int) a; (int *)p;

となります

投稿2025/05/22 12:47

nanashi123

総合スコア125

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問