Bir nesneyi başlatırken {0} ne anlama geliyor?


252

Bir {0}nesneyi başlatmak için ne zaman kullanılır, bu ne anlama gelir? Hiçbir {0}yere referans bulamıyorum ve kıvırcık ayraçlar nedeniyle Google aramaları yardımcı olmuyor.

Örnek kod:

SHELLEXECUTEINFO sexi = {0}; // what does this do?
sexi.cbSize = sizeof(SHELLEXECUTEINFO);
sexi.hwnd = NULL;
sexi.fMask = SEE_MASK_NOCLOSEPROCESS;
sexi.lpFile = lpFile.c_str();
sexi.lpParameters = args;
sexi.nShow = nShow;

if(ShellExecuteEx(&sexi))
{
    DWORD wait = WaitForSingleObject(sexi.hProcess, INFINITE);
    if(wait == WAIT_OBJECT_0)
        GetExitCodeProcess(sexi.hProcess, &returnCode);
}

Bu olmadan, yukarıdaki kod çalışma zamanında çökecektir.

Yanıtlar:


302

Burada olanlara toplu başlatma denir . İşte ISO spesifikasyonunun 8.5.1 bölümündeki bir agreganın (kısaltılmış) tanımı:

Toplama, kullanıcı tarafından bildirilmiş kurucuları, özel veya korumalı statik olmayan veri üyeleri, temel sınıfları ve sanal işlevleri olmayan bir dizi veya sınıftır.

Şimdi, {0}böyle bir agregayı başlatmak için kullanmak temelde 0her şeyin bir hilesi . Bunun nedeni, toplam başlatma kullanıldığında tüm üyeleri belirtmeniz gerekmemesi ve spesifikasyonun tüm belirtilmemiş üyelerin varsayılan olarak başlatılmasını gerektirmesidir, yani 0basit tipler için ayarlanmıştır .

İşte spec ilgili teklif:

Listede toplamda üyelerden daha az başlatıcı varsa, açıkça başlatılmayan her üye varsayılan olarak başlatılır. Misal:

struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };

başlatır ss.aile 1, ss.bile "asdf", ve ss.cformun bir ifadenin değeri ile int(), bir, 0.

Bu konuyla ilgili tüm bilgileri burada bulabilirsiniz


15
Mükemmel yanıt. Sadece {0} ile bir toplamayı başlatmanın sadece {} ile başlatmanın aynı olduğunu eklemek istedim. Belki birincisi, yerleşik tiplerin sıfırlandığını daha açık hale getirir.
James Hopkin

7
Bazı derleyiciler {}
boğdu

10
Pek değil .. C ++ 'da, ilk üye sıfır yapılamazsa, {0} çalışmaz. Örnek: yapı A {Bb; int i; char c; }; yapı B {B (); B (dizi); }; A a = {}; // bu ifade 'A a = {0}' olarak yeniden yazılamaz.
Aaron

25
@Branan, çünkü C "{}" içinde geçerli değil. C ++ 'da öyle. @ don.neufeld, bu C ++ 03 ile değiştirildi (varsayılan başlangıç ​​-> değer başlangıç ​​değeri). Alıntı, C ++ 98 Standardını aklınızda bulundurun.
Johannes Schaub - litb

3
Peki, bu ZeroMemory () (VC ++ 'da) kullanma gereksiniminin yerini alıyor mu?
Ray

89

Dikkat edilmesi gereken bir şey, bu tekniğin dolgu baytlarını sıfıra ayarlamayacağıdır. Örneğin:

struct foo
{
    char c;
    int  i;
};

foo a = {0};

Şununla aynı değildir:

foo a;
memset(&a,0,sizeof(a));

İlk durumda, c ve i arasındaki ped baytları başlatılmaz. Neden umursuyorsun? Bu verileri diske kaydediyorsanız veya bir ağ üzerinden veya başka bir şeyle gönderiyorsanız, bir güvenlik sorununuz olabilir.


13
Tabii ki, sadece farklı işlemciler / derleyiciler üzerinde farklı dosya kodlaması üretebilen '(f, & a, sizeof (a))' yazarsanız bir güvenlik sorunudur. İyi biçimlendirilmiş çıktılar memset olmadan güvenli olacaktır.
Aaron

3
Ayrıca, ağ üzerinden bir şeyler gönderiyorsanız, her zaman paketlenecek hizalamayı ayarlarsınız. Bu şekilde mümkün olduğunca az ek dolgu baytı elde edersiniz.
Mark Kegel

18
Spesifikasyonun dolgunun başlatılmasını gerektirmese de, herhangi bir aklı başında derleyicinin "etrafında" başlatılması için sadece zamana mal olacağından not edilmelidir.
Tomas

4
"Değil" kelimelerinin kullanımı ile ilgili konu almak istiyorum. Dolgu baytları uygulama tanımlıdır. Derleyici foo a = {0} 'ı kendi boş zamanlarında memset'e (& a, 0, sizeof (a)) çevirmekte serbesttir. Dolgu baytlarını "atlamak" ve yalnızca foo.c ve foo.i'yi ayarlamak gerekmez. (Potansiyel) güvenlik hatası için +1 +1
SecurityMatt

3
@Leushenko nokta 19 "açıkça başlatılmamış tüm alt nesneler" diyor, bu yüzden özensiz olmak 21 (ki alıntı) noktaya eğilir. Spesifikasyonun dolgunun son başlatıcı anına kadar başlatılmasına izin verilmesi, ardından dolgunun sıfırlanması gerektiğinde, özellikle belirlenen başlatıcılar kullanıldığında başlatıcıların sıra dışı görünebileceği düşünüldüğünde, gerçekten tuhaf olurdu.
MM

20

Boş bir toplama başlatıcısının da çalıştığını unutmayın:

SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};

11

Neden çöktüğüne cevaben ShellExecuteEx(): SHELLEXECUTEINFO"sexi" yapınızın birçok üyesi var ve siz sadece bazılarını başlatıyorsunuz.

Örneğin, üye sexi.lpDirectoryherhangi bir yeri işaret ediyor olabilir, ancak ShellExecuteEx()yine de kullanmaya çalışacaktır, bu nedenle bir bellek erişim ihlali alırsınız.

Satırı eklediğinizde:

SHELLEXECUTEINFO sexi = {0};

yapı kurulumunuzun geri kalanından önce, ilgilendiğiniz belirli öğeleri başlatmadan önce derleyiciye tüm yapı üyelerini sıfırlamasını söylersiniz. ShellExecuteEx()Sıfırsa, sexi.lpDirectoryyoksayması gerektiğini bilir .


7

Ben de örneğin dizeleri başlatmak için kullanın.

char mytext[100] = {0};

5
Tabii ki, eğer mytext bir dize kullanılıyorsa, char mytext [100]; yazım [0] = '\ 0'; boş bir dize verme ile aynı etkiye sahip olur, ancak yalnızca uygulamanın ilk baytı sıfırlamasına neden olur.
Chris Young

@Chris: Sıklıkla kısmi nesne başlatma için bir sözdizimi olmasını isterdim. X'i "bildirip başlatabilme", ​​ardından y, o zaman z ile aynı şekilde yapabilmek, x, y ve z bildirmek ve sonra x, y ve z'yi başlatmak, ancak yalnızca 100 bayt başlatmaktan çok daha iyidir. aslında başlatılmaya ihtiyacı oldukça israf görünüyor.
supercat

7

{0}C ve C ++ 'da herhangi (tam nesne) tip için geçerli bir başlatıcıdır. Bir nesneyi sıfıra başlatmak için kullanılan yaygın bir deyimdir (bunun ne anlama geldiğini görmek için okumaya devam edin).

Skaler tipler (aritmetik ve işaretçi tipleri) için kaşlı ayraçlar gereksizdir, ancak açıkça izin verilir. ISO C standardının N1570 taslağından alıntı , bölüm 6.7.9:

Bir skaler için başlatıcı, isteğe bağlı olarak parantez içine alınmış tek bir ifade olmalıdır.

Nesneyi sıfıra başlatır ( 0tamsayılar 0.0için, kayan nokta için, işaretçiler için boş bir işaretçi).

Skaler olmayan tipler (yapılar, diziler, birlikler) için, nesnenin ilk öğesinin sıfıra başlatıldığını {0}belirtir . Yapıları, yapı dizilerini, vb. İçeren yapılar için, bu yinelemeli olarak uygulanır, bu nedenle ilk skaler eleman, tipe uygun olarak sıfıra ayarlanır. Herhangi bir başlatıcıda olduğu gibi, belirtilmeyen öğeler sıfır olarak ayarlanır.

Ara ayraçlar ( {, }) atlanabilir; örneğin her ikisi de geçerlidir ve eşdeğerdir:

int arr[2][2] = { { 1, 2 }, {3, 4} };

int arr[2][2] = { 1, 2, 3, 4 };

bu nedenle, örneğin, { { 0 } }ilk öğesi skaler olmayan bir tür için yazmak zorunda kalmazsınız .

Yani bu:

some_type obj = { 0 };

objsıfıra sıfırlamanın kısa bir yoludur , yani her skaler alt nesnesinin bir tamsayı, kayan nokta ise veya işaretçi ise boş gösterici objolarak ayarlanması anlamına gelir .00.0

Kurallar C ++ için benzerdir.

Özel durumunuzda, sexi.cbSizevb. Değerler atadığınızdan, bunun SHELLEXECUTEINFObir yapı veya sınıf türü (veya muhtemelen bir birlik, ancak muhtemelen değil) olduğu açıktır , bu yüzden bunların hepsi geçerli değildir, ancak dediğim gibi { 0 }ortak bir şeydir daha genel durumlarda kullanılabilecek deyim.

Bu, (zorunlu olarak) nesnenin temsilini all-bits-zero olarak ayarlamak için kullanılmaya eşdeğer değildirmemset . Ne kayan nokta 0.0ne de bir boş gösterici zorunlu olarak all-b-zero olarak temsil { 0 }edilmez ve bir başlatıcı, dolgu baytlarını belirli bir değere ayarlamaz. Bununla birlikte, çoğu sistemde muhtemelen aynı etkiye sahiptir.


1
C ++ ' {0}da yapıcı kabul etmeyen bir nesne için geçerli bir başlatıcı değildir 0; ne de ilk elemanı bu şekilde olan bir agrega (veya
MM

3

C / c ++ ancak IIRC'de çalıştığımdan beri aynı kısayol diziler için de kullanılabilir.


2

Her zaman merak ettim, neden böyle bir şey kullanmalısın?

struct foo bar = { 0 };

İşte açıklamak için bir test örneği:

check.c

struct f {
    int x;
    char a;
} my_zero_struct;

int main(void)
{
    return my_zero_struct.x;
}

Ben ile derlemek gcc -O2 -o check check.cve sonra ile sembol tablosunu çıktı readelf -s check | sort -k 2(Bu bir x64 sisteminde ubuntu 12.04.2 üzerinde gcc 4.6.3 ile). Alıntı:

59: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
48: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
25: 0000000000601018     0 SECTION LOCAL  DEFAULT   25 
33: 0000000000601018     1 OBJECT  LOCAL  DEFAULT   25 completed.6531
34: 0000000000601020     8 OBJECT  LOCAL  DEFAULT   25 dtor_idx.6533
62: 0000000000601028     8 OBJECT  GLOBAL DEFAULT   25 my_zero_struct
57: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT  ABS _end

Burada önemli olan, o my_zero_structzamandan sonra __bss_start. Bir C programındaki ".bss" bölümü, belleğin daha önce sıfıra ayarlanan bölümüdür .bss üzerinde wikipedia'yamain bakın .

Yukarıdaki kodu şu şekilde değiştirirseniz:

} my_zero_struct = { 0 };

Sonra ortaya çıkan "kontrol" yürütülebilir ubuntu 12.04.2 üzerinde gcc 4.6.3 derleyici ile tamamen aynı görünüyor ; my_zero_structhala .bssdaha önce bölüm ve böylece otomatik olarak sıfıra başlatılır mainolarak adlandırılır.

Yorumlarda, memset"tam" yapıyı başlatabileceğine dair ipuçları da bir gelişme değildir, çünkü .bssbölüm tamamen temizlenir, bu da "tam" yapının sıfıra ayarlandığı anlamına gelir.

Bu olabilir C dili standart bunların hiçbirini söz etmediğini, ama gerçek bir dünya C derleyicisi farklı bir davranış görmedim.


Genel ve statik değişkenler her zaman varsayılan olarak 0 veya varsayılan ctor olarak başlatılır. Ancak yerel olarak f örneğini bildirirseniz farklı sonuçlar alabilirsiniz.
Logman

0

Tüm yapınızı boş / sıfır / null değerlere başlatmak için sözdizimi şekeri.

Uzun versiyon

SHELLEXECUTEINFO sexi;
sexi.cbSize = 0;
sexi.fMask = 0;
sexi.hwnd = NULL;
sexi.lpVerb = NULL;
sexi.lpFile = NULL;
sexi.lpParameters = NULL;
sexi.lpDirectory = NULL;
sexi.nShow = nShow;
sexi.hInstApp = 0;
sexi.lpIDList = NULL;
sexi.lpClass = NULL;
sexi.hkeyClass = 0;
sexi.dwHotKey = 0;
sexi.hMonitor = 0;
sexi.hProcess = 0;

Kısa versiyon

SHELLEXECUTEINFO sexi = {0};

O kadar kolay değil miydi?

Ayrıca güzel çünkü:

  • her üyeyi avlamanız ve başlatmanız gerekmez
  • daha sonra eklendiklerinde yeni üyeler başlatamayacağınızdan endişelenmenize gerek yoktur
  • aramak zorunda değilsinZeroMemory

-5

{0}, elemanını 0 olarak içeren anonim bir dizidir .

Bu, dizinin bir veya tüm öğelerini 0 ile başlatmak için kullanılır .

örneğin int arr [8] = {0};

Bu durumda arr öğelerinin tümü 0 olarak başlatılır.


4
{0}anonim bir dizi değil. Bu bir ifade bile değil. Bu bir başlatıcı.
Keith Thompson
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.