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

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

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

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

3回答

5661閲覧

mallocを使って新しい配列に文字列をコピーしたい

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2020/05/23 02:59

編集2020/05/26 13:55

すいません。たくさんのご指摘ありがとうございます。
ご指摘と反省点、また基本となる知識も踏まえたうえで最後に疑問を書かせていただきます。最初の方は自分の反省なので基本無視して大丈夫です。疑問の場所はコードの下です。もし間違いがありましたらご指摘をお願いします。

ポインタで関数の値を返すはずがvoid型となっているため、値が返されない。
つまり、参照返しができていないということが問題
string_duplicate関数でポインタ型変数char *aのメモリ内にあるデータが、
引数として関数に渡されているにも関わらず、関数内の文字列cがそのままアドレスaに代入されてしまって、10バイトの文字列データが使われない状態となっている。
その理由がwhile ( a!= '\0') 内にあるc = a;がアドレスを代入していることが原因。値を代入するにはc=*aをする必要がある。

ここで混同してしまったのが配列において、aが配列のポインタ変数となっている。
そのため*を付けるつけないの意味合いが全く異なってしまうことになる。*有りは配列のデータにある値を代入する。
またchar型の領域を確保したがfree関数を使わずに値を解放せずにしている。つまりいらなくなったaの配列のデータを消去せずにいる。
自分で気づかなかったことが、メイン関数に確保されているchara[10]のメモリと
string_duplicateのchar aのメモリの場所が全く異なった場所にある。
細かく書くと、
このプログラムだとメイン関数内にあるchar a[10]="ss"は
a[0]='s'
a[1]='s'
とメイン関数内に1バイト(8ビッド)ずつ格納されている。ここでchar型のバイト数が1バイトのため、例えばa[0]のアドレスが0019FF53とするとき、
a[1]のアドレスが0019FF54である。一バイトの差のため+1されている。また、逆のパターンのー1になることもある。
そこで、char
c =a;文字列データ "ss"はメモリに格納される。そのときに文字列データ "ss"が格納されているメモリの先頭のアドレスがポインタ変数cに格納されるようになる。つまり、char *cは初期化されるようになる
string_duplicate(c)を用いて、変数aが変数cに代入されるときに、変数aのメモリのデータが変数cの文字列データに格納されるということ。
当然のことながら、変数aのメモリと変数cのメモリが独立した場所にあるということが分かる。
このことに気づかずになんとなくで書いてしまったこともミスの一つである。

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> void string_duplicate(char *a) { int i = 0; char* c; c = (char*)malloc(sizeof(char)*10 ); while ( *a!= '\0') { c = a; c++; a++; } } void main() { char a[10] = "ss"; char* c=a; printf("%s\n", c); string_duplicate(c); printf("%s", c); }

そこで、今までの反省を含めた結果のコードです

#include <stdio.h> #include <stdlib.h> char* string_duplicate( char* a) { char* c; c = (char*)malloc(sizeof(char) * 10); while (*a != '\0') { *c = *a; c++; a++; } *c = '\0'; return c; } int main() { char a[10] = "ss"; char* c = string_duplicate(a); printf("%s\n", c); free(c); return 0; }

ここでepistemeさんとほぼ同じでchar *result =cとreturn resultを除いたこと以外はすべて同じです。関数もポインタの値を返すためにchar *型の関数にしました。
なぜ、関数内に新たなポインタ変数が必要なのでしょうか。ここではresultの事を指しています。コンパイルは通りますが例外が起こり実行できない状態となっております。新たに生成されたchar型の領域を
仮引数の値aをそのまま代入しましたが、returnでポインタ変数cの値をそのまま返すことができないのでしょうか?新たなポインタ変数が必要な理由を教えてください。

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

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

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

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

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

Zuishin

2020/05/23 03:05

c をインクリメントしてないからでしょう。したらしたで別の問題が発生しますが。
Zuishin

2020/05/23 03:16

別の問題が発生すると言ってるのに、それを考えずインクリメントしちゃうわけですか。
退会済みユーザー

退会済みユーザー

2020/05/23 03:21

インクリメントした結果、その結果をmain関数内でprintfを入力した結果無事にコピーできましたが、しない場合でもできました。その差は何ですか?
Zuishin

2020/05/23 03:25

> c = a; コピーできるがわけないです。
YT0014

2020/05/23 03:26

まず、ポインタを正確に理解して、char* aの場合の、aと*aの違いを理解してください。 なお、このコードだと、malloc()で確保した領域、使ってないです。
退会済みユーザー

退会済みユーザー

2020/05/23 03:28

わかりました。この質問は保留にします」
Zuishin

2020/05/23 03:31

ソースコードは文字列をコピーするものですが、strcpy を使えば余計なバグは入りません。 また、このコードによって何を検証したいのかがわかりません。 何を検証したいのかがわかれば、それに適した検証用コードが教えてもらえるかもしれません。
dodox86

2020/05/23 03:33

どうも質問の内容とコードがかみ合わないと言うか、質問の内容が理解できませんでしたが、char* c=a; でコピーできていると思ってしまったのでしょうか。 で、malloc()で割り当てたメモリと連携すると思ってしまったとか。前の質問でいただいた回答は、理解されましたか?
退会済みユーザー

退会済みユーザー

2020/05/23 03:33

strcpyを使用せずに、入力された文字列を,新しく用意した配列にコピーする関数を書くという内容です.
dodox86

2020/05/23 03:33

あ、Zuishinさんと同じ疑問を抱いていました。(何を確かめたいか分からないコード)
退会済みユーザー

退会済みユーザー

2020/05/23 03:36

つまり、mallocを使ってchar型の領域の配列を確保し、与えられた文字列をそのままコピーして、コピーされた配列を出力するという内容です
YT0014

2020/05/23 03:42

C言語をきちんと使いこなそうと思うなら、配列や変数、関数などが、メモリ上でどうなっているか、などの、アセンブラの基礎的な知識が必要となります。 メモリとアドレスの関係は理解されていますでしょうか? これまでの質問などを拝見した限り、そこを理解するのが先決かと思います。アドレスが理解できたら、ポインタも理解できるようになるでしょう。
archiver

2020/05/23 04:01

後、変数のスコープ(=有効範囲)がわかっていないようにも見える。 多分、main()の中の"char *c"とstring_duplicate()の中の"char *c"が同じになると思ってない? (これは他の人が散々言ってるけど。mallocで確保した領域を使ってないって) main()の中でstring_duplicate()を呼ぶ前後のポインタ変数cの内容で検証したいんだろうけど、同じもの(=コピー元)だから検証になってないよ。
guest

回答3

0

ベストアンサー

やりたかったのはこーゆーことか?

C

1#define _CRT_SECURE_NO_WARNINGS 2#include <stdio.h> 3#include <stdlib.h> 4 5char* string_duplicate(const char* a) { 6 int i; 7 char* c; 8 9 // a の長さをiに求める 10 for ( i = 0; a[i] != '\0'; ++i ) ; 11 12 // 文字列のコピーに必要な領域をcに確保する 13 c = (char*)malloc(sizeof(char) * (i+1)); // 終端文字を含んだ長さが必要 14 char* result = c; 15 16 // 文字列aをcにコピー 17 while (*a != '\0') { 18 *c = *a; 19 c++; 20 a++; 21 } 22 *c = '\0'; // '\0'で終端する 23 24 return result; 25} 26 27int main() { 28 char a[10] = "ss"; 29 char* c = string_duplicate(a); 30 printf("%s\n", c); 31 free(c); // 必ず開放すべし 32 return 0; 33}

投稿2020/05/24 01:06

episteme

総合スコア16614

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

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

episteme

2020/05/26 13:17

> なぜ、関数内に新たなポインタ変数が必要なのでしょうか。ここではresultの事を指しています。 いや、そりゃそーだろ。c に確保した領域の先頭をセットしたあと、文字列コピーのために c++ を何度かやるから、return c; では確保領域の先頭ではなくなってる。
episteme

2020/05/26 13:30

どーしても result が嫌いなら return c; 改め: return c-i; だね。
pepperleaf

2020/05/26 13:34

また、i で for ()を回せば?
退会済みユーザー

退会済みユーザー

2020/05/26 13:45

return resultだと、配列の先頭アドレスが確保した状態で返せるという事ですか?また、return cだとc++されたままで、その時の値がc=\0なのでこれ以上文字を返せない様になるからですか?
episteme

2020/05/26 13:54 編集

そーです。作ったとおりに動きますから。 cは文字列コピーの際に書き換わっちゃうから、書き換える前にresultに取っておくってだけのこと。 > その時の値がc=\0なのでこれ以上文字を返せない様になるからですか? いや値がどーこーではなく、確保領域の先頭を返さんと意味がないやろ?
guest

0

returnでポインタ変数cの値をそのまま返すことができないのでしょうか?

返されるcの中身が何であるかをよく考えてみて下さい。
最初にmallocしたアドレスを代入した時点では、cの中身は確保したエリアの先頭アドレスです。
しかし、その後、whileの中でc++を何度も行っています。
その度にcの値であるアドレスは先へ進んでいきます。
最後には*c='\0'として、cのアドレスへヌル文字を書き込んですら居ますよね?
つまり、cの中身は文字列の末尾のアドレスです。

このcをそのまま返すという事は、文字列の末尾へのポインタをmainに返すという事です。

受け取ったmainがそのアドレスを頼りに中を覗いてみれば、いきなりヌル文字ですから、mainからすれば、空文("")が返されたんだな。と思ってしまいます。

配列名が常に配列の先頭アドレスを指す配列とは違い、
ポインタ変数は中身をどんどん書き換えてしまう為、先頭アドレスが必要な場合は何処かに覚えておかねば、見失ってしまいます。

投稿2020/05/26 18:59

amiya

総合スコア1218

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

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

0

沢山の問題があります。

まず、main関数の中で配列を定義し、その先頭アドレスをポインタとしてcに保存しています。
そして、string_duplicateにそのポインタを渡していますが、
この時に渡されるのは、ポインタ値のコピーです。
関数の中で、ポインタ値をいくら書き換えても、オリジナルのcには何の影響もありません。
何も変わらないので、その下のprintfでも表示は変わりません。

string_duplicateの中身では、mallocを使って確保したメモリエリアのアドレスを、cに保存していますが、
その直後に、c = a (ここでのaはメインから渡されたcの値のポインタ値です。)として、上書きしてしまっています。
mallocで確保した領域は、誰からも参照されないゾンビになります。

その後は、c++ a++としていますが、これはcとaにあるポインタ値を進めています。
c=aとしているので、双方を++しても==のままですが、なぜかc=aを繰り返しています。
どのみち、ループが終わると、そのままcもaも廃棄されます。

投稿2020/05/23 05:39

amiya

総合スコア1218

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問