ページ 11

構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 16:53
by テーゼ
c言語を勉強している初心者なのですが
構造体変数をコピーする勉強を自分で考えて色々試していたのですが

構造体メンバに初期化して文字列らを代入してから別の構造体変数にコピー(代入)したのですが
値(数値)の方はコピー出来たのだけれど文字列の方はコピーされていませんでした

何が原因なのでしょうか?それとも文字列はコピーできないんでしょうか?

また、コンパイラは、エラーや警告は0と表示されてて問題ないようです

初心者で用語などがまだ半端者ですが宜しくお願い致します。




/* 構造体変数のコピー */

#include <stdio.h>

struct g {

int i,j;
char a[];

}fd = {64,16,"コマンド"},ed;


int main()
{


printf("%d,%d,%s,\n",fd.i,fd.j,fd.a);


ed = fd; //ここで構造体変数をコピーしました


printf("%d,%d,%s,\n",ed.i,ed.j,ed.a);


return 0;

}

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 17:20
by nil
コードタグをご利用ください。

配列をコピーすることは=演算子では不可能です。
memcpy関数を使うか
配列の要素をfor文を回して一つ一つ格納する必要があります。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 17:32
by non
格納する領域が確保されてないからでは。
構造体のメンバーを
char a[10];
に宣言してみたらどうですか?

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 17:44
by softya(ソフト屋)
char a[];の場合はサイズ0の配列として扱われていると思います。
なので構造体のサイズとしてはint i,j;の分だけと解釈されます。で、int i,jだけコピーされるのです。
なお、初期値としてはfd = {64,16,"コマンド"}の分だけ初期化されているので文字列としては読み取れますがコピーされません。
char a[12];などのサイズを固定の配列にしてみてください。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:05
by ISLe
文字列がコピーできないのではなくて、要素のない配列だからですね。

要素のない配列は、実体を持ちません。

fd.aで文字列が表示されるのは初期化に使った{64,16,"コマンド"}の文字列の位置をfd.aが指していると思われます。
#こういう初期化ができると知りませんでしたけど。

ed.aには実体がないので不正なアクセスをしていると思われます。
#Cygwinのgcc 4.5.3で試してみたら文字列もコピーされるように振る舞うけどなんだろう。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:15
by softya(ソフト屋)
ISLe さんが書きました: ed.aには実体がないので不正なアクセスをしていると思われます。
#Cygwinのgcc 4.5.3で試してみたら文字列もコピーされるように振る舞うけどなんだろう。
確かにgccだとコピーしたように振舞いますね。
でもsizeof()は8バイトとという矛盾。

【追記】アセンブラレベルでも確認。(注)私の都合でedからfd2に名前変えてます。

movl _fd, %eax
movl %eax, _fd2
movl _fd+4, %eax
movl %eax, _fd2+4
movl _fd+8, %eax
movl %eax, _fd2+8
movl _fd+12, %eax
movl %eax, _fd2+12
movzbl _fd+16, %eax
movb %al, _fd2+16
きっちりコピーしている。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:18
by ホヅミ
こんなのはどうでしょうか?

コード:

// こうするか
struct g {
    int i,j;
    char *a;
}
// こうする
struct g {
    int i,j;
    char a[256];
}

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:30
by テーゼ
有難うございました皆様
char a[];の部分をchar a[10];と変えたところ

edにちゃんとコピー出来るようになりました



ですが教科書で配列を勉強した時の話になるのですが

配列を初期化するときは、a[] = {10,20,30};といったように[]の部分を書かずに省略できて
コンパイラ自体が[]の部分を[3]と自動的(固定)にしてくれると書いていた、と思うのですが・・



構造体の場合は[]の部分を省略したら自動的には固定([3]のように)はされないのでしょうか?
それとも、文字列の場合は先程教えて頂いたように[]の部分に10や12らを明示的に書き示す必要があるのでしょうか?。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:44
by softya(ソフト屋)
構造体の最後のメンバ変数でサイズ不定だった場合には、サイズ0と見なされると思います。
なので構造体のコピー対象にはなりません。
int a[] = {10,20,30};とかの初期化とはコンパイルの解釈が違うので、たぶんコンパイラの規格として未定義の部分(コンパイラを作っている会社や人たち次第)じゃないかなと予想。

[少し修正]
なぜダメなのかというとfd,edで初期化次第でサイズが変わる構造体が許されると矛盾を抱かえる事になるのがマズイと思います。つまり、サイズが大きいfdから小さいedにコピーを許してしまうとedの外側のメモリが破壊されてしまうのです。これはマズイですよね。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:50
by テーゼ
ホヅミ様も有難うございました

*aでも

無事文字列をコピーできました

色々な書き方有難うございました。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:55
by ISLe
テーゼ さんが書きました:構造体の場合は[]の部分を省略したら自動的には固定([3]のように)はされないのでしょうか?
おっしゃるとおり、構造体のメンバの場合は、要素数を省略すると要素のない配列となります。
とうぜんですが要素数を省略する配列は最後のメンバでなければいけません。

コード:

struct g {
    int i,j;
    char a[];
};
struct g fd = {64,16, "コマンド"};
struct g ed = {32, 8, "命令" };
初期化値でサイズが決まるならばこのようにするとfdとedは同じ構造体ではなくなってしまいます。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:55
by ISLe
softya(ソフト屋) さんが書きました:【追記】アセンブラレベルでも確認。

movl _fd, %eax
movl %eax, _fd2
movl _fd+4, %eax
movl %eax, _fd2+4
movl _fd+8, %eax
movl %eax, _fd2+8
movl _fd+12, %eax
movl %eax, _fd2+12
movzbl _fd+16, %eax
movb %al, _fd2+16
きっちりコピーしている。
プログラマから見えないところで初期化値のサイズを覚えているみたいですね。
これは致命的なバグを誘発しますね。余計なお世話としか。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 18:56
by ISLe
テーゼ さんが書きました:ホヅミ様も有難うございました

*aでも

無事文字列をコピーできました
これは文字列をコピーしているのではないということを理解してください。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 19:01
by softya(ソフト屋)
ちなみにホヅミさんの

コード:

struct g {
    int i,j;
    char *a;
}
は文字列ポインタをコピーするだけで文字列実体は複製されていないので文字列の内容を書き換えるとひどい目に会います。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 19:45
by YuO
softya(ソフト屋) さんが書きました:確かにgccだとコピーしたように振舞いますね。
でもsizeof()は8バイトとという矛盾。
Flexible Array Memberがある場合,sizeof演算子はその(末尾の)メンバーを除いた構造体と同一のサイズを返します。
# ISO/IEC 9899:1999 6.7.2.1 Structure and union specifiers ¶16

FAM付きの構造体の代入についての標準の記述は見つけられていません。
そもそも,FAM付き構造体を動的割り付けしないで使えるのか,という根本的な疑問もあったりしますが。
# FAMはC99からの新要素。C++には取り入れられていません。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 20:16
by テーゼ
皆様続けざまに質問のご回答有難うございました

初心者なもので皆様の言われている事を一つ一つ試していたら
ご返事が遅れてしまいました申し訳ない;


やはり配列の時とは違って構造体の場合は文字列を代入したいのであれば明示的書き示す必要があるのですね、char a[12];といったように、
どうして書き示す必要があるのかも分かったような気が致しました

ホヅミ様の書き方も文字列をコピーしたのではなく文字列ポインターをコピーしたのですね分かりました

コンパイラなどによっても違いがあるのかもしれませんがホヅミ様のやり方で途中文字列(4文字だったのを3文字にしたところ)の内容を書き換えてコンパイルしたところとくに何もなく成功いたしました


問題解決致しました皆様どうも有難う御座いました。


※尚、このサイトで初めてスレッドを立てさせて頂いたので一応一時間経過してから解決を押させて頂きます

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 20:25
by softya(ソフト屋)
ホヅミさんのポインタ型の場合の問題を起こす場合の参考プログラムを書いておきます。

コード:

/* 構造体変数のコピー */

#include <stdio.h>

struct g {
	int i, j;
	char *a;
} fd = {64, 16, "abcedf"}, ed;


int main() {
	printf( "fd = {%d,%d,%s}\n", fd.i, fd.j, fd.a );
	ed = fd; //ここで構造体変数をコピーしました
	printf( "ed = {%d,%d,%s}\n", ed.i, ed.j, ed.a );
	ed.a[1] = '1'; //ここで文字列を書き換えました。
	printf( "fd = {%d,%d,%s}\n", fd.i, fd.j, fd.a );
	printf( "ed = {%d,%d,%s}\n", ed.i, ed.j, ed.a );
	return 0;
}
実行結果を確かめてみてください。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 21:51
by テーゼ
softya(ソフト屋) 様最後の最後までアドバイス有難う御座いました


softya(ソフト屋) 様の書かれたソースコードではコンパイル(実行)したところ確かに

書き換えたにもかかわらず何も変わっておらず実行(出力)されておりました

こういった場合もあるのですね勉強になりました

皆様長い時間お付き合い頂いて本当に有難う御座いました
もっと勉強して理解を深めていきたいとます

また宜しくお願い致します。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月04日(木) 21:57
by softya(ソフト屋)
テーゼ さんが書きました: 書き換えたにもかかわらず何も変わっておらず実行(出力)されておりました
書き換わるはずですよ。よく見てください。
重要ポイントはedを書き換えているにも関わらずfdも書き換わっている点です。
つまり意図しない変更の伝搬が起こっています。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月07日(日) 16:23
by テーゼ
softya(ソフト屋) さんが書きました:
テーゼ さんが書きました: 書き換えたにもかかわらず何も変わっておらず実行(出力)されておりました
書き換わるはずですよ。よく見てください。
重要ポイントはedを書き換えているにも関わらずfdも書き換わっている点です。
つまり意図しない変更の伝搬が起こっています。



一応 softya(ソフト屋) 様が作って頂いたサンプルプログラムそのままコピーして貼り付けてから実行(コンパイル)したのですが

動作を停止しましたと表示された後、コマンドプロンプトに softya(ソフト屋)様が書いて頂いたプログラムの実行結果が表示されるのですが

以下のような実行結果でした


fd = {64,16,abcedf}
ed = {64,16,abcedf}



コンパイラによって実行結果などは違うのでしょうか?
それともコンパイラの設定がおかしいのでしょうか。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月07日(日) 16:28
by softya(ソフト屋)
もう2行表示されませんか?
printfが4行分あるので表示されるはずです。

Re: 構造体変数をコピーしたのですが・・

Posted: 2013年4月07日(日) 18:46
by ISLe
文字列リテラルを書き換えようとして異常終了するならLinuxで動かしているとか?

でもgcc使ってるとしたら、最初の質問とわたしの実験結果が食い違いますね。