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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

5回答

8183閲覧

配列を返す関数を作りたい[VC++][VS2008]

notgoodpg

総合スコア37

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

2クリップ

投稿2018/01/18 06:56

編集2018/01/18 08:06

###前提・実現したいこと
配列を返す関数を作りたい。
戻り値に配列の長さ、引数にポインタをもつ関数で配列の値が取得できるようにしたい。
具体的には、ポインタ引数を関数内でnewし、memcpyで値を書き込んだら
関数の外でポインタを参照することで値を参照したい。
このサイトのGetReceive関数を参考にしています。

よろしくお願いいたします。
###発生している問題・エラーメッセージ

配列のポインタを指定したつもりのポインタが未定義のままになっており値を参照できない。 // ご指摘により解決しました
memcpyによりdstに対してnewした領域にsrcの持っていた値がコピーされ、srcをdeleteしたところでdstにはすべての文字がコピーされ保持されると思っていました。
しかし、関数内でsrcをdeleteしたことによってdstから参照する文字の一部が破壊されてしまいます。
srcからコピーした文字をdstからすべて参照する方法は無いでしょうか?
(最初の質問で文章にし忘れていました。申し訳ございません)
###該当のソースコード

c++

1#include <iostream> 2 3using namespace System; 4 5char* m_src; 6 7void IniSrc() 8{ 9 m_src = new char[16]; 10 char temp[16] = { 'W','e','a', 'r', 'e', 't', 'h', 'e', 'D', 'i', 'v', 'i', 's', 'i', 'o', 'n' }; 11 memcpy( m_src, temp, sizeof(temp)); 12} 13// うまく動作しない関数 14//int MemCpyTest(char* dst) ご指摘を受けて修正 15int MemCpyTest(char*& dst) 16{ 17 const int strLen = 16; 18 dst = new char[16]; 19 20 // memcpy(dst, m_src, sizeof(m_src)); ご指摘を受けて修正 21 memcpy(dst, m_src, strLen); 22 23 delete[] m_src; // いわゆる読み捨てのような事を行いたいのでここでm_srcをdeleteしてしまいたい 24 25 return strLen; 26} 27 28int main() 29{ 30 char *str = NULL;  31 32 IniSrc(); 33 int strLen = MemCpyTest(str); // newで確保した領域の先頭アドレスをstrに渡す事に成功しました 34 35 for (int i = 0; i < strLen; ++i) { // 解決 // しかしmemcpyではsrcをdeleteしたことによって値が破壊され 36 std::cout << str[i]; // 解決 // 値の一部が変わってしまいました 37 } 38 39 std::cout << std::endl; 40 41 std::cin; 42 43 delete [] str; // ご指摘を受けて追加 44 45 return 0; 46}

###補足情報(言語/FW/ツール等のバージョンなど)

  • Windows7/64bit SP1
  • VisualStudio2008
  • VC++

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

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

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

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

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

guest

回答5

0

ベストアンサー

これでいいのかな?

C++

1#include <iostream> 2 3char* m_src; 4 5void IniSrc() { 6 m_src = new char[16]; 7 char temp[16] = { 'W','e','a', 'r', 'e', 't', 'h', 'e', 'D', 'i', 'v', 'i', 's', 'i', 'o', 'n' }; 8 memcpy( m_src, temp, sizeof(temp)); 9} 10 11int MemCpyTest(char*& dst) { // 引数はポインタの'参照'な! 12 int strLen = 16; 13 dst = new char[strLen]; 14 memcpy(dst, m_src, strLen); 15 return strLen; 16} 17 18int main() { 19 char *str = NULL; 20 21 IniSrc(); 22 int strLen = MemCpyTest(str); 23 24 for (int i = 0; i < strLen; ++i) { 25 std::cout << str[i]; 26 } 27 std::cout << std::endl; 28 delete[] str; // 捨てるのは使ったあとぢゃないと。 29}

投稿2018/01/18 07:07

episteme

総合スコア16614

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

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

notgoodpg

2018/01/18 07:23 編集

ご回答ありがとうございます。 引数をポインタ引数の参照渡しに変更したらnewすることで確保した領域の先頭アドレスを関数の外でも参照できるようになるのですね。
ozwk

2018/01/18 07:38

>> 引数をポインタ引数の参照渡しに変更したらnewすることで確保した領... そんな小難しい話というよりは ただ単に質問者さんは void hoge(int a){a = 1;} に対して、引数で渡した変数が変わらない!なんで?とやっていただけです。
notgoodpg

2018/01/18 07:49

ありがとうございます。 その通りですね。
episteme

2018/01/18 08:01 編集

ま、そーゆーことで。 僕ならvector<char> 返すけどねー vector<char> str = MemCpyTest(); const char* ptr = str.data(); // 先頭位置 int len = str.size(); // 大きさ これなら使った後でdelete[]せんでえぇし。
notgoodpg

2018/01/18 08:04

ありがとうございます。 改善に役立てさせていただきます。
episteme

2018/01/18 08:16 編集

さもなくば unique_ptr<char>/shared_ptr<char> こいつも自動的に解放してくれる。
notgoodpg

2018/01/19 00:07

有益な情報をありがとうございます。 スマートクラスというもののようですね初めて知りました。 内容について調べてよりよい実装を模索してみます
episteme

2018/01/19 00:29

"スマートポインタ(賢いポインタ)"な。 でもなー...VS2008じゃなー...
guest

0

引数(char* dst)を使う前に、書き換えているところが問題です(下記のコードの最後の行で、引数 dst の値が変更されています)。

” dst = new char[16];”の行は不要なのではないでしょうか。

// うまく動作しない関数 int MemCpyTest(char* dst) { const int strLen = 16; dst = new char[16];

また、C/C++言語の文字列は、末尾に"\0"が付いている必要があります。そのため、16文字のASCII文字列を表すには17バイトのメモリが必要です。
最初から最後までサイズが16のcharの配列として扱うのなら良いのですが、”char* dst”のように文字列として扱っている箇所が散見されるので、思ったように動作しない可能性があります(最悪の場合、文字列の最後を見つけようとして、アクセスが許されないメモリ領域に達し、Segmentation Fault等のシステムエラーを引き起こします)

この観点からも、コードを見直してください。

投稿2018/01/18 07:28

coco_bauer

総合スコア6915

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

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

notgoodpg

2018/01/18 07:42

ご回答ありがとうございます。 > ” dst = new char[16];”の行は不要なのではないでしょうか。 領域を動的に確保したいのでこのように書いています。 領域を確保せずにmemcpyしたらエラーになると思うのですが、どうでしょう (単純にこの行を削除して動かしたらエラーが発生しました。) >また、C/C++言語の文字列は、末尾に"\0"が付いている必要があります。そのため、16文字のASCII文字列を表すには17バイトのメモリが必要です。 > 最初から最後までサイズが16のcharの配列として扱うのなら良いのですが ご指摘ありがとうございます。 ですが、この場では単純に16文字のchar配列と捉えていただきたいです。
guest

0

しかし、関数内でsrcをdeleteしたことによってdstから参照する文字の一部が破壊されてしまいます。

それが原因ではありません。

epistemeさんの回答memcpy(dst, m_src, strLen);
ご自身のコードmemcpy(dst, m_src, sizeof(m_src));
をよく見比べてみてください。
さらにsizeof(m_src)がいくつになるか確認してみてください。

m_srcchar*型変数なのでsizeofの結果は格納されている文字数に関係なく4や8などの固定値をとります。
つまりmemcpy(dst, m_src, sizeof(m_src));では、m_srcの前半部分しかdstにコピーできていません。
その結果、呼出元での16回のループ後半では、newで確保されたdstの未初期化の値を出力しているため、破壊されているように見えているだけです。

投稿2018/01/18 07:51

編集2018/01/18 07:53
can110

総合スコア38234

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

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

notgoodpg

2018/01/18 07:58

ご回答ありがとうございます。 せっかく頂いた回答なのに重要な箇所を見落としていたわけですね・・・ご指摘ありがとうございました。
guest

0

vector<char> を返してみた(ただしC++11)

C++

1#include <iostream> 2#include <vector> 3 4char* m_src; 5 6void IniSrc() { 7 m_src = new char[16]; 8 char temp[16] = { 'W','e','a', 'r', 'e', 't', 'h', 'e', 'D', 'i', 'v', 'i', 's', 'i', 'o', 'n' }; 9 memcpy( m_src, temp, sizeof(temp)); 10} 11 12std::vector<char> MemCpyTest() { 13 return std::vector<char>(m_src, m_src+16); 14} 15 16int main() { 17 IniSrc(); 18 std::vector<char> str = MemCpyTest(); 19 20 for ( char ch : str ) { 21 std::cout << ch; 22 } 23 std::cout << std::endl; 24} 25

投稿2018/01/18 08:21

episteme

総合スコア16614

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

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

notgoodpg

2018/01/22 00:32

ご回答ありがとうございます。 かなり簡潔で読みやすくなりますね。コードの改善の参考にさせていただきます。
guest

0

もう解決していますが、C++ならstringを使っても良いような・・・。
趣旨がよくわからなかったのですがメモリのコピーをしたいのでしょうか?
単純に移動をしたいのであればunique_ptrを使っても良いような気がします。

C++

1#include <iostream> 2#include <malloc.h> 3#include <memory> 4 5using uPtrChar = std::unique_ptr<char>; 6uPtrChar m_src=nullptr; 7 8void IniSrc() 9{ 10 char temp[17] = { 'W','e','a', 'r', 'e', 't', 'h', 'e', 'D', 'i', 'v', 'i', 's', 'i', 'o', 'n','\0' }; 11 m_src.reset(new char[sizeof(temp)]); 12 memcpy(m_src.get(), temp, sizeof(temp)); 13} 14 15int MemCpyTest(uPtrChar& dst) 16{ 17 const int strLen = 17; 18 dst.reset(new char[17]); 19 //memcpy(dst.get(), m_src.get(), strLen); 20 //m_src.release(); 21 dst = std::move(m_src); //こっちでも良い 22 return strLen; 23} 24 25int main() 26{ 27 uPtrChar str = nullptr; 28 29 IniSrc(); 30 int strLen = MemCpyTest(str); 31 32 for (int i = 0; i < strLen; ++i) { 33 std::cout << str.get()[i]; 34 } 35 36 std::cout << std::endl; 37 char temp; 38 std::cin>>temp; 39 return 0; 40}

投稿2018/01/18 08:37

m_yoko

総合スコア156

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

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

m_yoko

2018/01/18 08:42

書いたあとに気がついだんですがVS2008なんですね。。。多分2010からじゃないとこのコード使えないです。参考までに。
notgoodpg

2018/01/22 02:03 編集

ご回答ありがとうございます。 VS2010以降ならば右辺値を破棄する処理が行えるのですね。勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問