C'de bir yapı diğerine atayın


149

Bir yapının bir örneğini diğerine atayabilir misiniz, örneğin:

struct Test t1;
struct Test t2;
t2 = t1;

Basit yapılar için işe yaradığını gördüm, ancak karmaşık yapılar için çalışıyor mu?
Derleyici, veri öğelerini türlerine bağlı olarak nasıl kopyalayacağını, yani bir intdizeyi ve dizeyi ayırt etmeyi nasıl biliyor ?

Yanıtlar:


155

Yapı aynı türdeyse evet. Bir hafıza kopyası olarak düşünün.


72
Belleğe işaret eden derin bir kopya olmadığını unutmayın.
Georg Schölly

3
Eşzamanlılık da burada bir sorundur.
Tim Post

17
@Tim Concurrency, tamsayılar ve çiftler gibi yerleşik türlerin atanması için olduğundan daha fazla bir sorun değildir - atama bunlar için de atomik bir işlem değildir.

2
Tamam, bir kopya oluşturulmuşsa, belleği daha sonra free () ile boşaltabilir miyim?
Betlista

5
@Betlista Belleği free () ile serbest bırakamazsınız çünkü bunlar otomatik değişkenler: en.wikipedia.org/wiki/Automatic_variable
joshdoe

142

Evet, yapılar için atama desteklenir. Ancak sorunlar var:

struct S {
   char * p;
};

struct S s1, s2;
s1.p = malloc(100);
s2 = s1;

Şimdi her iki yapının işaretçileri aynı bellek bloğuna işaret ediyor - derleyici gösterilen veriyi kopyalamıyor. Artık hangi yapı örneğinin verilere sahip olduğunu bilmek zor. Bu nedenle C ++, kullanıcı tanımlı atama operatörleri konseptini icat etti - bu durumu ele almak için özel kod yazabilirsiniz.


1
İyileştirdim çünkü okumak kendi cevabımdaki hatayı / ihmali anlamamı sağladı.
Clifford

1
+1, gerçekte herhangi bir kopyalama olmadığını belirtmek için.
Tom Duckering

16
Bu neden spam olarak işaretlendi? Birisi faresinin kontrolünü kaybetti mi?
Georg Fritzsche

@gf Ve görünüşe göre saldırgan da!

2
@rahmanisback Bu konu hakkında anon'un cevabı oldukça açık: "derleyici gösterilen veriyi kopyalamaz ". Kendisinin verileri structaçıkça kopyalanmıştır.
Tobias

28

İlk olarak bu örneğe bakın:

Basit bir C programı için C kodu aşağıda verilmiştir.

struct Foo {
    char a;
    int b;
    double c;
    } foo1,foo2;

void foo_assign(void)
{
    foo1 = foo2;
}
int main(/*char *argv[],int argc*/)
{
    foo_assign();
return 0;
}

Foo_assign () için Eşdeğer ASM Kodu:

00401050 <_foo_assign>:
  401050:   55                      push   %ebp
  401051:   89 e5                   mov    %esp,%ebp
  401053:   a1 20 20 40 00          mov    0x402020,%eax
  401058:   a3 30 20 40 00          mov    %eax,0x402030
  40105d:   a1 24 20 40 00          mov    0x402024,%eax
  401062:   a3 34 20 40 00          mov    %eax,0x402034
  401067:   a1 28 20 40 00          mov    0x402028,%eax
  40106c:   a3 38 20 40 00          mov    %eax,0x402038
  401071:   a1 2c 20 40 00          mov    0x40202c,%eax
  401076:   a3 3c 20 40 00          mov    %eax,0x40203c
  40107b:   5d                      pop    %ebp
  40107c:   c3                      ret    

Montajda bir atamanın basitçe bir "mov" komutuyla değiştirildiğini görebileceğiniz gibi, atama operatörü basitçe verileri bir bellek konumundan başka bir bellek konumuna taşımak anlamına gelir. Atama, bunu yalnızca bir yapının acil üyeleri için yapacak ve bir yapıda Karmaşık veri türlerine sahip olduğunuzda kopyalayamayacaktır. Burada KARMAŞIK, listelere işaret eden işaretçiler dizisine sahip olamayacağınız anlamına gelir.

Bir yapı içindeki bir karakter dizisi kendi başına çoğu derleyicide çalışmayacaktır, çünkü atama, veri türünün karmaşık tipte olmasına bile bakmadan kopyalamaya çalışacaktır.


2
Benim için her zaman işe yarıyor gibi görünmesine neden olan hangi koşullarda başarısız olacağını
açıklayabilir

16

Bu, aynen yapacağınız gibi basit bir kopyadır memcpy()(aslında, bazı derleyiciler memcpy()bu kod için bir çağrı üretirler ). C'de "dizi" yoktur, sadece bir grup karaktere işaret eder. Kaynak yapınız böyle bir işaretçi içeriyorsa, karakterlerin kendisi değil, işaretçi kopyalanır.


Tamam, derleyici bu çevirir böylece memcpy, buraya bakın: godbolt.org/z/nPxqWc - Ama şimdi aynı işaretçileri geçmesi halinde ave bve *a = *bbir çevrilir memcpyçünkü için, tanımsız davranış olduğunu memcpy"hafıza alanları gerekir değil örtüşme." (man sayfasından alıntı). Öyleyse derleyici kullanmakta yanlış mı memcpyyoksa böyle bir ödevi yazarken hatalı mıyım?
kullanıcı değil

7

Gerçek ve hayali parçalı karmaşık sayıdaki gibi "Karmaşık" mı demek istediniz? Bu pek olası görünmüyor, bu yüzden olmasa da bir örnek vermeniz gerekecek çünkü "karmaşık" C dili açısından belirli bir şey ifade etmiyor.

Yapının doğrudan bellek kopyasını alacaksınız; İstediğiniz şeyin bu olup olmadığı yapıya bağlıdır. Örneğin, yapı bir işaretçi içeriyorsa, her iki kopya da aynı veriyi gösterecektir. İstediğiniz bu olabilir veya olmayabilir; bu program tasarımınıza bağlıdır.

'Akıllı' bir kopya (veya 'derin' bir kopya) gerçekleştirmek için, kopyalamayı gerçekleştirmek üzere bir işlev uygulamanız gerekecektir. Yapının kendisi de işaretçiler ve işaretçiler içeren yapılar ve belki de bu tür yapılara işaretçiler içeriyorsa (belki de "karmaşık" derken kastettiğin şey budur) ve bakımı zor ise, bunu başarmak çok zor olabilir. Basit çözüm, C ++ kullanmak ve her yapı veya sınıf için kopyalama yapıcıları ve atama işleçleri uygulamaktır, ardından her biri kendi kopya anlamından sorumlu olur, atama sözdizimini kullanabilirsiniz ve daha kolay korunur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.