2011-11-10 22 views
6

Mam strukturę do reprezentowania ciągów w pamięci wyglądające tak:Korzystanie size_t do określania precyzji sznurku w printf C za

typedef struct { 
    size_t l; 
    char *s; 
} str_t; 

wierzę używając size_t sens dla określenia długości łańcucha char . Chciałbym również wydrukować ten ciąg za pomocą printf("%.*s\n", str.l, str.s). Jednak precyzja * oczekuje argumentu w postaci int, a nie size_t. Nie udało mi się znaleźć niczego istotnego na ten temat. Czy jest jakiś sposób prawidłowego użycia tej struktury, bez rzutowania na int w wywołaniu ?

+0

czy nie planujesz na końcu wartości "char *"? Jeśli jest on zakończony null, nie widzę, jakie ustawienie dokładności na pierwszy rzut oka byłby w pierwszej kolejności. Poza tym, myślę, że twoje wybory to: rzuć lub zmień 'size_t' na' int' i po prostu nigdy nie pozwól, aby było negatywne. –

+2

@EvanTeran Struny faktycznie wskazują fragmenty większego bufora, więc znak 'null' jest gdzieś dalej. Rozmiar 'int' nie jest problemem, ponieważ łańcuchy mają zwykle co najwyżej kilkaset bajtów. Frustruje mnie to, że chociaż 'size_t' dobrze pasuje do tego celu, C99 nie definiuje specjalnego modyfikatora precyzji, który akceptuje argument" size_t ". –

Odpowiedz

3

można zrobić makro

#define STR2(STR) (int const){ (STR).l }, (char const*const){ (STR).s } 

a następnie użyć tego jako printf("%.*s\n", STR2(str)).

Pamiętaj, że to dwukrotnie ocenia STR, więc należy zachować ostrożność przy działaniach niepożądanych, ale prawdopodobnie już to wiesz.

EDIT:

Używam inicjalizatory złożone takie, że są to konwersje niejawne. Jeśli coś pójdzie nie tak, istnieje większa szansa, że ​​kompilator ostrzeże cię, niż z wyraźnym rzutem.

Np jeśli STR ma pole .l że jest wskaźnikiem i chcesz umieścić tylko oddanych do int, wszystkie kompilatory chęcią przekonwertować wskaźnik int. Podobny w przypadku pola .s musi to być zgodne z char* lub czymś kompatybilnym, w przeciwnym razie pojawi się ostrzeżenie lub błąd.

+0

Nadal jest rzutem, ale na każdym kroku wygląda lepiej niż rzutowanie. Zastanawiasz się, jak dokładnie ten inicjator związku różni się od wyraźnej obsady? Dokumenty GCC wspominają, że: "Literały złożone dla typów skalarnych i typów związków są również dozwolone, ale wtedy literał złożony jest odpowiednikiem obsady". (http://gcc.gnu.org/onlinedocs/gcc/Compound-Literals.html) –

+0

@LuciStanescu, nie nie jest obsadą, jak powiedziałem, niejawną konwersją. Wszystkie odlewania są konwersjami, ale nie wszystkie konwersje są rzutowane. Różnica byłaby wtedy, gdybyś przekazał "STR" o polu '.l', które byłoby wskaźnikiem, a nie typem liczby całkowitej. Gdyby to był rzut, wszystkie kompilatory z radością się nawrócą. W przypadku konwersji większość kompilatorów ostrzega. BTW to samo dotyczy pola '.s', będę edytować mój post. –

+0

Rzeczywiście, chociaż instrukcja GCC wspomina, że ​​w przypadku typów skalarnych inicjator złożony jest równoważny rzutowi, to narzeka, że ​​przekazano typ nieintermergiczny. –

5
printf("%.*s\n", (int)str.l, str.s) 
//    ^^^^^ use a type cast 

Edit

OK, nie czytać poprawnie na pytanie. Nie chcesz używać rzutowania typu, ale myślę, że w tym przypadku: trudne.

Albo to, albo po prostu użyć fwrite

fwrite(str.s, str.l, 1, stdout); 
printf("\n"); 
+1

+1 dla sugestii 'fwrite'. Chociaż prawdopodobnie pójdę z 'fwrite (str.s, 1, str.l, stdout)' i sprawdzam wartość zwracaną aby zobaczyć, ile znaków zostało poprawnie zapisanych. – Nemo

+0

Przykładowy plik 'printf()' podany przeze mnie był naprawdę tylko przykładem, a pytanie jasno określa, że ​​celem jest użycie size_t w celu określenia precyzji. Jeśli zastanawiasz się, dlaczego nie podoba mi się sugestia 'fwrite': gdy zaczniesz mieć bardziej złożone stałe ciągi, wiele str_t ciągów, liczb i używaj' snprintf' do budowania wiadomości, to staje się zbyt denerwujące, aby nie używać formatowania Funkcje. Sądzę więc, że utknąłem z "twardej" sugestii z twojej odpowiedzi :-). Ale okrzyki! –

+0

@nemo: Pomyślałem o tym, ale potem musisz radzić sobie z krótką liczbą zwrotów> 0, podczas gdy w mojej wersji wartość zwracana to 1 (sukces) lub 0 (niepowodzenie). Zgodnie z dokumentami na 'fwrite()' na moim komputerze (Mac OS X), tak czy inaczej, dostaniesz tylko krótką liczbę błędów zapisu, więc prawdopodobnie chcesz przestać, bez względu na to, co w tym momencie. – JeremyP

1

Nie ma gwarancji, że size_t jest int, lub że może być reprezentowane w int. Jest to tylko część spuścizny C, która nie definiuje dokładnego rozmiaru int, w połączeniu z obawami, że implementacja size_t może wymagać użycia dużych obszarów pamięci (które mają więcej niż MAX_INT wartości w nich).

Najczęstszym błędem dotyczącym size_t jest założenie, że jest to odpowiednik unsigned int. Takie stare błędy były powszechne, az osobistych doświadczeń wynika, że ​​przenoszenie z architektury 32-bitowej na 64-bitową jest uciążliwe, ponieważ trzeba cofnąć to założenie.

W najlepszym razie można użyć obsady. Jeśli naprawdę chcesz pozbyć się obsady, możesz odrzucić użycie size_t.

+1

W rzeczywistości istnieje gwarancja, że ​​'size_t' nigdy nie będzie równoznaczne z' int'; może to być jednak odpowiednik "unsigned int". (Musi być niepodpisany, jego rozmiar może być lub nie być taki sam jak rozmiar 'int'.) –

Powiązane problemy