Newsgroup: it.comp.lang.c
Titolo: stranezza...

Salve,
l'altro giorno sono mi e' capitata una cosa che non mi era mai capitata
prima e che francamente non mi riesco a spiegare.
Stavo scrivendo codice in C su di un sistema embedded....
Una funzione che devo usare, di cui non ho il sorgente, si trova in una
libreria (.lib). Di questa funzione ho il prototipo in un file .h.
Ovviamente non mi e' stata data alcuna documentazione di tale libreria.....
Presto mi sono accorto che la funzione si comportava in modo strano, il
prototipo e' "char f(void);" eppure la funzione ritornava 255 (invece
di -1), cosi' ho incominciato ad usare casts forzati a signed int, variabili
di appoggio signed int etc.. ma nulla da fare, se chiamo quella funzione,
ottengo valori unsigned:
Code Snippet:
----------------- 
char f(void);      /* prototype */
...
signed char o1 = 0;
signed char o2 = -1;
signed char o3 = 0xff;
...
o1 = (signed char) f();
printf("%d\r\n",o1);
printf("%d\r\n",o2);
printf("%d\r\n",o3);
Output:
---------- 
255
-1
-1
Questo e' codice "vero" preso e postato qua. L'unica differenza, per evitare
problemi, ho rinominato la funzione in "f"....
Ora ho provato di pensare a tante cose:
- il prototipo non viene incluso, quindi si assume int, quindi se la
funzione ritorna 0xff esse viene interpretato come 255, ma... ho fatto in
modo di assicurarmi che il prototipo venga incluso e poi, usando la
variabile di appoggio non si spiega.
- ho pensato che magari la funzione, con prototipo char, ritorni invece un
int o un unsigned int o un unsigned char etc... ma ad ogni modo, se il
prototipo e' "char", il compilatore non sapendo cosa viene ritornato
realemente, si comporta come fosse un char e... dovrebbe stampare -1 quindi.
etc...
Non riesco a capire cosa possa succedere. Ovviamente il codice compila senza
errori e senza warnings. L'unica warnings che ottengo e' per:
signed char o3 = 0xff;
ma questo non e' ovviamente il problema.
Qualcuno ha piu' fantasia di me?
Grazie,
Tommy

 Qualcuno ha piu' fantasia di me?
Hai provato a chiamare un esorcista? :-)
Seriamente, l'unica spiegazione che mi viene in mente per un comportamento
del genere è che il tipo char deve essere più grande di 8 bit.
La cosa potrebbe aver senso nel caso di un processore embedded: chi l'ha
progettato avrà omesso l'infrastruttura per accedere ai singoli byte per
semplificare il firmware.
Ma, in tal caso, non si spiega il warning quando assegni 0xFF a una delle
variabili: se davvero il char ha più di 8 bit, anche solo uno in più, 255 è
un valore positivo perfettamente lecito per un signed char.
Potrebbe essere che il compilatore è scritto in due moduli: uno generico che
traduce il codice sorgente in un formato intermedio (un quadtree, per
esempio) e uno specifico che traduce il formato generico nel codice macchina
di ogni particolare processore.
In tal caso, il modulo generico conterrebbe un piccolo baco nella parte che
emette i warning: assume che il tipo char abbia sempre 8 bit, anziché
controllare quanti ne abbia effettivamente nel processore di destinazione.
--
Cingar
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

 destinazione.
Ripensandoci, quel warning non è affatto un baco. Anche se quel
particolare processore avesse il char più grande di 8 bit, assegnare a una
variabile char un valore che richiede più di 8 bit potrebbe essere un
grave errore di portabilità, per cui il compilatore fa benissimo a dare
almeno un piccolo avvertimento.
Per verificare la mia ipotesi che il tuo char sia di 16 bit, dovresti
provare a stampare il valore di sizeof(short): se è 1, l'ipotesi è provata.
Puoi anche cercare la costante CHAR_BITS negli header della libreria
standard.
--
Cingar
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 ottengo valori unsigned:
Siccome 255 = 1111 1111 che è -1 scritto in complemento a due (se ho 
detto cazzate perdonatemi), allora il risultato della funzione è corretto.
Però forse il compilatore che utilizzi vede i char come valori a 16 bit, 
in modo da usare un byte per tutti i valori da 0 a 255, e l'altro per 
indicare che segno deve avere il numero. In questo modo in QUEL 
compilatore i char variano da -255 a +255.
Possibile soluzione: puoi dichiarare dei signed short char?
Tentare non nuoce ;)
-- 
Quando noi ignoranti eravamo di più, tutto era più bello (A. Celentano).
  ___   _   ___ _  _______   _______ ___
|   \ /_\ | _ \ |/ / _ ) \ / /_   _| __|
| |) / _ \|   / ' <| _ \\ V /  | | | _|
|___/_/ \_\_|_\_|\_\___/ |_|   |_| |___|

 Ovviamente non mi e' stata data alcuna documentazione di tale libreria.....
Forse e' un metodo un po' "rude"...ma hai provato a fare il debugging 
del codice assembly di quella funzione, per capire meglio cosa succede?
Ciao
Dan

 ottengo valori unsigned:
La natura signed o unsigned del char dipende dal sistema.
La forzatura a (signed int) non serve assolutamente a nulla, perché il "wrap
around" da -1 a 255 avviene prima.

 standard.
Beh la tua teoria e' interessante, ma...
source:
-------- 
char c;
...
printf("sizeof(c) = %d\r\n",sizeof(c));
printf("sizeof(char) = %d\r\n",sizeof(char));
printf("sizeof(short) = %d\r\n",sizeof(short));
printf("sizeof(int) = %d\r\n",sizeof(int));
output:
-------- 
sizeof(c) = 1
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
e...
#define CHAR_BIT 8
#define SCHAR_MIN (-128)
#define SCHAR_MAX 127
#define UCHAR_MAX 255
Prima o poi usero l'ICE per capire questa cosa ma dovro' farlo fuori dagli
orari di lavoro. IL mio manager non mi e' sembrato molto interessato a
capire questa cosa, ha solo detto: hai ragione, non ha senso. Nel case metti
0xff invece di -1 e fregatene. Ovviamente questo non e' un comportamento
tipico, e' solo perche' il prodotto sara' entro 6 mesi abbandonato, questa
e' l'ultima release...
Tommy

 Tentare non nuoce ;)
;-)
Tommy

 Beh la tua teoria e' interessante, ma...
Già... Mi sono reso conto solo dopo averla postata che, se la mia ipotesi
fosse stata giusta, anche la variabile o3 sarebbe stata stampata come 255,
non come -1.
Mi sa che l'unica è l'esorcista...


 #define UCHAR_MAX 255
Cioè alla fine sei d'accordo con me o no?
Secondo me la libreria, al suo interno, provvede a restituire UN byte 
trattandolo come signed char (e quindi assegnandogli il valore -1 che 
equivale a 1111 1111).
All'esterno, poi, c'è il compilatore per ambiente embedded, che vede i 
char come valori signed a due byte, e che magari non ha l'accortezza di 
controllare che il risultato restituito dalla funzione possa essere 
'signed': magari per lui è semplicemente unsigned (da 0 a 255).
Ulteriore tentativo: dichiara (col tuo compilatore) una variabile di 
tipo char: se le assegni -255 che succede?



Ciao Tommy,
secondo me state cercando il motivo nella direzione sbagliata. Io credo che
la vera ragione sia da ricercare nella chiamata alla printf. Questa, essendo
una funzione a parametri variabili non da al compilatore nessuna possibilità
di valutare la congruenza dei parametri passati, altrimenti questo avrebbe
potuto avvisarti del fatto che nella stringa di rappresentazione tu hai
dichiarato alla printf con %d che gli avresti passato un int ed invece gli
hai passato un char. Così facendo, hai permesso all'ottimizzatore di,
passami l'affermazione, interpretare le tue intenzioni quando ha pushato il
dato nello stack prima della chiamata printf. In realtà, nei 3 char vi è lo
stesso valore ma l'istruzione push lavora, come minimo, a word. Per quel che
riguarda il warning, quello è corretto; usando 0x tu hai definito un int e
lo hai assegnato ad un char. Questo dovrebbe succedere anche per gli altri
tipi di numerazione (decimale, ottale). Per quello che ne so, quando lo
studiavo era così, l'unico modo per inizializzare un char è quello di
metterlo tra apici singoli ('\xff', '\033', 'a', ecc.). Un saluto.

 
Un char lo puoi inizializzare in mille modi diversi (anzi no, solo in 
256 se e` di 8 bits). L'uso degli apici serve solo per indicare al 
compilatore che richiedi la conversione da ASCII ad intero.
char ch = '\xff'; e char ch = 0xff; sono equivalenti
come anche char ch = '\033'; o char ch = 033; o
char ch = '\1b'; o char ch = 0x1b; o char ch = 27;
sono perfettamente equivalenti.


 hai passato un char.
non credo sia questo il problema, vengono convertiti in automatico in ints
ed il 0xff (-1) dovrebbe venire sign-extended in 0xffffffff e valere
sempre -1, invece viene portato a 0x000000ff e vale solo +255, why?
Inoltre i problema non e' solo la printf, anche il normale assegnamento di
tale numero ad un int, etc... La printf l'ho solo usata per mostare
l'errore, l'errore non nasce e non e' legato alla printf.
Tommy

Ciao Giovanni/Tommy,
scusatemi se insisto ma a mio avviso se viene passato un parametro ad una
funzione a parametri variabili e questo fa parte dei parametri non noti:
int f(const char *, ...);
f("ciao", a);
sulla variabile a, il compilatore non č in grado di effettuare nessun tipo
di conversione per il semplice motivo che non ha informazioni derivanti dal
prototipo della funzione. Se Tommy, nel suo esempio, avesse scritto %lf al
posto di %d, l'unico a lamentarsi sarebbe stato lui.

 posto di %d, l'unico a lamentarsi sarebbe stato lui.
quando passi un char ad una funzione a "parametri variabili" esso viene
convertito automaticamente in int.
dopo di che il %d nella stringa dice all printf di cercare un int (4 bytes
ad esempio) e trattarlo come signed int. (%u direbbe di trattarlo come
unsigned)
Ad ogni modo se fosse come dici tu anche le altre variabili o2 ed o3
avrebbero dovuto dare problemi.
Inoltre come ti ho detto il problema non si limita alla printf, la printf
l'ho solo usata per mostrare il problema che comunque esisteva anche senza
la printf.
Tommy

 
Su questo non sono proprio d'accordo.
se:
signed char ch1 = 0xff;
unsigned char ch2 = 0xff;
la chiamata a:
printf("%d %d \n", ch1, ch2);
stampa:
-1 255
Come mai?
Tu dici al compilatore che conversioni fare e lui esegue.
Poi ci sono compilatori che verificano se hai passato un numero 
sufficiente di valori per il numero di specifiche di conversione 
contenute nel formato e se questi valori sono compatibili con la 
specifica, ma questa e` un'altra storia.


 around" da -1 a 255 avviene prima.
;-)
Questo lo so, ma quando il compilatore si comporta in modo irrazionale provi
cose irrazionali per vedere se cambiano il comportamento per qualche strano
motivo.