Newsgroup: it.comp.lang.c
Titolo: shifting di array

Ciao, stavo provando a fare uno shift tra elementi adiacenti di un 
array, ma qualcosa non funziona come dovrebbe. Mi aiutate?
/*prova.c*/
int main(){
   char ar[2]={1,0};
   unsigned short * pt;
   printf("%d %d\n",ar[0],ar[1]);
   pt=(unsigned short*)ar;
   (*pt)>>=8;
   printf("%d %d\n",ar[0],ar[1]);
   return 0;
}
Come e' intuibile voglio che lo shift porti il primo elemento dell'array 
sul secondo. Uso un puntatore a unsigned short perche' se facessi lo 
shift direttamente sull'elemento dell'array non avrebbe effetto perche' 
spingerei tutti i bit del byte "nel nulla". Il problema e' che nemmeno 
cosi' funziona, e il problema ancora piu' grosso e' che se inverto il 
verso dello shift funziona.
Allora ho convertito il codice in assembler cosi':
$gcc prova.c -S -o prova.s
Qui pero' non ci capisco molto, guardando dentro a quello che ho 
ottenuto ho visto che l'array sembra messo sullo stack nell'ordine in 
cui lo vedo normalmente (viene messo prima ar[0] e poi ar[1])
movb	$1, -2(%ebp)
movb	$0, -1(%ebp)
quindi dovrebbe apparire cosi' (giusto?):
       -2(%ebp)  -1(%ebp)
[...][00000001][00000000]
e quindi il mio puntatore dovrebbe puntare a -2(%ebp), (credo che 
l'istruzione relativa sia "leal -2(%ebp), %eax"), e se fino qui non ho 
fatto errori, shiftando di 8 bit quello che e' puntato
shrw	$8, %ax
movw	%ax, (%edx)
dovrei modificare lo stack e quindi il contenuto dell'array in questo modo:
       -2(%ebp)  -1(%ebp)
[...][00000000][00000001]
Invece non funziona... e peggio funziona al contrario. Probabilmente 
sbaglio tutto perche' non so quasi niente di assembler, comunque se 
potete aiutatemi. Grazie
-- 
Devaraja (Xdevaraja87^gmail^c0mX)
Linux Registerd User #338167
http://counter.li.org

Giovanni ha scritto:

Avevo pensato anche io che dipendesse dall'ordine dei byte, ma -2(%ebp) 
non e' gia' la posizione meno significativa? Voglio dire, lo stack e' 
gia' di sua natura crescente? E poi se stampo la variabile short vedo 
256 e questo conferma la mia idea, cioe' [00000001][00000000] ed e' per 
questo che mi sembra strano che lo shift a destra non inverta gli elementi.


DevarajA ha scritto:

No, avevo sbagliato qualcosa, mi stampa 1. Comunqe non capisco.. i byte 
sono gia' nell'ordine giusto.


DevarajA ha scritto:

Forse ho capito: proprio perche' il byte meno significativo e' nella 
posizione meno significativa (lo stesso modo in cui viene rappresentata 
la variabile short) e' come se leggessi uno short contenente 1. Altro 
dettaglio dimenticato: lo shift funziona sulla rappresentazione 
"teorica" del numero e non su quella reale, quindi 1 divena 
[00000000][00000001] e non [00000001][00000000], quindi lo shift va 
fatto verso sinistra. Giusto?


 [00000000][00000001] e non [00000001][00000000], 
Come dici tu, "lo shift è fatto sulla rappresentazione 'teorica'";
d'altronde quando shifti verso destra un 1 memorizzato in qualunque tipo
cosa ti aspetteresti ? diversi risultati a seconda della rappresentazione
prevista per quel tipo, o sempre e comunque uno 0 ?
Pensa sa shitando a destra un 1 otterresti un 128 a seconda se fosse uno
short o 0 se fosse un char :)) o 65536 se fosse un int :)
Ciao
Matic


Cosi` e` come funzionano le CPU x86.  Il byte piu` basso di un intero 
viene messo in memoria all'indizizzo piu` piu` basso.
Se:   char a[4] = { 1, 2, 3, 4, };  leggendo come intero da` 0x04030201


 potete aiutatemi. Grazie
Con tutte le opportune attenzioni (alcune delle quali ti sono gia` state
segnalate  - i.e.  endianess)  puoi provare  questo  codice (scritto  di
fretta in attesa che l'omino delle pizze si ricordi di casa mia ...):
#include <stdio.h>
#ifndef NITEMS
# define NITEMS         10
#endif
# define swap(x)        \
do \
{ \
  unsigned short *ptr = (unsigned short *)(x); \
  *ptr = (((*ptr & 0xFF) << 8) | (*ptr >> 8)); \
} \
while (0);
#define print_r(x)      \
do \
{ \
  int i; \
  \
  (void)fprintf (stdout, "%s[%d] = { ", #x, NITEMS); \
  \
  for (i = 0; i < NITEMS; i++) \
    (void)fprintf (stdout, "%d ", x[i]); \
  \
  (void)fprintf (stdout, "};\n"); \
  fflush (stdout); \
} \
while (0);
int main(void)
{
  char ar[NITEMS] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  int i;
 
  print_r(ar);
 
  for (i = 0; i < NITEMS - 1; i++)
  {
    swap (ar + i);
    print_r(ar);
  }
 
  return 0;
}
Ciao.
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 while (0);
A cosa serve while(0)? Non basta scrivere il codice senza cicli per 
farlo eseguire una volta sola?


 farlo eseguire una volta sola?
E` pratica corrente usare do { ... } while(0) nelle macro quando e`
necessario definire delle variabili temporanee.  Definite all'interno
del blocco possono essere ridefinite ogni volta che si usa la macro.
Inoltre puoi mettere il ';' dopo la macro per es: swap(a);


 farlo eseguire una volta sola?
E` fatto per  evitare problemi nell'uso degli "if"  (tralascia, per ora,
il discorso  della variabile  locale); se,  ad esempio,  avessimo quella
macro definita come segue:
[code]
#include <stdio.h>
#define swap(x) \
  unsigned short *ptr = (unsigned short *)(x); \
  *ptr = (((*ptr & 0xFF) << 8) | (*ptr >> 8))
int main(void)
{
  char ar[2] = { 1, 0 };
  if (1)
    swap (ar);
  else
    printf ("whoa!\n");
  return 0;
}
[/code]
Dandola in pasto al preprocessore otterremmo:
:~# cat test.c | grep -v include | gcc -E -P -
int main(void)
{
  char ar[2] = { 1, 0 };
  if (1)
    unsigned short *ptr = (unsigned short *)(ar); *ptr = (((*ptr & 0xFF)
    << 8) | (*ptr >> 8));
  else
    printf ("whoa!\n");
  return 0;
}
:~#
Che risulta palesemente  errata in quanto solamente  la prima istruzione
verrebbe interpretata come  conseguenza dell'if e quindi  errori vari in
fase di  compilazione. A questo punto  potresti pensare che il  tutto si
risolverebbe inserendo un blocco nella macro, ovvero definendola come:
[code]
# define swap(x) \
{ \
  unsigned short *ptr = (unsigned short *)(x); \
  *ptr = (((*ptr & 0xFF) << 8) | (*ptr >> 8)); \
}
[/code]
Che dandola il pasto al preprocessore verrebbe espansa in:
:~# cat test.c | grep -v include | gcc -E -P -
int main(void)
{
  char ar[2] = { 1, 0 };
  if (1)
    { unsigned  short *ptr =  (unsigned short  *)(ar); *ptr =  (((*ptr &
    0xFF) << 8) | (*ptr >> 8)); };
  else
    printf ("whoa!\n");
  return 0;
}
:~#
Nota  quel punto  e  virgola  dopo il  blocco  dell'if prima  dell'else;
ovviamente e` errato  generando un errore di sintassi  a compile time. A
questo punto direi che e` abbastanza chiara la necessita` di scrivere un
do { /* blah */ } while(0), infatti definendo la macro come:
[code]
# define swap(x) \
do \
{ \
  unsigned short *ptr = (unsigned short *)(x); \
  *ptr = (((*ptr & 0xFF) << 8) | (*ptr >> 8)); \
} \
while (0)
[/code]
si ottiene:
:~# cat test.c | grep -v include | gcc -E -P -
int main(void)
{
  char ar[2] = { 1, 0 };
  if (1)
    do { unsigned short *ptr =  (unsigned short *)(ar); *ptr = (((*ptr &
    0xFF) << 8) | (*ptr >> 8)); } while (0);
  else
    printf ("whoa!\n");
  return 0;
}
:~#
Che e` un  codice sicuramente (dal punto di  vista sintattico) corretto;
detto  cio` ritorniamo  al discorso  della variabile  locale tralasciato
all'inizio; come hai potuto capire il do  { /* blah */ } while(0) non si
utilizza  per questo  motivo  ma  per i  problemi  che possono  derivare
nell'uso della macro in presenza di  "if"; infatti se tu non utilizzassi
mai degli "if" nel tuo codice e ne scrivessi uno come :
[code]
#include <stdio.h>
# define swap(x) \
{ \
  unsigned short *ptr = (unsigned short *)(x); \
  *ptr = (((*ptr & 0xFF) << 8) | (*ptr >> 8)); \
}
int main(void)
{
  char ar[2] = { 1, 0 };
  swap (ar);
  swap (ar);
  swap (ar);
  swap (ar);
  return 0;
}
[/code]
Ovvero espanso in:
:~# cat test.c | grep -v include | gcc -E -P -
int main(void)
{
  char ar[2] = { 1, 0 };
  { unsigned short *ptr = (unsigned short *)(ar); *ptr = (((*ptr & 0xFF)
  << 8) | (*ptr >> 8)); };
  { unsigned short *ptr = (unsigned short *)(ar); *ptr = (((*ptr & 0xFF)
  << 8) | (*ptr >> 8)); };
  { unsigned short *ptr = (unsigned short *)(ar); *ptr = (((*ptr & 0xFF)
  << 8) | (*ptr >> 8)); };
  { unsigned short *ptr = (unsigned short *)(ar); *ptr = (((*ptr & 0xFF)
  << 8) | (*ptr >> 8)); };
  return 0;
}
:~#
Non ottieni nessun problema a  compile time in quanto ciascuna variabile
locale e` "protetta"  da un blocco (quindi non  visibile all'esterno) ed
un punto e virgola  alla fine di un blocco, in  questo caso, non provoca
alcun problema  di sorta  (infatti il  codice risulta  compilabile senza
errori).
