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

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

ただいまの
回答率

88.93%

C言語 malloc関数の

解決済

回答 4

投稿

  • 評価
  • クリップ 1
  • VIEW 581

Tomori_T

score 8

大学の講義でmalloc関数を学習したのですが,いまいち使い方がわからないため,どこを理解できていないのかを確認したいです.

まず,以下の「修正前のコード」では,char型のポインタaと,文字列b="cd"を用意しています.

その下で,malloc関数等を利用して,char型のアドレスをポインタaに代入しています.

aはポインタなので,*a=" ~ "として,ポインタaの示す変数*aに文字列を代入しています.

その後,*aの値の中にbの値が入っているかを調べて,その値のアドレスをポインタpに格納しています.

最後に,ポインタpが示す文字列を出力しています.

という流れでテストコードを作成したのですが,エラーがでてしまいます.

一応,下の修正後のコードでコンパイルした場合は,うまく動きました.
しかし,なぜaはポインタであるはずなのに,文字列を格納できたのか,という点が疑問です.
おそらく,何か根本的に勘違いをしていると思うので,正しい理解へ導いてほしいです.

// 修正前のコード
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    char *a;
    char b[10] = "cd";

    a = (char*)malloc(sizeof(char));
    *a = "abcdefgh";
    char *p;

    p = strstr(*a, b);

    printf("%s", *p);

    return 0;
}
// 修正後のコード
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    char *a;
    char b[10] = "cd";

    a = (char*)malloc(sizeof(char));
    // *a → a   文字列は*aに格納するのではないのか?
    a = "abcdefgh";
    char *p;

    // *a → a
    p = strstr(a, b);

    // *p → p
    printf("%s", p);

    return 0;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

0

あくまでaが指している先は『文字』です。
文字が連続領域に置かれている為、またヌル終端されている為に文字列として扱うことができます。

実際 *a = 'a'; ならエラーは吐かない筈です。これは a[0] = 'a'; と同じです。
(ただしヌル終端していないので、文字列としては不適格)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/12/11 00:27

    *a = とした場合は,ある一点のアドレスの部屋にアクセスするのに対して,a = とした場合は,ある一点というわけではないので,分割して,一文字ずつ,連続した部屋に格納する,ということであたっていますか?

    キャンセル

  • 2019/12/11 07:16 編集

    > a = "文字列" の場合は,文字列の一文字目だけが,aに格納されるのですか?

    a = "abcdef"; としたとき、文字列の実体は別の領域に確保され、aにはその先頭アドレスが渡されます。
    aはあくまでアドレスを保持するだけです。

    ---
    > *a = とした場合は,ある一点のアドレスの部屋にアクセスするのに対して,a = とした場合は,ある一点というわけではないので,分割して,一文字ずつ,連続した部屋に格納する

    そんなに複雑ではありません。
    aはcharへのポインタです。そういう意味では、aが指しているのは先頭文字だけです。

    ただ文字列は『連続領域に確保され』『ヌル終端している』ので、先頭しか知らなくても上手く取り扱えるのです。

    キャンセル

  • 2019/12/11 08:11

    malloc(...) とすると、書き込み可能な連続領域が確保されます。仮にこの領域がX番地からスタートしたとすると、a = malloc(...) としたとき a には X が代入されることになります。
    a は領域の先頭アドレスを知っているだけです。そのアドレスが連続していくつ確保されているのか、そして書き込み可能(編集可能)な領域であるのか、a は知りません。

    "abcdef" とすると、主に読み込み専用の連続領域が確保されます。仮にこの領域がY番地からスタートしたとすると、a = "abcdef" としたとき a には Y が代入されることになります。
    a は領域の先頭アドレスを知っているだけです。そのアドレスが連続していくつ確保されているのか、そして書き込み可能(編集可能)な領域であるのか、a は知りません。

    char ch = 'z' とすると、1バイト分の領域が確保されます。仮にこの領域がZ番地にあるとすると、a = &ch としたとき a には Z が代入されることになります。
    a は領域のアドレスを知っているだけです。そのアドレスが連続していくつか確保されているのか、そして書き込み可能(編集可能)な領域であるのか、a は知りません。

    キャンセル

0

前に '*' があるかどうかでポインタ変数の扱いは全く異なることをまず理解すべきと思います.
a とした場合はアドレスを扱います.
*a とした場合はそのアドレスが示す値(変数)を扱います.

文字列(例:"abc")は先頭の文字のアドレスで扱われます. ですので a には代入できますが *a にはできません.
文字(例:'a')は値です. ですので *a には代入できますが a にはできません.
配列の配列名(例:b)はアドレスで扱われます. ですので a には代入できますが *a にはできません.
配列の要素(例:b[1])は変数です. ですので *a には代入できますが a にはできません.
(ですが, 変数はアドレスを得ることが出来ます. それは &b[1] と '&' を付けた場合で, そうすると a には代入できますが, *a には代入できません. そしてこの方法は文字にはできません.)

配列に対する初期値の設定(例:char b[10]="cd")は特殊で, 上記のような理解には当てはまりません.

malloc や strstr はアドレスを返します, ですので a には代入できますが *a には代入できません.

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

修正前も修正後も間違ってますね。
mallocというのは領域を確保してそのアドレスを返す関数です。
つまりaにはアドレスが入っているのみで、aの変数のメモリに領域があるわけではありません。
少々分かりづらいかもしれませんが、以下に図解していきます。

アドレス 変数/関数 中身
0x1000 a 0x2000
0x2000 malloc 指定したサイズの領域

修正前で*a = "abcdefgh";とした場合、aが指している領域に、どこかに確保された"abcdefgh"の先頭アドレスを入れることになります。

アドレス 変数/関数 中身
0x1000 a 0x2000
0x2000 malloc 0x3000
0x3000 "abcdefgh" 'a' 'b' 'c' ...

修正後でa = "abcdefgh";とした場合、aが持っているmallocした領域のアドレスを変更することになります。
つまりmallocした領域のアドレスが失われることになるので、freeで領域解放をすることができなくなります。

アドレス 変数/関数 中身
0x1000 a 0x3000
0x2000 malloc 指定したサイズの領域
0x3000 "abcdefgh" 'a' 'b' 'c' ...

正しくmallocした領域に文字列を格納したいのであればstrcpy等を使う必要があります。

strcpy(a, "abcdefgh");
アドレス 変数/関数 中身
0x1000 a 0x2000
0x2000 malloc 'a' 'b' 'c' ...

※アドレスは適当です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

なぜaはポインタであるはずなのに,文字列を格納できたのか

文字列を格納したのではありません。文字列へのポインタを格納したのです。

ポインタの型、文字、文字配列、文字列
以上について、きちんと知識を整理しましょう。

修正前でエラーとなったのは
*a(型は char )に 文字列(型はchar *)を代入しようとしたからです。

修正後では
a(型は char *)に文字列(型はchar *)を代入なのでエラーになりません。
修正後の

a = "abcdefgh";

はポインタに文字列を入れているのではありません。

「文字列リテラルを書くとそれは先頭の文字を示すポインタと扱われる」
と言うような意味が教科書、参考書に書いてありませんか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/12/11 12:45

    整数変数に格納するのは整数だ、はまだ通じると思いますが、
    同じ格好でも、ポインタ変数に格納するのはポインタだ・・・はどうでしょうか。じゃ「ポインタ」って何?ということにならないでしょうか。
    私はズバリ、メモリアドレスという言葉を使ったら良いと思うのですが。
    - ポインタ変数に格納したのは、文字列の先頭アドレスだ
    - 文字列リテラルを書くとそれは先頭文字のメモリアドレスとして扱われる

    どうでしょうか。メモリアドレスという言葉は、あまりに具体的・物理的すぎて、なじまないんでしょうか。私は逆にポインタという言葉を使われると、それは一体何を想定して使った言葉なの?と違和感を覚えます。

    キャンセル

  • 2019/12/13 10:52

    「同じ型どうしでなければ演算はできない」ということを強調したかったからです。
    勿論、暗黙の型変換やキャストがありますが、初心者には型の同一ということに注意してほしいです。
    でないと、void * の存在理由も理解できないでしょう。

    C言語自体には「アドレス」という概念はなかったと思います。初心者に「アドレス」の概念を持ち出すのはどうかな?と思いました。
    もっとも、rubato6809さんが仰っしゃるとおり、「アドレス」を理解していれば、Cのポインタは問題なく理解できるのですが…

    キャンセル

  • 2019/12/13 13:39

    C言語自体には「アドレス」という概念はなかった・・・そうなんですか。。。でも、ポインタ変数の値がメモリアドレスでないCコンパイラなど見たことがありません(と書いた所でCインタプリタだとそういう実装があるかもしれない、と気づいたけど、使ったことがない)。というか、アセンブリ言語についで、具体的なメモリを意識せざるをえないのがC言語だというのが私の実感です。

    現実は、ポインタを扱える人はメモリもメモリアドレスも、的確にイメージできている人です。「メモリ上の変数の姿」を的確にイメージできない限り、いつまでたってもポインタはボウっと雲をつかむようなものなんだと思います。だとすれば、操作対象であるメモリを、できるだけ具体的に、早い段階で理解することが初心者にとって習得の早道だということになる。

    ポインタ変数に格納する値はメモリアドレスである・・・これ自体は一貫性がありますし、「演算は同じ型同士で」に通じるものだと思います。

    キャンセル

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

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る