5

私はメンバーとしていくつかのポインターを持つ構造を持っており、memcpy を実行しようとしています。memcpy は浅いコピー (ポインターをコピーすることを意味します) ではなく深いコピー (どのポインターをコピーすることを意味する) を行うため、この場合は memcpy を使用しないように提案されました。指し示す)。

しかし、次のプログラムで違いが生じない理由がわかりません: コードと出力を見てください。この場合、浅いコピーではない理由を説明してください。

#include <stdio.h>
#include <malloc.h>
#include <string.h>

struct student {
    char *username;
    char *id;
    int roll;
};

void print_struct(struct student *);
void print_struct_addr(struct student *);
void changeme(struct student *);

int main (void) {
    struct student *student1;
    char *name = "ram";
    char *id = "200ABCD";
    int roll = 34;

    student1 = (struct student *)malloc(sizeof(struct student));
    student1->username = name; 
    student1->id = id;
    student1->roll = roll; 
    print_struct_addr(student1);
    print_struct(student1);
    changeme(student1);
    print_struct(student1);
    print_struct_addr(student1);
    return 0;
}

void print_struct(struct student *s) {
    printf("Name: %s\n", s->username);
    printf("Id: %s\n", s->id); 
    printf("R.No: %d\n", s->roll);
    return; 
}

void print_struct_addr(struct student *s) {
    printf("Addr(Name): %x\n", &s->username);
    printf("Addr(Id): %x\n", &s->id);
    printf("Addr(R.No): %x\n", &s->roll);
    return;
}

void changeme(struct student *s) {
    struct student *student2;
    student2->username = "someone";
    student2->id = "200EFGH";
    student2->roll = 35;
    print_struct_addr(student2);
    memcpy(s, student2, sizeof(struct student));
    student2->username = "somebodyelse";
    return;
}

出力:

Addr(Name): 9b72008
Addr(Id): 9b7200c
Addr(R.No): 9b72010
Name: ram
Id: 200ABCD
R.No: 34
Addr(Name): fa163c
Addr(Id): fa1640
Addr(R.No): fa1644
Name: someone
Id: 200EFGH
R.No: 35
Addr(Name): 9b72008
Addr(Id): 9b7200c
Addr(R.No): 9b72010

memcpy が浅いコピーを行う場合、student1->username が「somebodyelse」ではないのはなぜですか。

どのシナリオでこのコードが問題を引き起こす可能性があるかを説明してください。main() で changeme() を呼び出した後、student1 に Student2 の情報が必要で、後でこの変更された Student1 のデータを使用できるはずです。

ここでは memcpy() を使用しないように提案されましたが、うまく機能しているようです。

ありがとう

これは変更されたコードです: しかし、まだここに浅いコピーの概念が表示されません:

#include <stdio.h>
#include <malloc.h>
#include <string.h>

struct student {
    char *username;
    char *id;
    int roll;
};

void print_struct(struct student *);
void print_struct_addr(struct student *);
void changeme(struct student *);

int main (void) {
    struct student *student1;
    char *name = "ram";
    char *id = "200ABCD";
    int roll = 34;

    student1 = malloc(sizeof(*student1));
    student1->username = name; 
    student1->id = id;
    student1->roll = roll; 
    print_struct_addr(student1);
    print_struct(student1);
    changeme(student1);
    print_struct(student1);
    print_struct_addr(student1);
    return 0;
}

void print_struct(struct student *s) {
    printf("Name: %s\n", s->username);
    printf("Id: %s\n", s->id); 
    printf("R.No: %d\n", s->roll);
    return; 
}

void print_struct_addr(struct student *s) {
    printf("Addr(Name): %x\n", &s->username);
    printf("Addr(Id): %x\n", &s->id);
    printf("Addr(R.No): %x\n", &s->roll);
    return;
}

void changeme(struct student *s) {
    struct student *student2;
    student2 = malloc(sizeof(*s));
    student2->username = strdup("someone");
    student2->id = strdup("200EFGH");
    student2->roll = 35;
    print_struct_addr(student2);
    memcpy(s, student2, sizeof(struct student));
    student2->username = strdup("somebodyelse");
    free(student2);
    return;
}
4

3 に答える 3

11

これ:

struct student *student2;
student2->username = "someone";
student2->id = "200EFGH";
student2->roll = 35;

割り当てられていないメモリに書き込みを行っており、未定義の動作を引き起こしています。student2書き込む前に、有効な場所を指していることを確認する必要があります。

とにかくコピーするだけなので、それを割り当てるか、スタック上のインスタンスを使用してください。

もちろん、初期化student2してから上書きsするというこのビジネス全体は不必要に複雑ですs。直接変更するだけです。

また、これ:

student1 = (struct student *)malloc(sizeof(struct student));

C では、次のように記述した方が適切です。

student1 = malloc(sizeof *student1);

これにより、無意味な (そして潜在的に危険な) キャストが削除され、サイズが型に適したものになるようになり、プログラマーによってチェックされた依存関係がコンパイラーによって処理される依存関係に置き換えられます。

第 3 に、構造体を代入できることに気付かないのは、初心者の C プログラマーの典型的な「症状」のようなものです。だから、代わりに

memcpy(s, student2, sizeof *s);

あなたはただ書くことができます:

*s = *student2;

そして、コンパイラを正しいものにします。memcpy()これは、割り当てが認識できてコピーできないが無視できない多くのパディングを構造体に含めることができるため、パフォーマンスが向上する可能性があります。

于 2012-10-30T12:07:26.047 に答える
2

それがまったく機能するのはまぐれです。あなたのchangeme()関数では、 の新しいポインタを作成してstudent2いますが、メモリを割り当てていません。

次に、同じ関数を にコピーしたstudent2 後に変更しsます。浅いコピーは、コピー内のポインターが共有されることを意味するのではなく、ポインター自体のもコピーされることを意味します。したがって、student2->usernameの後に変更memcpyしても、 の値は変更されませんs->username

作業が進むにつれて、これらの構造内でのメモリの割り当てにも注意する必要があります。AFAICR、定数リテラル文字列を使用すると、ポインターはプログラムのメモリ空間内の静的に初期化されたデータのチャンクを指します。ただし、より厳密な設計ではmalloc()free()これらの要素の動的メモリが必要になります。静的に初期化された値が必要な場合は、strdup()または同様の方法で文字列を静的空間からヒープ メモリにコピーします。

于 2012-10-30T12:07:02.480 に答える
0

コピーした、ユーザー名を「somebodyelse」に設定します。そして、それは関数「changeme()」内のローカルコピーのみを変更します。"changeme()"内でStudent2 を出力してみてください。私の言いたいことがわかるでしょう。

于 2012-10-30T12:06:59.893 に答える