アプリ版:「スタンプのみでお礼する」機能のリリースについて

現在C++を本で独学しており、ポインタの章を終えて配列の章を
学んでいるのですがでわからないところがあります。

『配列名は配列の先頭要素のアドレスをあらわす。』と習ったのですが、
下記のコードにての

#include <iostream>
using namespace std;

int main()
{
char str[] = "Hello";
cout << str << '/\';
return 0;
}

を実行すると"Hello"が出力されるとのことですが、
どうしてchar型配列strの要素をそのまま出力することになるのでしょうか?
この場合、『配列名は配列の先頭要素のアドレスをあらわす。』に
のっとれば出力されるのは「char型配列strの先頭要素のアドレス」に
なり、アドレスが出力されなければおかしいと思うのですが・・・?

同様に

#include <iostream>
using namespace std;

int main()
{
char* char = "Hello";
cout << str << '/\';
return 0;
}

のコードでもどうして間接参照演算子*さえ使わずに
strの要素を出力できるのかがさっぱりわかりません。

ご説明頂ければ幸いです。

A 回答 (7件)

coutの<<はタイプがchar*だとnull文字まで出力するからです。


アドレスが見たければタイプをlongにキャストするのです。
char* str = "Hello";
cout << (long)(str) << endl;

char* も char[]も同じです。つまりこうしても結果は"e"です。
char* iampointer = "Hello";
cout << iampointer[1] << endl;
    • good
    • 1
この回答へのお礼

ご返答ありがとうございます。
せっかくお答え頂いたのに、私にはまだピンとこないのですが・・・

>coutの<<はタイプがchar*だとnull文字まで出力するからです。
>アドレスが見たければタイプをlongにキャストするのです。

char型の配列及びポインタの場合は
『配列名は配列の先頭要素のアドレスをあらわす。』の定義の
例外的で、longキャストして始めてアドレスが見られるということで
よろしいのでしょうか?
スミマセン・・・こんな短いコードなのに理解できない
自分が不甲斐ないです。

お礼日時:2019/04/23 06:04

米国のあるソフト会社の社長が、プログラマ採用する件で書かれた本に「C言語のポインター概念がどうしても理解できない人がいる」と書かれていました。


ですので、質問者さんは、そういう人なのかもしれませんね。
であれば「さっぱりわかりません」というのも仕方ないことです。
理解するのを諦めることも人生です。
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。

>理解するのを諦めることも人生です。

そこを何とかしがみつきたいと思って
今、学習中なのですが・・・。

お礼日時:2019/04/23 16:32

>配列名は配列の先頭要素のアドレスをあらわす



とは言ってるがそれをcoutでそのまま出力するとは言ってない

>char* char = "Hello";

これもポインターだからstrは先頭要素のアドレスが格納されてる
    • good
    • 0

cout の << は。

その後の型によって何を表示するかが変わります。
このような仕組をポリモーフィズムと言って、C++の重要な仕組の一つです。


他のポインタだと、そのアドレスを出力する、となっています。
対して、 char * だと「その示すアドレスから順番に、'\0'の前までの『文字』を出力する」となっています。

これは、以下のような理由があります。
・C++の元になったC言語では、「文字列型」というものが無く、「charの配列の先頭から'\0'の前までを文字列として扱う」というルールを使っている。
C++でも、そのルールを引き継いで、char * / char[] を文字列として扱うケースが多い。
・cout << にchar * を指定したとき、圧倒的に「文字列を出力したい」ケースが多い


アドレスを出力させたいのなら、char *でないポインタにキャストすることです。
そういう時は、汎用につかえる void * にキャストするのが常套手段です。

C言語由来の記法では (void * ) str と、(型)とします。
ですが、このキャストはなんでも有りすぎるので、C++ではC++専用のキャスト方法が用意されているので、そちらを使いましょう。
static_cast<void *>(str)
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。

>cout の << は。その後の型によって何を表示するかが変わります。
>このような仕組をポリモーフィズムと言って、C++の重要な仕組の一つです。

ポリモーフィズムの説明は使っている本の
私が今読んでいるところの後の章で
おそらくなされていることと思います。
私も以前に別のオブジェクト指向言語の書籍を読んだことがありましたが・・・
現在は「どんなものだったか?」の具体例を忘れてしまっておりまして(汗)

>他のポインタだと、そのアドレスを出力する、となっています。
>対して、 char * だと「その示すアドレスから順番に、
>'\0'の前までの『文字』を出力する」となっています。

そうだったのですね!

>C++の元になったC言語では、「文字列型」というものが無く、
>「charの配列の先頭から'\0'の前までを文字列として扱う」というルールを使っている。

はい。確かC言語ではstring型というものがなく、
char型の配列で文字列を扱っていたのだったと思います。

>アドレスを出力させたいのなら、char *でないポインタにキャストすることです。
>そういう時は、汎用につかえる void * にキャストするのが常套手段です。

先にご返答頂いていた、User20103さんもlong型にキャストすればよいと
仰られておりました。

>C言語由来の記法では (void * ) str と、(型)とします。
>ですが、このキャストはなんでも有りすぎるので、C++では
>C++専用のキャスト方法が用意されているので、そちらを使いましょう。
>static_cast<void *>(str)

「static_cast<void *>(str)」というこの構文は今日初めて目にしました。
まだこの構文の意味は私にはわかりませんが、疑問に感じていたところから
何歩か前進できた気がします。

お礼日時:2019/04/23 16:58

cout << の動作をきちんと理解するには、演算子オーバーロードの仕組みと、型継承におけるオーバーライドの仕組みの両方を理解していないと難しいと思いますよ。


std::coutはstd::ostreamクラスのオブジェクトで、<<はstd::ostream.operator<<というメンバ関数の呼び出しになります。このメンバ関数は引数の型ごとに異なる動作の関数がオーバーロードされていて、引数がchar*のときとint*のときとでは別の関数が呼ばれるのです。
ちなみにstd::ostream.operator<<は返値として自身への参照を返し、これにより次の<<を呼び出せるようになっています。
例えば
cout << str << '/\';
では、まず cout << str で str の中身が出力され、coutへの参照が返されます。このcoutへの参照に対して << '/\' が呼ばれ、'/\' が出力されます。ここでもcoutへの参照が返されますが、これは無視されます。

なお『配列名は配列の先頭要素のアドレスをあらわす。』は厳密には間違いです。多くの場合に先頭アドレスを示すのは事実ですが、違う場合もあります。
例えば
char str[] = "Hello";

char* str = "Hello";
では
sizeof(str)
の値が違います。
    • good
    • 0
この回答へのお礼

ご返答ありがとうございます。

>cout << の動作をきちんと理解するには、演算子オーバーロードの仕組みと、
>型継承におけるオーバーライドの仕組みの両方を理解していないと難しいと思いますよ。

cout << の動作に鍵があるのですね!
まだオーバーロード、オーバーライドの章まで読み進められておりません・・・。
そこまでたどり着いたら、また調べてみたいです。

>なお『配列名は配列の先頭要素のアドレスをあらわす。』は厳密には間違いです。
>多くの場合に先頭アドレスを示すのは事実ですが、違う場合もあります。
>例えば
>char str[] = "Hello";
>と
>char* str = "Hello";
>では
>sizeof(str)
>の値が違います。


そうなのですか!?
教えて頂きましたこちらは、知識として知っておきたいと思いました。

お礼日時:2019/04/23 17:11

こんな初歩的な段階で


オペレータオーバーローディングとかは
きついでしょ。

とりあえず、c++のCの部分だけを学習の対象にしては?

#include <stdio.h>

int main() {
char *greeting ="Hello";
printf("%s¥n", greeting);
return 0;
}
    • good
    • 1
この回答へのお礼

ご返答ありがとうございます。

>こんな初歩的な段階で
>オペレータオーバーローディングとかは
>きついでしょ。

>とりあえず、c++のCの部分だけを学習の対象にしては?

C言語は昔学習してみた経験がありますが、もう忘れている部分も
多いです(汗)
C++が最高難度レベルの難しい言語であるということは
承知してはいるのですが、私が今回C++の学習を始めたのは
『プログラマの考え方がおもしろいほど身につく本』という書籍を
入手しまして、いずれその本を読んでみたいと思ったからなのです。
(※この書籍の解説はC++のコードでなされています。)

ですが、
「構造化プログラミングの考え方はオブジェクト指向プログラミングを
学ぶ上で逆に障害となる」という声に賛否両論あるようですし
私も自分のレベルにあった学習段階がどこにあるのか?に
試行錯誤しております。

お礼日時:2019/04/23 22:58

>どうしてchar型配列strの要素をそのまま出力することになるのでしょうか?


C++が(ある意味)賢いからです

(賢くない) C言語 で同じ部分を書き直せば、配列とポインタの違いも納得できると思います。
それでもわからなければ、展開されたアセンブラコードを読まれると、C++が行っている「賢さ」に納得できると思います
    • good
    • 1

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