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

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

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

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

Q&A

解決済

2回答

5057閲覧

C言語 memcpyで配列コピーがうまくいかない

mjk

総合スコア303

C

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

0グッド

0クリップ

投稿2020/09/06 22:22

編集2020/09/06 23:18

#概要(実現したいこと)
const char *c[AS] = {"1", "2", "3"};の配列を関数に渡して、
memcpy(b, c, sizeof(*c));関数内で配列をコピーしたい

その後ソートなどの処理をしたいので元データの配列順序は変更したくない。

#問題点
期待通りの配列コピーが出来ないのですがどこが良くないのか分からないのでアドバイス頂けると助かります。

funcA()の出力
正:1 2 3 

funcB()の出力
誤:1 (null) (null)


#回答とアドバイスを頂き試したこと

memcpy(b, c, sizeof(*c) * AS);
配列の要素数AS(=3)を掛けたらうまくいきました。
8 x 3 = 24 byte?

うまくはいきましたがこの書き方が一般的なのでしょうか?
アドバイスなどあればコメントください。

C

1void funcB(const char *c[AS]) { 2 char *b[AS]; 3 memcpy(b, c, sizeof(*c) * AS); 4 printf("%d %d\n", sizeof(*b), sizeof(*c)); 5 printf("%d %d\n", sizeof(b), sizeof(c)); 6 printf("%s %s %s\n", b[0], b[1], b[2]); 7}

output

11 2 3 28 8 324 8 //←ここで気づきました 41 2 3

#質問時のコード

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#define AS (3) // ARRAY_SIZE 5 6void funcA() { 7 char *a[AS] = {"1", "2", "3"}; 8 printf("%s %s %s\n", a[0], a[1], a[2]); 9} 10 11void funcB(const char *c[AS]) { 12 char *b[AS]; 13 memcpy(b, c, sizeof(*c)); 14 printf("%s %s %s\n", b[0], b[1], b[2]); 15} 16 17int main(void) { 18 const char *c[AS] = {"1", "2", "3"}; 19 funcA(); 20 funcB(c); 21 getchar(); 22} 23

output

11 2 3 21 (null) (null)

開発環境

Win10 (10.0.18362)
VSC1.48.2
C11 C++17 boost
gcc version 10.2.0 (Rev1, Built by MSYS2 project)

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

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

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

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

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

guest

回答2

0

配列の要素数AS(=3)を掛けたらうまくいきました。
8 x 3 = 24 byte?

うまくはいきましたがこの書き方が一般的なのでしょうか?

提示されたプログラムを直すとして直し方の1つとしてはありだと思いますが、プログラム全体を見直してみると、もっと読みやすくなるやり方があるんじゃないかという気はします。

funcBの引数「const char c[AS]」で渡されるのはchar型変数のアドレスであり(ポインタのポインタ)、「AS」と書いたとしてもその情報は関数内には渡りません。
「AS」はコードを読むひとに誤解を与えるため書かないほうが良いです。
以下の4つの書き方はどれも同じ意味になります。

void funcB(const char *c[AS]) void funcB(const char *c[255]) /* 配列サイズに何を書いても一緒 */ void funcB(const char *c[]) void funcB(const char **c)

提示されているプログラムにおいて、使われているchar*型の配列の要素数はASで固定されているという暗黙の了解で全体が記述されています。
この前提が崩れない限りは、引数でサイズが渡ってこないのならば別のところから取得すれば良く、そういう意味では要素数「AS」を直接掛けるやり方は正解のひとつとなります。

@y_waiwai さんが指摘されている下記のやり方も、funcBの配列「b」なら配列サイズ情報にアクセス出来、「c」の配列サイズと等しくなっているためそれを使えば良いという考え方になります。

memcpy(b, c, sizeof(b));

たぶん以上で質問の答えとしては十分だと思いますが、
別のやり方を2つ示します。以降のやり方は質問の解決の仕方としてややオーバースペックかもしれません。

funcB2()
扱うデータ型を、配列要素5のchar*配列型という1つの型というふうに風にとらえてそれのポインタを渡すというやり方があります。
これならば引数のデータ型のサイズ情報がコンパイル時に確定して渡されるのでfuncBでそのサイズ情報を使用することが出来ます。

funcB3()
引数を追加して要素数を渡すようにします。
bのサイズを動的に変更されるようにしたり、その他処理を可変にするように対応する必要があります。
ちなみに今のCはmalloc()等を使用しなくても配列の可変サイズに対応できます。

#include <stdio.h> #include <stdlib.h> #include <string.h> #define AS (3) // ARRAY_SIZE void funcA() { char *a[AS] = {"1", "2", "3"}; printf("%s %s %s\n", a[0], a[1], a[2]); } void funcB(const char *c[AS]) { char *b[AS]; memset(b, 0, sizeof(b)); /* bの初期値は不定なため0クリア */ memcpy(b, c, sizeof(*c)); printf("%s %s %s\n", b[0], b[1], b[2]); } void funcB2(const char *(*c)[AS]) { char *b[AS]; memset(b, 0, sizeof(b)); /* bの初期値は不定なため0クリア */ memcpy(b, c, sizeof(*c)); printf("%s %s %s\n", b[0], b[1], b[2]); } void funcB3(const char **c, size_t sz ) { char *b[sz]; memset(b, 0, sizeof(b)); /* bの初期値は不定なため0クリア */ memcpy(b, c, sizeof(*c) * sz); for ( int i = 0 ; i < sz ; ++i ) { printf("%s ", b[i]); } printf( "\n" ); } int main(void) { const char *c[AS] = {"1", "2", "3"}; funcA(); funcB(c); funcB2(&c); funcB3(c, AS); getchar(); }

処理結果

1 2 3 1 (null) (null) 1 2 3 1 2 3

投稿2020/09/07 04:35

編集2020/09/07 05:27
hidezzz

総合スコア1248

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

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

mjk

2020/09/07 06:46

詳細な回答ありがとうございます。 *c[AS],*c[255],*c[],**cがどれも同じということには驚きました。 ASは誤解を与えるため書かない方が良いというご指摘もとても参考になります。 C++独学でポインタを使う機会が無いので理解が無いまま放置というか避けていたので今後ポインタが使われているコードを読む時の参考にもなると思います。 今の自分が理解出来る範囲で個人的な好みは汎用的でどこにでも使えそうな以下の書き方です。 void func(const char *c[], size_t sz) {}
hidezzz

2020/09/07 12:19 編集

そうですね。 今回は質問用のテストコードのようなもので、固定サイズの配列にしていたのだと思いますが、 現実のコードではデータ数は可変でなければならないことのほうが一般的であり、関数の引数にサイズを渡すやり方のほうが良く使われると思います。
mjk

2020/09/07 12:19

コメントありがとうございます。 趣味の独学ということもあり現実のコードや一般的な観点でのアドバイスはとても貴重で助かります。
guest

0

ベストアンサー

memcpy(b, c, sizeof(*c));

c というのは、char です
一つのポインタのサイズしかコピーしてません

memcpy(b, c, sizeof(c));
とすれば、ポインタの配列をコピーできます。
が、あくまで、ポインタの配列ってことに注意してください

投稿2020/09/06 22:29

編集2020/09/06 22:32
y_waiwai

総合スコア88042

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

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

mjk

2020/09/06 22:57

回答ありがとうございます。試してみましたが結果同じでした。
y_waiwai

2020/09/06 22:59

sizeof(c)と、sizeof(b) はいくつになってるか調べてみよう
mjk

2020/09/06 23:07

なるほど!そういうことでしたか。ありがとうございます。 解決した内容を質問に追記しておきます。
y_waiwai

2020/09/06 23:07

ああ、引数の配列はポインタと解釈されますね # void funcB(const char **c) { // めんどくせー ということで、やるなら、 memcpy(b, c, sizeof(b)); ですね
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問