Niet-ondertekende en ondertekende gehele getallen in C

Mogelijk duplicaat:
   Niet-ondertekend lang met negatieve waarde
   Negatieve nummers toewijzen aan een niet-ondertekende int?

#include

int main()
{
    struct a 
    {
        unsigned int i:3;
        int c:3; 
    }s;
    s.i=5;
    s.c=5;

    printf("s.i=%d\n",s.i);
    printf("s.c=%u\n",s.c);
    unsigned int x = -1;
    printf(" x = %d", x);
    return 0;
}

Dit levert:

s.i=5
s.c=4294967293
x=-1 

Ik ben niet duidelijk over de uitvoer van "x" en "sc" (sc kan slechts 3 bits opslaan maar geeft in de uitvoer een zeer grote waarde). "X" wordt als niet-ondertekend aangegeven, zodat de bits die in x zijn opgeslagen 1111111 ....... en de uitvoer van x moet een grote waarde zijn in plaats van -1. De 1e printf() -instructie geeft het resultaat zoals verwacht Ik gebruik devc ++ compiler.

0
Al uw argumenten worden doorgegeven aan printf als int s, het is aan u om de juiste opmaakspecificaties in te stellen.
toegevoegd de auteur K-ballo, de bron
Waarom heb je je code niet laten inspringen?
toegevoegd de auteur Alex Lockwood, de bron

2 antwoord

De uitvoer is afhankelijk van de ondertekening van het formaatkarakter, niet van de ondertekende verklaring. Denk er eens over: printf kan niet weten of x int of unsigned int is verklaard, aangezien er geen type-informatie is doorgegeven in C. Dus het drukt af op basis van hoe je het hebt laten afdrukken. % d is ondertekend, dus u krijgt een waarde ondertekend. Met s.c is het een int, dus het is ondertekend, maar u hebt het afgedrukt met% u zodat het als niet ondertekend wordt behandeld.

Voor s.i is het niet ondertekend, dus 5 kan in de 3 bits passen, dus wordt het doorgegeven aan printf als 5 zonder teken dat het verlengt, dus% d (of% u) drukt het af als 5.

4
toegevoegd
@ sourabh912 Jim heeft hier gelijk. Als je nog steeds moeite hebt om het te begrijpen, zal mijn antwoord misschien wat langdradiger zijn. Het lezen van de printf() handleiding en de Wikipedia-pagina over de complement-weergave van twee kan ook helpen.
toegevoegd de auteur Darshan Rivka Whittle, de bron
@JimBalter Bedankt!
toegevoegd de auteur Darshan Rivka Whittle, de bron
Wat ik, zoals ik al zei, in mijn bewerking heb beantwoord. s.i wordt aangegeven als unsigned int zodat het tekenbit niet wordt uitgebreid, dus maakt het niet uit of u het met% d of% u afdrukt. Niet zo met x en s.c, waarvan elk het tekenbit heeft uitgebreid, dus de afgedrukte waarde is afhankelijk van het formaat.
toegevoegd de auteur Jim Balter, de bron
@Darshan Ik denk dat mijn antwoord ontbreekt in het aannemen van een goed begrip van hoe negatieve gehele getallen worden weergegeven. Uw antwoord kan dit goed verhelpen.
toegevoegd de auteur Jim Balter, de bron
Je hebt je gedachte niet afgemaakt, maar ik heb in mijn bewerking geantwoord. Er is geen "als" over - dit is een basiskwestie die wordt begrepen door alle C-programmeurs die niet net beginnen zoals u bent.
toegevoegd de auteur Jim Balter, de bron
sorry, er was een probleem met mijn internetverbinding, dus ik kon mijn commentaar niet bewerken. Eigenlijk vroeg ik dat zou niet de uitvoer van printf moeten zijn ("s.i =% d \ n", s.i); be -3 (omdat mijn opmaakspecificatie% d is en "i" alleen in de 3 bits is opgeslagen, dus 5 (101) moet als negatief worden behandeld).
toegevoegd de auteur sourabh912, de bron
als wat je zei waar is, dan zou niet de uitvoer van printf ("s.i =% d \ n", s.i);
toegevoegd de auteur sourabh912, de bron

Het lijkt erop dat er twee dingen aan de hand zijn die begrepen moeten worden:

  • printf() conversion specifiers
  • Integral conversions

En nog twee dingen die helpen om uw output te begrijpen:

  • Argumentenpromotie voor variadische functieargumenten
  • Twee-complementrepresentatie

Ten eerste is printf() een variadische functie. Het weet niet wat de typen argumenten zijn (anders dan de tekenreeks), dus u moet conversiespecificaties gebruiken om de argumenten te interpreteren. Deze argumenten vallen onder de "standaardpromotiepromoties", zodat uw 3-bits bitvelden worden gepromoveerd tot int s.

U gebruikt specificatieopgaven voor conversie (% d , % u en % d ) die niet overeenkomen met de ondertekening van uw gegevens, zodat u krijg ongedefinieerd gedrag dat afhangt van hoe uw gegevens daadwerkelijk in het geheugen worden weergegeven.

Ten tweede verklaart de C11-norm:

6.3.1.3 Ondertekende en niet-ondertekende gehele getallen

     
      
  • Wanneer een waarde met een integer-type wordt geconverteerd naar een ander integer-type anders dan _Bool, als de waarde kan worden weergegeven door het nieuwe type, is deze ongewijzigd.

  •   
  • Anders, als het nieuwe type niet ondertekend is, wordt de waarde geconverteerd door herhaaldelijk één waarde toe te voegen of af te trekken die hoger is dan de maximumwaarde die in het nieuwe type kan worden weergegeven totdat de waarde binnen het bereik van het nieuwe type valt.

  •   
  • Anders wordt het nieuwe type ondertekend en kan de waarde daarin niet worden weergegeven; ofwel is het resultaat door de implementatie gedefinieerd of wordt een door de implementatie gedefinieerd signaal opgehaald.

  •   

(Voor zover ik weet, zijn de relevante details hier althans sinds C89 waar.)

Dit vertelt ons een paar dingen over uw code:

  • Wanneer u -1 toewijst aan een unsigned int , wordt UINT_MAX + 1 eraan toegevoegd, waardoor UINT_MAX wordt weergegeven of 4294967295 voor 32-bits gehele getallen.

  • Wanneer u 5 aan een 3-bits ondertekend bitveld toewijst, is het resultaat door de implementatie gedefinieerd.

Dus je hebt zowel ongedefinieerd als door de implementatie gedefinieerd gedrag, maar we kunnen nog steeds proberen om je uitvoer te begrijpen, gewoon voor de lol. Ik neem 32-bits gehele getallen en twee-complement vertegenwoordiging aan.

Uw systeem vertegenwoordigt de 4294967295 opgeslagen in x als 11111111 11111111 11111111 11111111 . Wanneer je printf() hebt verteld dat het argument dat je doorgeeft is ondertekend, worden diezelfde bits geïnterpreteerd als -1 , wat de uitvoer is die je hebt gekregen.

Voor s.c is het implementatie-gedefinieerde gedrag dat u lijkt te hebben gekregen eenvoudig: de drie bits 101 vertegenwoordigen 5 zijn opgeslagen zoals ze zijn. Dat betekent dat met de juiste conversiespecificatie printf() s.c moet weergeven als -3 .

Dit zijn de waarden die je hebt toegewezen:

s.i = 101
s.c = 101
  x = 11111111 11111111 11111111 11111111

De 3-bits waarden worden gepromoveerd tot 32-bits door links opvullen met 0 voor de niet-ondertekende waarde en het herhalen van het teken voor de ondertekende waarde:

s.i = 00000000 00000000 00000000 00000101
s.c = 11111111 11111111 11111111 11111101
  x = 11111111 11111111 11111111 11111111

Wat, geïnterpreteerd als ondertekend, niet ondertekend en ondertekend getallen geeft:

s.i=5
s.c=4294967293
x=-1

De x = -1 suggereert dat je in feite een twee-complement-weergave gebruikt (wat in ieder geval een redelijk veilige gok was), en de uitvoer voor sc suggereert dat uw int s zijn 32-bits breed.

2
toegevoegd
Dit antwoord is onvolledig en op zijn minst mild verkeerd zonder het noemen van ongedefinieerd gedrag. OP's code vertoont slechte UB.
toegevoegd de auteur R.., de bron
Dat is een geweldige bewerking, +1. Het afdrukken van UINT_MAX als -1 met % d is inderdaad een zeer sterke indicatie van twee's complement (maar natuurlijk geen bewijs, omdat het ongedefinieerd gedrag is - zo dicht mogelijk bij een bewijs zoals je kunt krijgen, hoewel).
toegevoegd de auteur Daniel Fischer, de bron
U kunt het complement van twee niet afleiden uit de waarde van x . Het converteren van -1 naar een niet-ondertekend type resulteert altijd in U {type} _MAX , ongeacht welke representatie wordt gebruikt voor ondertekende gehele getallen. U kunt twee van de complementen afleiden van s.c = 5; resulterend in de waarde -3 . Niet helemaal, omdat het converteren van waarden buiten bereik naar getekende integertypen implementatiegericht is, en de standaard verbiedt geen rare methoden om dit te doen.
toegevoegd de auteur Daniel Fischer, de bron
@R Bedankt voor de reactie. Ik heb mijn antwoord bijgewerkt - laat het me weten als je andere fouten opmerkt.
toegevoegd de auteur Darshan Rivka Whittle, de bron
@DanielFischer Ten eerste, bedankt voor de reactie. Nu dat ik beter begrijp hoe ondertekend-naar-niet-ondertekende conversie werkt, denk ik dat het bewijs (of de buurt ervan) van het complement van twee is dat UINT_MAX wordt uitgevoerd als -1 wanneer u printf() vertelt, is dit een waarde die is ondertekend. Hoe dan ook, bedankt voor de correcties en laat het me weten als je iets anders op een dwaalspoor ziet.
toegevoegd de auteur Darshan Rivka Whittle, de bron
@ sourabh912 Ja, je hebt het. Merk op dat 111 ... 101 eigenlijk -3 is. (Dit alles veronderstelt de complementaire rekenkunde van twee, die bijna universeel is.)
toegevoegd de auteur Jim Balter, de bron
Het is een heel goede verklaring. Ik heb een kleine twijfel om te wissen. Als de waarde van sc = 3 (011) is, wordt deze geconverteerd naar sc = 00000000 00000000 00000000 00000011 als een integer geheel van 32 bits. Omdat sc is ondertekend en de waarde 5 (101) geeft dit aan als een negatief getal, daarom geconverteerd naar 32- bit integer waren er enen in alle bitposities behalve de laatste drie bitposities (in dit geval zullen de laatste drie bits het nummer zelf zijn).
toegevoegd de auteur sourabh912, de bron
Bedankt Jim en Darshan. Beiden waren een grote hulp.
toegevoegd de auteur sourabh912, de bron
@ R .. Kunt u het antwoord alstublieft invullen?
toegevoegd de auteur sourabh912, de bron