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

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
テーゼ

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

#1

投稿記事 by テーゼ » 7年前

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;

}

nil
記事: 428
登録日時: 9年前

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

#2

投稿記事 by nil » 7年前

コードタグをご利用ください。

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

non
記事: 1097
登録日時: 10年前

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

#3

投稿記事 by non » 7年前

格納する領域が確保されてないからでは。
構造体のメンバーを
char a[10];
に宣言してみたらどうですか?
non

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

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

#4

投稿記事 by softya(ソフト屋) » 7年前

char a[];の場合はサイズ0の配列として扱われていると思います。
なので構造体のサイズとしてはint i,j;の分だけと解釈されます。で、int i,jだけコピーされるのです。
なお、初期値としてはfd = {64,16,"コマンド"}の分だけ初期化されているので文字列としては読み取れますがコピーされません。
char a[12];などのサイズを固定の配列にしてみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2648
登録日時: 10年前
連絡を取る:

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

#5

投稿記事 by ISLe » 7年前

文字列がコピーできないのではなくて、要素のない配列だからですね。

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

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

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

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

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

#6

投稿記事 by softya(ソフト屋) » 7年前

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
きっちりコピーしている。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ホヅミ
記事: 110
登録日時: 10年前

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

#7

投稿記事 by ホヅミ » 7年前

こんなのはどうでしょうか?

コード:

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

テーゼ

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

#8

投稿記事 by テーゼ » 7年前

有難うございました皆様
char a[];の部分をchar a[10];と変えたところ

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



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

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



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

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

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

#9

投稿記事 by softya(ソフト屋) » 7年前

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

[少し修正]
なぜダメなのかというとfd,edで初期化次第でサイズが変わる構造体が許されると矛盾を抱かえる事になるのがマズイと思います。つまり、サイズが大きいfdから小さいedにコピーを許してしまうとedの外側のメモリが破壊されてしまうのです。これはマズイですよね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

テーゼ

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

#10

投稿記事 by テーゼ » 7年前

ホヅミ様も有難うございました

*aでも

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

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

ISLe
記事: 2648
登録日時: 10年前
連絡を取る:

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

#11

投稿記事 by ISLe » 7年前

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

コード:

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

ISLe
記事: 2648
登録日時: 10年前
連絡を取る:

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

#12

投稿記事 by ISLe » 7年前

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
きっちりコピーしている。
プログラマから見えないところで初期化値のサイズを覚えているみたいですね。
これは致命的なバグを誘発しますね。余計なお世話としか。

ISLe
記事: 2648
登録日時: 10年前
連絡を取る:

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

#13

投稿記事 by ISLe » 7年前

テーゼ さんが書きました:ホヅミ様も有難うございました

*aでも

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

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

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

#14

投稿記事 by softya(ソフト屋) » 7年前

ちなみにホヅミさんの

コード:

struct g {
    int i,j;
    char *a;
}
は文字列ポインタをコピーするだけで文字列実体は複製されていないので文字列の内容を書き換えるとひどい目に会います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

YuO
記事: 943
登録日時: 10年前
住所: 東京都世田谷区

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

#15

投稿記事 by YuO » 7年前

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: 構造体変数をコピーしたのですが・・

#16

投稿記事 by テーゼ » 7年前

皆様続けざまに質問のご回答有難うございました

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


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

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

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


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


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

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

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

#17

投稿記事 by softya(ソフト屋) » 7年前

ホヅミさんのポインタ型の場合の問題を起こす場合の参考プログラムを書いておきます。

コード:

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

#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;
}
実行結果を確かめてみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

テーゼ

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

#18

投稿記事 by テーゼ » 7年前

softya(ソフト屋) 様最後の最後までアドバイス有難う御座いました


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

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

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

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

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

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

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

#19

投稿記事 by softya(ソフト屋) » 7年前

テーゼ さんが書きました: 書き換えたにもかかわらず何も変わっておらず実行(出力)されておりました
書き換わるはずですよ。よく見てください。
重要ポイントはedを書き換えているにも関わらずfdも書き換わっている点です。
つまり意図しない変更の伝搬が起こっています。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

テーゼ

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

#20

投稿記事 by テーゼ » 7年前

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



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

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

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


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



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

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 10年前
住所: 東海地方
連絡を取る:

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

#21

投稿記事 by softya(ソフト屋) » 7年前

もう2行表示されませんか?
printfが4行分あるので表示されるはずです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2648
登録日時: 10年前
連絡を取る:

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

#22

投稿記事 by ISLe » 7年前

文字列リテラルを書き換えようとして異常終了するならLinuxで動かしているとか?

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

閉鎖

“C言語何でも質問掲示板” へ戻る