2015-04-25 13 views
18

Chcę być w stanie zrobić coś takiego:C struktura jako typ danych

typedef struct 
{ 
    char* c_str; 

} string; 

string s = "hello"; 

Czy można to zrobić w inny sposób?

Wiem, że jest możliwe, aby to zrobić:

typedef struct 
{ 
    char* c_str; 

} string; 

string s = { "hello" }; 

Ale nie podoba mi się klamrowych, gdy jest tylko jedna zmienna członkiem.

Odpowiedz

30

można użyć typedef zamiast struct:

typedef char* string; 
string s = "hello"; 

Ale wtedy const string uczyniłaby wskaźnik const, a nie spiczasta do danych. Tak więc const string s jest odpowiednikiem char* const s. Rozwiązaniem może być zdefiniowanie dodatkowego typu dla ciągów stałych:

typedef char* string; 
typedef const char* const_string; 

Dla oryginalnej struktury jest tak samo. (C++ ma ten sam „problem”, dlatego że ma iterator i const_iterator w swoich typów kontenerów.)

Zaletą typedef dla typu wskaźnika jest to, że można wpisać

string s1, s2, s3; 

zamiast

char *s1, *s2, *s3; 
+8

Warto wspomnieć, że jednym znanym problemem, który przychodzi wraz z 'typedef' wskaźnika jest fakt, że' const' stosuje się do wskaźnika zamiast spiczastych danych. Innymi słowy, 'const string s' będzie równoznaczne z' char * const s' zamiast 'const char * s' (czego normalnie oczekujesz). –

+0

Innym problemem z 'typedef' jest to, że tworzy on alias dla typu, a nie nowego typu. Powoduje to, że rodzaj sprawdzania przez kompilator, jakiego można oczekiwać od C++, nie jest tym, co otrzymujemy za pomocą C. Tak więc 'typedef' z' char * 'będzie taki sam jak każdy inny' char * ', o ile kompilator jest zaniepokojony. Jeśli pytanie ma zapewnić sposób na bardziej szczegółowe sprawdzanie typu za pomocą 'struct', to przejście z' typedef' nie zapewni sprawdzenia typu kompilatora. –

14

w C, nie jest możliwe, ale można to zrobić w C++, jeśli dodać konstruktora, który przyjmuje jedną odpowiedni parametr. Kompilator zrobi resztę. Możesz oznaczyć konstruktor jako jawny, jeśli chcesz uniknąć tego niejawnego zachowania konwersji.

W C++:

struct string { 

    char * m_c_str; 

    /* explicit */ string(char* c_str) : m_c_str(c_str) { } 
}; 

int main(int argc, char * argv[]) { 

    string s = "hello"; 

    return 0; 

} 
4

Czy można to zrobić w inny sposób?

Nie, to nie jest możliwe, aby to zrobić z struct lub union. Para 16 sekcji 6.7.9 stwierdza, że ​​

[...] inicjatora dla obiektu, który ma łączną lub Union Typ powinny być braceenclosed lista inicjalizatorów dla elementów lub nazwy członków.

Istnieje inny sposób, aby zrobić to samo z różnymi typami danych, jak wyjaśniono w tym answer.

3

Nie jestem pewien, czy to jest faktycznie kanoniczne i pasuje do standardów C, ponieważ Microsoft Visual Studio ma reputację nieco luźnego w interpretacji standardu, jednak tutaj jest podejście, które kompiluje i działa podczas przeglądania w debugerze Visual Studio 2005.

Ale jeśli nie lubisz inicjalizatorów z kręconymi szelkami, prawdopodobnie nie będziesz również dbał o makro.

typedef struct { 
    char *c_str; 
} String; 

// following macro assigns the string to the struct member and uses the 
// comma operator to make the statement return the original struct variable. 
#define xString(x, y) ((x).c_str = (y), (x)) 

void jjj (void) 
{ 
    String b = xString(b,"hello"); 

} 

byłem w stanie zdefiniować wiele zmiennych w czasie, tak wiele definicji zmiennych w tej samej linii kompiluje jak w:

String b = xString(b,"hello"), c = xString(c,"Jello"); 

może się zdarzyć, że będziesz chciał mieć makro, które zrobi całe wyrażenie w formie funkcjonalnej konstrukcji wyglądającej na język, chociaż może istnieć tylko jedna na instrukcję, więc wielokrotność na tej samej linii wymagałaby rozdzielenia średników na poszczególne instrukcje definicji.

#define xString(x,y) String x = (x.c_str = (y), x) 

i byłoby wykorzystywane jako

void jjj (void) 
{ 
    xString(myStruct, "hello"); 
    int j = 2; 

    // .. do stuff 
} 

lub można po prostu użyć

#define xString(x,y) String x = {y} 

lista Initializer naprawdę wydaje się być najlepszym rozwiązaniem, jeśli chcesz struct z jakiegoś powodu aby umożliwić sprawdzanie argumentów czasu kompilacji dla określonego typu char *.

Kiedy robi się coś niesamowitego, należy wykonać następujące czynności w celu zainicjowania wielu członków struct w momencie zdefiniowania zmiennej struct.

typedef struct { 
    int len; 
    char *c_str; 
} String2; 

#define yString(x,y) x = (x.c_str = (y), x.len = strlen(y), x) 

void jjj (void) 
{ 

    String2 b2 = yString(b2,"Hello"); 
    int j = 2; 

    // .. do stuff 
} 

Będąc ciekawy Próbowałem inną odmianę, która wygląda następująco. To odsuwa się od konkretnego pytania i więcej na to, jakie są możliwości zastosowania tego podejścia w króliczej dziurze. Korzystanie z tego samego struct z innym makrem, który pozwala określić członka struct, aby zainicjować wraz z wartością.

typedef struct { 
    int len; 
    char *c_str; 
} String2; 

#define zString(x,y,a,b) x=(x.c_str=(y),x.a=(b),x) 

void jjj (void) 
{ 
    String2 b3 = zString(b3,"Hello",len,72); 

    // ... do stuff 
}