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

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

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

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

Q&A

解決済

4回答

1941閲覧

C言語 malloc関数の

Tomori_T

総合スコア8

C

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

0グッド

1クリップ

投稿2019/12/10 14:34

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

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

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

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

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

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

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

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

C

1// 修正前のコード 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5 6int main(void) { 7 char *a; 8 char b[10] = "cd"; 9 10 a = (char*)malloc(sizeof(char)); 11 *a = "abcdefgh"; 12 char *p; 13 14 p = strstr(*a, b); 15 16 printf("%s", *p); 17 18 return 0; 19}

C

1// 修正後のコード 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5 6int main(void) { 7 char *a; 8 char b[10] = "cd"; 9 10 a = (char*)malloc(sizeof(char)); 11 // *a → a 文字列は*aに格納するのではないのか? 12 a = "abcdefgh"; 13 char *p; 14 15 // *a → a 16 p = strstr(a, b); 17 18 // *p → p 19 printf("%s", p); 20 21 return 0; 22}

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

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

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

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

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

guest

回答4

0

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

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

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

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

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

a = "abcdefgh";

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

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

投稿2019/12/11 02:41

nob.

総合スコア711

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

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

rubato6809

2019/12/11 03:45

整数変数に格納するのは整数だ、はまだ通じると思いますが、 同じ格好でも、ポインタ変数に格納するのはポインタだ・・・はどうでしょうか。じゃ「ポインタ」って何?ということにならないでしょうか。 私はズバリ、メモリアドレスという言葉を使ったら良いと思うのですが。 - ポインタ変数に格納したのは、文字列の先頭アドレスだ - 文字列リテラルを書くとそれは先頭文字のメモリアドレスとして扱われる どうでしょうか。メモリアドレスという言葉は、あまりに具体的・物理的すぎて、なじまないんでしょうか。私は逆にポインタという言葉を使われると、それは一体何を想定して使った言葉なの?と違和感を覚えます。
nob.

2019/12/13 01:52

「同じ型どうしでなければ演算はできない」ということを強調したかったからです。 勿論、暗黙の型変換やキャストがありますが、初心者には型の同一ということに注意してほしいです。 でないと、void * の存在理由も理解できないでしょう。 C言語自体には「アドレス」という概念はなかったと思います。初心者に「アドレス」の概念を持ち出すのはどうかな?と思いました。 もっとも、rubato6809さんが仰っしゃるとおり、「アドレス」を理解していれば、Cのポインタは問題なく理解できるのですが…
rubato6809

2019/12/13 04:39

C言語自体には「アドレス」という概念はなかった・・・そうなんですか。。。でも、ポインタ変数の値がメモリアドレスでないCコンパイラなど見たことがありません(と書いた所でCインタプリタだとそういう実装があるかもしれない、と気づいたけど、使ったことがない)。というか、アセンブリ言語についで、具体的なメモリを意識せざるをえないのがC言語だというのが私の実感です。 現実は、ポインタを扱える人はメモリもメモリアドレスも、的確にイメージできている人です。「メモリ上の変数の姿」を的確にイメージできない限り、いつまでたってもポインタはボウっと雲をつかむようなものなんだと思います。だとすれば、操作対象であるメモリを、できるだけ具体的に、早い段階で理解することが初心者にとって習得の早道だということになる。 ポインタ変数に格納する値はメモリアドレスである・・・これ自体は一貫性がありますし、「演算は同じ型同士で」に通じるものだと思います。
guest

0

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

アドレス変数/関数中身
0x1000a0x2000
0x2000malloc指定したサイズの領域

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

アドレス変数/関数中身
0x1000a0x2000
0x2000malloc0x3000
0x3000"abcdefgh"'a' 'b' 'c' ...

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

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

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

c

1strcpy(a, "abcdefgh");
アドレス変数/関数中身
0x1000a0x2000
0x2000malloc'a' 'b' 'c' ...

※アドレスは適当です。

投稿2019/12/11 01:01

ttyp03

総合スコア16998

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

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

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 には代入できません.

投稿2019/12/10 18:25

編集2019/12/11 01:23
jimbe

総合スコア12648

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

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

0

ベストアンサー

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

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

投稿2019/12/10 14:39

編集2019/12/10 14:42
LouiS0616

総合スコア35660

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

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

Tomori_T

2019/12/10 14:47

回答ありがとうございます. 「文字」に対して,「文字列」を代入しようとしているから,*a = "文字列"は不適格ということですか,,なるほど,, 修正後のコードの方で,ポインタaに対して文字列を代入するとうまくいくのは,無理やり,文字列をアドレスとして認識しているということしょうか?
majiponi

2019/12/10 14:53

というより、文字列の実体は、連続した文字を格納している領域(配列)のアドレスです。ポインタは、アドレスを格納するための変数です。
LouiS0616

2019/12/10 14:54

文字配列を初期化するときを除き、文字列リテラルはその先頭アドレスに読み替えられます。 実際 printf("%p\n", "abc"); とすればそのときのアドレスが確認できます。 無理矢理アドレスとして認識しているというか、元々アドレスでやり取りされるものなのです。 なお、後者のコードのmallocは必要ありません。
Tomori_T

2019/12/10 15:04

後者のコードにmallocが必要ないというのは,そのすぐ下で,アドレスを書き換えているからでしょうか? また,後者のコードでは,ポインタaが示す*aの値は不定ということで間違えないですか?
LouiS0616

2019/12/10 15:08

> 後者のコードに ... アドレスを書き換えているからでしょうか? はい。そのとおりです。 必要無いと言うか、メモリリークを起こしているので取り除くべきですね。 > ポインタaが示す*aの値は不定ということで間違えないですか? いえ、*a は a[0] と同じ、つまり 'a' です。 一般に *(a + n) は a[n] と同じです。*a は *(a + 0) なので a[0] ですね。
Tomori_T

2019/12/10 15:24

どうやら,ポインタの知識があいまいなようです,, *a = "文字列" は文字列を文字に変更する必要があったのに,a = "文字列" の場合は,文字列の一文字目だけが,aに格納されるのですか?
Tomori_T

2019/12/10 15:27

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

2019/12/10 22:16 編集

> a = "文字列" の場合は,文字列の一文字目だけが,aに格納されるのですか? a = "abcdef"; としたとき、文字列の実体は別の領域に確保され、aにはその先頭アドレスが渡されます。 aはあくまでアドレスを保持するだけです。 --- > *a = とした場合は,ある一点のアドレスの部屋にアクセスするのに対して,a = とした場合は,ある一点というわけではないので,分割して,一文字ずつ,連続した部屋に格納する そんなに複雑ではありません。 aはcharへのポインタです。そういう意味では、aが指しているのは先頭文字だけです。 ただ文字列は『連続領域に確保され』『ヌル終端している』ので、先頭しか知らなくても上手く取り扱えるのです。
LouiS0616

2019/12/10 23: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 は知りません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問