
C言語を勉強中です。
ポインタは宣言しなくても、*印をつけるだけで使えるものなのでしょうか?
下記のプログラムが問題なく動くのはなぜなのか、どなたか教えてください。
(それともこれはポインタではない…??)
どうぞ、よろしくお願いいたします。
-----------------
#include <stdio.h>
int main(void)
{
int values[10];
int i;
for( i = 0 ; i < 10 ; i++ )
scanf( "%d", values + i );
for( i = 9 ; i >= 0 ; i-- )
printf( "\n%d", *( values + i ) );
return 0;
}

No.2ベストアンサー
- 回答日時:
*( values + i ) に現れる * は、「間接演算子」という名前の演算子です(単項の演算子ですね)
演算子ですので、有効なものにつければ、正しい値を返します。
さて、
int values[10];
と配列を宣言したとき、valuse という「配列名」は、その先頭配列を差すポインタになります。
なので、valuse というもの自体が、(配列を宣言したので)ポインタとして扱われます。
(values + i) という式は、「ポインタ + 整数」です。
この式は、「ポインタを、その要素(この場合は、int) i 個分だけ進めたところにあるポインタ」を返します。
※valuse[i] の存在するアドレスと同じ。
最後に、*( values + i ) で、「その場所にある値」を返します。
で、非常にややこしいのは、
int i; に対する int *ptr; という宣言に現れる * は、「ポインタ宣言子」という、ポインタを宣言することを示す記号です。
なので、宣言の時に現れる * と、*( values + i ) に現れる * は、別のものということになります。
ポインタ変数とは別物の、間接演算子というものなのですね。
奥が深い…というか、ビギナーには紛らわしいものですね。
ポインタをようやく乗り越えたと思っていたのにまたつまずきました。(汗)
わかりやすい回答をありがとうございました!
No.3
- 回答日時:
トラディショナルC言語は、アセンブリ言語(マシン語、ネイティブコード)が
主流であった時代に意識されたものですから、配列の先頭とポインタは同じ扱いになります。
values[10] は、*values の使用を許可しており、
*values の定義は、values[] の記述を許可している意味になります。
アセンブリ言語では、[]のような便利なものが無く、ポインタしか存在しません。
これに対して、
「[]って記述したら、ポインタを上手に使って、プログラマの意図したようにコンパイルします。」
というのが売りです。
当時のプログラマは、これに感心して、
「便利だわ~。俺もCに乗り換えようかな。」
とか思っていたわけです。
当時のユーザーはポインタのほうが考えやすかった。
こういうユーザー配慮がある以上、ポインタのほうが優先されるわけです。
昨今では、この使い方が危険だとし、ワーニングを出したり、禁止するコンパイラもあります。
この禁止の思想はコンパイラや規格により方言として扱うのが宜しいでしょう。
同様にC++も、C言語になれた人に如何にしてアピールするか悩んで作られています。
このために、構造化とオブジェクト化の敷居が曖昧であり、
オブジェクト言語としては使いづらいものになっています。
このように、
言語仕様と言うのは、常に不完全であり、そのたびに新しい言語が登場しています。
初心者ですと、
「この様に書いたら、だめだった。」
と言う部分で悩むでしょうから、
「どの様に書いたらよいでしょうか?」
と考えてしまい、視野が狭くなります。
その前には、
・この言語は何に適しているのか?
・パターンと成る記述方法(考え方)はどんなものか?どんな特徴があるのか?
・何を苦手とするのか?
と捉えないといけません。
プログラミングの本質を大雑把に理解し、
開発直前に言語仕様を調べて開発を行い、終わったらすっかり忘れる。
これが正しいあり方です。
言語を学ぶにあたり、記述方法を覚えていくと限界があります。
何を想定して作られた言語なのかを知ると良いと思います。
そうすると、
・きっとこうなっているだろう。
・と思っていたら、そうじゃなかった。
という二つの発想で覚えていくことが出来ます。
例えば、初心者向けの本にはprintfと相対して、scanfが良く記述されています。
しかし、慣れてきますと、
scanfの変換動作がわかりづらく、これをパワーユースすることを嫌うようになります。
C言語を使う人は、文字列をメモリ情報として扱い、これを直接操作して、
文字列操作を行うことを好みます。
printfは、メモリ情報を文字列に変換してそのまま出力デバイスに送信します。
デバイスを直接操作する部分は、OSに任せたいところですので、
最初のほうは好意を持ってしようします。(ファイル操作ぐらいまでは)。
しかし、
C言語が本当に活躍するのは、多様な出力デバイスを扱い、
リッチでスピード感のあるアプリケーションやハードウェア操作をするとき
ですから、
printfは、存在そのものとして価値がなくなってしまいます。
(コンソール画面に文字しか出せないものですから)
この様に、言語そのものの仕様というのは、個人のスキルの上達には直接関係なく、
言語が何に適しているかが大事に成ります。
C言語の場合は、実際のコンピューター上のメモリを直接読み書きする思想です。
例えば、
あるメモリ領域がVRAM(画面情報をセーブしたメモリ)であると想定した場合、
これを0で埋めてしまえば(大概の仕様では)画面が黒色で埋まります。
このときに、画面上での座標はVRAMの先頭アドレスから計算して何バイト目に
相当するか計算できます。
横が2048ドットであり、1ドットが32ビット=4バイトであり、
横列終わった後、次の行が続いているとします。(大概はそうなっている。)
x=15、y=10とした場合、(共に0から始まるとする)
オフセットアドレス = ((2048*10)+ 15 ) * 4;
となりますよね。
これにより、x、y座標が何バイトめになるかわかります。
そこに直接カラー情報を書き込めば、描画プログラムを作ることも可能です。
この様に、C言語はメモリを直接操作したり、これを繰り返してループする場合
に優れています。
そして思想と言うのがあります。
例えば文字列操作で、文字列を分割する操作があったとします。
splitなどと良く言われています。
”abc,def,ghi,jkelm"
という文字列から','で区切られた文字列を取り出す場合、
①元の文字列を配列にコピーする。
②出力となる文字列アドレスの配列を定義する。
③①の配列を1文字ずつ調べていき、','を見つけたら0で埋める。
④③と同時に、0で生めた場所+1のアドレスを②の配列に記録していく。
(注:最初の文字が','でなければ、元の文字列の先頭を②にあらかじめ入れておく)
⑤①と②を出力として返却する。
(注:返却する①②の配列の上限は予め定め、仕様とし、オーバーしないようにする)
このとき、
もとの文字列を明示的に*つきのポインタとして受け渡し、
コピーした左記の①は配列としておき、[]で操作する。
②も[]で操作します。
返却する場合は、[]で返したいところですが、これが(つまり「あれ?できないぞ」)
仕様上無理なので、呼び出し側が[]で確保しておき、ポインタを与えます。
int stringSplit( char **outputStrings , char *outputStringsBody , char *inputString , char splitChar , int maxOutStrings )
outputStrings: 出力文字列の先頭アドレス配列 ②
outputStringsBody:分割された文字列の格納先 ①
inputString:分割したい文字列
splirtChar:分割するタグに沿うとする文字
maxOutStrings:分割したとき、バッファが溢れないように制限した、最大文字列数
リターン値として、分割された文字列数を返します。
みたいな関数として作成できます。
こうした関数は、既に質問者さんが知る言語仕様だけで作成できます。
使い勝手もよろしくて、個人で作製されている人も多数いるでしょう。
C#では、string.Split( ',' )のように記述できます。
あらかじめライブラリとして用意されていると言う事です。
もちろん、for文を使用して自分で作成できますが、C#やJavaなどはC言語と違い、
直接のデータ操作が苦手です。(ループ記述での繰り返しは比較にならないくらい遅い)。
そのため、多くのライブラリを用意し、なるべくループ動作をしないように配慮しています。
C言語を使う人は、これらを嫌い、全部自分で作ります。
これ以外の言語は、ライブラリ仕様をどれだけ良く知っているかという部分が大事になります。
思想と言うのはこの様に言語により違ってきます。
C言語を使う場合は、最初の導入の記述を覚えたら、
・配列とポインタ操作で、如何にして便利な自分ライブラリが作れるか?
に進み、
多言語にあるライブラリ(もちろんC言語でも)に相当するものを、
自分で作ろうとしてテーマにし続けることが大事です。
つまり、C言語は覚えることが少ないのであっけなく、習得は終わります。
その後が長いわけです。
どこまで、
「他人が作った物と同じモノを自分だけで作れるか?」
だけが上達になります。
もう、大体の記述を覚えているようですから、
文字列操作系のライブラリを全部自分で作ることをお勧めします。
ベンチマークと言うのがありまして、
(数万回実施したときとか)、どのくらいの時間差になるのかを、
自作の場合と比較します。
こうしたものしか利点がありませんので、
既存ライブラリより早いスピードで動くものを作れない場合は、
C言語をつかう意味はあまりないと言って良いでしょう。
上達とは、これを指しますので、文法上での検討は、わからないとき本を読む程度にし、
数日でおわらせて、(繰り返しメモリ操作での)ライブラリ自作を死ぬほどやってください。
文法上では多くのC言語プログラマが知っていることは、もう知っているはずです。
ここから如何にしてLinuxが作れるのか?
如何にしてSkypeが作れるのか?
はたまた如何にしてオンラインゲームが作れるのか?
そこに秘匿されたノウハウがあり、壁になるのです。
通信を扱うアプリケーションなどでは、TCP/IPそのものが適していない場合があります。
こういうときは、UDPなどからTCPに相当する通信スタックを自作します。
これを数日で出来ないようですと、仕事そのものが納期に間に合わない場合もあります。
例えば、音声のコーダ(圧縮)が高くて、自作したことがあります。
買うと実験用だけで数百万円もしました。
これは半日で作ることが出来ました。
ライブラリ仕様を調べたり、製品の納品を待つ間の時間よりも、
自作でのスピードが速ければいい。
こういう種族がCプログラマです。
以上、ご参考に成れば。
とてもプロフェッショナルなご回答ありがとうございました。
ようやく読み終わりました…もちろん?4分の1も理解出来ておりません(すみません!)が、
半分でも理解できるよう頑張りたいと思います。
簡単なプログラムが書ければと気軽に思っていましたが、とても奥が深いのですね。
まだまだ勉強不足を痛感しました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
- C言語・C++・C# C言語でif文が予想と違う動きをする件について7 4 2023/03/20 00:26
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- C言語・C++・C# ポインタの型変換、どうやるんでしたっけ? 2 2022/03/28 11:00
- C言語・C++・C# C言語 3 2022/10/04 15:07
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# C言語: ポインタ 5 2022/06/01 08:33
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Run-Time Check Failure #3とい...
-
セグメントエラー
-
init関数の意味
-
デバイスハンドルとは?
-
ポインタについて
-
C言語によるメモリ読み書き
-
C言語のポインタに直接アドレス...
-
いまc言語を独学で勉強している...
-
戻り値で構造体を返すことは可...
-
ダイアログから、ドキュメント...
-
NULLとブランクの違い
-
マクロ,クラス
-
基本アルゴリズムの『返す』の...
-
ハンドル、アドレス、ポインタ...
-
ポインタ変数のインクリメント
-
ポインタの宣言
-
多重delete
-
ポインタにによる値の表現と文...
-
C#,C++/CLI,MFCにおけるデータ...
-
LPSTR型の初期化について
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
セグメントエラー
-
C言語のポインタに直接アドレス...
-
init関数の意味
-
Run-Time Check Failure #3とい...
-
戻り値で構造体を返すことは可...
-
ExcelVBAでのkernel32(64bit)
-
アプリを32bitから64bit移行
-
参照型で受け取った引数をポイ...
-
fopne で失敗する原因
-
PASCALとFARの意味
-
LPSTR型の初期化について
-
CWnd::EnableWindow()の扱い方
-
ポインタについて
-
プーさんのマウスポインタを教...
-
連結リスト 要素の入れ替え
-
ハンドルはポインタか
-
C++で関数ポインタから関数名を...
-
自作DLLの引数について、ポイン...
-
NULLポインタが0でない処理系と...
-
TCHAR文字列内の検索について
おすすめ情報