Dudas sobre byte* var y byte& var

Hola buenos días.

Como es mi primer hilo, lo primero y sobretodo MUCHISIMAS GRACIAS!! por todos los post y comentarios que hay en este foro, me han ayudado un montón.

Lo segundo, pues en el titulo lo digo…por mas que busco (que puede que este haciendo mal la busqueda) no encuentro informacion sobre definir variables con las “marcas” de punteros.

A ver, yo declaro:

byte *puntero;///el puntero
byte aqueapunta;///la variable

puntero = &aqueapunta;///el puntero vale ahora lo que hay en la memoria fisica
*puntero++;///aumento el valor de lo que hay en la memoria fisica
puntero++;///el puntero apunta a la siguiente direccion de memoria fisica.

Ok, todo esto creo que lo tengo claro no?? o estoy equivocado en algo???

Ahora, mi duda/problema es que creo que me ahorraria mil lineas si consiguiese saber con certeza que cuando declaro:

void funcion(byte& variable){
   hago lo que acontezca con la variable;}

ese byte& tiene el valor de la memoria fisica de la variable que realmente declare al principio de todo el sketch…
y que diferencias habria si declaro byte* variable???

De antemano muchas gracias, y SURBYTE, no me mates si he escrito mal mi primer post :o …yo lo arreglo :smiley: :smiley: :smiley: :wink: :wink: :wink:

Hola

En este hilo del foro ArduTips tienes, entre otros, un extenso tutorial sobre punteros y sus usos.

Si pese a todo sigues teniendo dudas postealo y se te intentará ayudar.

Saludos

Por cierto, acabo de agregar al índice la última entrada, que es la que probablemente te interese, titulada ¡Qué jaleo con los punteros! ¿Conoces las referencias?. Ahí ves un ejemplo práctico del uso de referencias como parámetros, y se señala al código de una función equivalente realizada con puntero.

Vale, creo que he realizado mal mi pregunta:

Que diferencia hay que yo le ponga el operador al y no al nombre de la variable:

es exactamente lo mismo byte* puntero
que byte *puntero???

o con el operador & byte& variable == byte &variable???

Eso es en principio indiferente, y he visto seguidores de ambas escuelas. Creo que se puede poner indiferentemente de las tres formas:

     byte* puntero;
     byte *puntero;
     byte * puntero;

Yo, particularmente utilizo la segunda, pero adopta la que te parezca más clara.

Hyarmenya:
Que diferencia hay que yo le ponga el operador al y no al nombre de la variable:

es exactamente lo mismo byte* puntero
que byte *puntero???

o con el operador & byte& variable == byte &variable???

Como digo noter: ninguna.
El compilador lo que “ve”, es si hay algún “caracter especial” en medio de las dos palabras; de ahí que no importe donde se coloque, siempre y cuando sea en medio de. Además, no debería haber ambigüedad para el compilador por ese lado; se supone que no está permitido nombrar variables cuya inicial no sea una letra del alfabeto (excepto ‘_’, aunque se usa convencionalmente para indicar un atributo “oculto” de objeto; eso es tema de creación de librerías).

noter:
Creo que se puede poner indiferentemente de las tres formas:

     byte* puntero;

byte *puntero;
    byte * puntero;

Yo me voy por la primera por la siguiente razón:

long numero = 0;
Serial.readBytes((byte*)&numero, sizeof(long));
// Lee un long enviado por el puerto serie; lo recibe en "forma binaria".

La razón está en el “casting” (byte*); aquí el tipo de dato debe llevar el ‘*’ sí o sí; porque de lo contrario, o habrá error de compilación, o me coloca los bytes en otra ubicación de la memoria donde no debería.

Para el compilador de AVR, todo tipo de dato que lleve el * al final (puntero), por debajo siempre será el equivalente a int (2 bytes con signo).

La parte confusa es que aunque por debajo sea un int, si lo imprimes, obtienes el valor leído según dirección de memoria de ese puntero y el tipo de dato, no la dirección en sí. Ejemplo: byte* “por debajo” es una variable de 16 bits, si lo intento imprimir, obtengo un valor entre 0 y 255 (8 bits).

Por eso:

*puntero++;///aumento el valor de lo que hay en la memoria fisica
puntero++;///el puntero apunta a la siguiente direccion de memoria fisica.

Un detalle más que cabe destacar: cuando aumentas el valor del puntero, este lo hace en relación al tipo de dato que el puntero interpreta. Ejemplo:

byte* punteroByte = (byte*)&loQueSea;
int* punteroInt = (int*)&loQueSea;
long* punteroLong = (long*)&loQueSea;

punteroByte++; // Incrementa el valor del puntero en 1.
punteroInt++; // Incrementa el valor del puntero en 2.
punteroLong++; // Incrementa el valor del puntero en 4.

noter:
Por cierto, acabo de agregar al índice la última entrada, que es la que probablemente te interese, titulada ¡Qué jaleo con los punteros! ¿Conoces las referencias?. Ahí ves un ejemplo práctico del uso de referencias como parámetros, y se señala al código de una función equivalente realizada con puntero.

Vengo a darte las gracias por ese aporte en la documentación y darte un LIKE, karma o como quieran llamarlo. Me gustaría que estuviese fijo con la típica chincheta.

Lucario:
Iba a mencionar precisamente que a pesar de que yo utilizo la segunda, la primera podía ser más consecuente, ya que cuando el compilador te da errores de conversión te lo especifica de una forma similar:

        cannot convert 'int*' to 'char*' in assignment

Puede parecer más claro de esa forma (con el * junto al tipo en lugar de junto al nombre de variable). Sin embargo, eso mismo en otras circunstancias puede llevar a equívoco. Por ejemplo:

        int* a, b;

Pudiera interpretarse que b es también un puntero a entero, cuando realmente es un entero. Para declararlo como tal en la misma línea habría que poner:

        int* a, *b;

Por ello no encuentro una razón de peso a favor de una u otra opción. Cada uno debe trabajar con lo que se encuentre más cómodo. Yo pego el puntero al nombre de la variable sencillamente porque me encuentro más cómodo, porque es lo que he visto más a menudo.

En cuanto al casting, efectivamente los paréntesis deben englobar tipo y puntero, estén o no juntos:

        Serial.readBytes((byte *) &numero, sizeof(long));

Lo que recomiendo, incluso para mí, salvo que se tengan muy claras las precedencias, es el uso de paréntesis cuando mezclamos operadores (y el * de dirección lo es). Por ejemplo, el comando que mencionas:

        *puntero++;

No actúa como pones, incrementando el valor apuntado, sino que postincrementa el puntero tras devolver el valor que apuntaba en memoria. Uso típico es:

        char a =*puntero++; //devuelve el valor, e incrementa el puntero

Para incrementar la variable apuntada deberíamos poner:

        (*puntero)++;

También se me deslizó así (sin paréntesis) en un ejemplo de las ardutips, para colmo en el mismo párrafo y justo antes de hacer la misma advertencia que pongo aquí sobre los paréntesis. Ya lo corregí poniendo paréntesis en

(*pintA)++; // Si hubiéramos direccionado pintA a una variable entera a, equivaldría a a++;

Arduinito:
Vengo a darte las gracias por ese aporte en la documentación y darte un LIKE, karma o como quieran llamarlo. Me gustaría que estuviese fijo con la típica chincheta.

Gracias, Arduinito.
Es gratificante saber que un intento de comunicar algo que uno considera interesante ha llegado a buen puerto :wink:

noter:
Sin embargo, eso mismo en otras circunstancias puede llevar a equívoco. Por ejemplo:

        int* a, b;

Pudiera interpretarse que b es también un puntero a entero, cuando realmente es un entero. Para declararlo como tal en la misma línea habría que poner:

        int* a, *b;

Otro que me acaba de cubrir la cuota de “cada día se aprende algo nuevo”. De no saberlo, me hubiera quedado con la primera interpretación.

noter:
sino que postincrementa el puntero tras devolver el valor que apuntaba en memoria. Uso típico es:

        char a =*puntero++; //devuelve el valor, e incrementa el puntero

Ahora la duda:

*puntero++; // Esto incrementa el valor del puntero.
puntero++; // ¿Esto incrementa el valor del puntero?

Yo antes había hecho una función que volcaba la memoria RAM, pero no recuerdo si el puntero lo incrementaba de la primera o segunda manera; y sobretodo, cómo lo podía usar en una comparación estilo:

for (byte* puntero = 0; puntero < 2048, puntero++)

Argh, ni siquiera recuerdo si esto es válido (o al menos funcional) hacerlo… :confused:

Lucario448:
Otro que me acaba de cubrir la cuota de “cada día se aprende algo nuevo”. De no saberlo, me hubiera quedado con la primera interpretación.

Si te digo la verdad, sólo recordaba que antaño me hice la misma pregunta que tú, porque me parecía extraño que se utilizara más la forma tipo *puntero que tipo* puntero cuando parecía más lógica la segunda. He tenido que rebuscar para buscar el porqué no cambié mi notación “tradicional”, pero recordaba que habían quedado “en tablas”.

Lucario448:
Ahora la duda:

*puntero++; // Esto incrementa el valor del puntero.

puntero++; // ¿Esto incrementa el valor del puntero?

Ambas incrementan el valor del puntero. La primera es una “evolución” de la segunda que, además de postincrementar, devuelve el valor previo del puntero. Otra cosa es que no tomes el valor devuelto, pues la utilización normal es

     variable = *puntero++;

que equivaldría a

     variable=*puntero;
     puntero++;

Siendo esta última opción más clara. Por ello conviene recordar(me) que siendo el de dirección (*) un operador más, sometido por tanto a precedencias y demás, tener en cuenta lo que comentaba en uno de los primeros ArduTips (Condensar el código fuente no hará nuestro programa ni más pequeño ni más rápido); y dividir nuestras operaciones en pasos claros (esto no debería suponer ninguna merma en la eficiencia del código final); o al menos asegurar con paréntesis lo que queremos hacer si no estamos totalmente seguros del resultado, so pena de bug de los difíciles de encontrar.

Lucario448:
Yo antes había hecho una función que volcaba la memoria RAM, pero no recuerdo si el puntero lo incrementaba de la primera o segunda manera; y sobretodo, cómo lo podía usar en una comparación estilo:

for (byte* puntero = 0; puntero < 2048, puntero++)

Argh, ni siquiera recuerdo si esto es válido (o al menos funcional) hacerlo… :confused:

Lo que sí te puedo asegurar es que tal y como está el for te dará errores de conversión. Puedes salvarlos con cast:

for (byte* puntero = (byte*) 0; puntero < (byte*) 2048, puntero++)

Pero no soy optimista de que obtengas el resultado esperado. No sé si es posible (y si lo es, cómo) asignar directamente una posición de memoria física a un puntero. Habrá que realizar algunas pruebas.

:D:D:D perdon?? y yo que creía que empezaba a entender esto de los punteros:D:D:D

entonces si yo quiero incrementar lo que apunta == (*puntero)++;
y si lo que quiero es adjudicarle un valor a la dirección que apunta no está bien escrito: *puntero = 0x80; ni: puntero = 0x80;

tendria que escribirlo con algun parentesis: (*puntero) = 0x80; ? ? ?

noter:
… Pero no soy optimista de que obtengas el resultado esperado. No sé si es posible (y si lo es, cómo) asignar directamente una posición de memoria física a un puntero. Habrá que realizar algunas pruebas.

Hasta aquí las pruebas que he realizado:

int vint = 1234;   // Creamos la variable y le asignamos un valor
int *pint;    // Creamos un puntero

void setup()
{
  Serial.begin(9600);
  pint = &vint;    // Direccion de memoria de la variable <vint>
  Serial.print("La dirección de <vint> es: ");
  Serial.println((int)pint);

  pint = (int*)vint;   // Tomamos <vint> como puntero
  Serial.print("Tomamos <vint> como un puntero: ");
  Serial.println((int)pint);

  pint = (int*)5678;    // Asignamos directamente una posicion de memoria fisica al puntero <pint>
  Serial.print("Asignamos directamente una direccion de memoria a <pint>: ");
  Serial.println((int)pint);
  
  // Suponiendo que la direccion de <vint> sea 256 (cambiar por la que salga),...
//  pint = (int*)256;    // Asignamos directamente una posicion de memoria fisica al puntero <pint>
//  Serial.println((int)pint);
//  *pint = 1001;        // Cambiamos el contenido de la variable <vint>
//  Serial.println(vint);
}

El cast dá para todo…

Hyarmenya:
:D:D:D perdon?? y yo que creía que empezaba a entender esto de los punteros:D:D:D

entonces si yo quiero incrementar lo que apunta == (*puntero)++;
y si lo que quiero es adjudicarle un valor a la dirección que apunta no está bien escrito: *puntero = 0x80; ni: puntero = 0x80;

tendria que escribirlo con algun parentesis: (*puntero) = 0x80; ? ? ?

Incrementar lo que apunta un puntero: (*puntero)++;
Adjudicar la dirección, teóricamente sería: puntero = 0x80; peeeero el compilador te va a decir que quieres asignar un entero a un puntero. Deberás hacer un cast para decirle al compilador que realmente quieres hacer esa "aberración":
puntero = (int *) 0x80; //el (int *) es lo que indica que quieres convertir el entero 0x80 en un puntero a entero.

Alfaville. Efectivamente lo he probado y parece funcionar. Soy un hombre de poca fe :grin: :grin: :grin: :grin:

Un pequeño programa para corroborar la sutil diferencia entre *puntero++ y (*puntero)++:

void setup()
{
  char cadena[]="135";
  char *puntero = cadena;
  Serial.begin(9600);
  Serial.print("cadena inicialmente: ");
  Serial.println (cadena);
  Serial.print("Valor devuelto por *(puntero++) y *puntero++: ");
  Serial.println(*puntero++);
  Serial.print("cadena actualmente: ");
  Serial.println (cadena);
  Serial.print("puntero apunta actualmente a: ");
  Serial.println(*puntero);
  puntero=cadena;
  Serial.print("Valor devuelto por (*puntero)++: ");
  Serial.println((*puntero)++);
  Serial.print("cadena actualmente: ");
  Serial.println (cadena);
  Serial.print("puntero apunta actualmente a: ");
  Serial.println(*puntero);
}

void loop()
{
}

Y la salida que da:

cadena inicialmente: 135

Valor devuelto por *puntero++: [b]1[/b]
cadena actualmente: [b]1[/b]35
puntero apunta actualmente a: [b]3[/b]

Valor devuelto por *puntero++: [b]1[/b]
cadena actualmente: [b]2[/b]35
puntero apunta actualmente a: [b]2[/b]

Vemos que en ambos casos la salida de la instrucción es la misma (1). Después, en el primer caso vemos que se incrementó el puntero, y en el segundo se incrementó el 1 al que apuntaba el puntero (y sigue apuntando, pues ahora no se incrementó).

Vale :smiley: creo que vuelvo a errar en la pregunta…tambien porque no lo explico todo…

yo trabajo SIEMPRE que me es posible con el byte…asi que mi puntero y mi variable son del mismo tipo, ¿por que tengo que hacerle un cast a int? BUABUABUA me estoy haciendo la picha un lio con tanta informacion :smiley: menos mal que lo puedo leer 50mil veces pa entenderlo :smiley:
creo que estoy entendiendo algunas cosas mal…pero como me estaban funcionando :o …me parece que voy a imprimir el Ardutips pa subrayar y hacer apuntes :smiley:

y mi pregunta (bien realizada):

byte var = 7;
byte *puntero = &var;

(*puntero)++; ///ahora var = 8------ hasta aqui si que habia pillado…creo

AHORA…como puedo decirle que el valor de ‘var’ es 0x80 y no 0x08 que es lo que vale ahora???

puntero = 0x80???
*puntero = 0x80???
(*puntero) = 0x80???

Noter en el ultimo post casi mi lo aclaras pero es que una vez haces: char *puntero = cadena (sin & porque es un array supongo)
y luego puntero=cadena y te “funciona” igual…apunta las dos veces a la posicion ‘0’ de la cadena

PD. y yo que venia a enseñar mi “super funcion” que me habia hecho con dos punteros y ahora me da verguenza :smiley:

byte pantalla;
byte cursor;
byte *pos_cursor = &cursor;
byte *pos_cursor_pantalla_INTRO[5] = {&var, &var1, &var2, &var3, &var4};
byte *pos_cursor_pantalla_OTRA[5] = {&var5, &var6, &var7, &var8, &var9};

void botones (byte _pantalla){///ahora que estoy resumiendo mi sketch me pregunto si es totalmente innecesario que esto sea un puntero…
if(
_pantalla == INTRO){
if(le doy al boton incrementar){///es un byte ‘funcion’ (un map de A0)
(*pos_cursor_pantalla_INTRO[(*pos_cursor)])++;
}
}
}

void loop(){
botones(&pantalla);}

a mi esto ahora mismo me esta funcionando como queria no se si hay algo mal y seguro que es meorable…

MIL GRACIAS a los dos: noter y Alfaville…

Hyarmenya:
yo trabajo SIEMPRE que me es posible con el byte…asi que mi puntero y mi variable son del mismo tipo, ¿por que tengo que hacerle un cast a int?

No son del mismo tipo.
byte *puntero declara un puntero que apunta a objetos de tamaño byte
pero el tamaño de debe ser tal, que pueda direccionar todo el rango de memoria. Normalmente un int
El cast se utiliza para decirle al compilador que queremos “meter” un int en , ya que es su tipo.

Hyarmenya:
y mi pregunta (bien realizada):

byte var = 7;
byte *puntero = &var;

(*puntero)++; ///ahora var = 8------ hasta aqui si que habia pillado…creo

AHORA…como puedo decirle que el valor de ‘var’ es 0x80 y no 0x08 que es lo que vale ahora???

puntero = 0x80???
*puntero = 0x80???
(*puntero) = 0x80???

A no le interesa el valor de , solo su direccion ( contiene direcciones no datos), por lo tanto las asignaciones anteriores no guardan relacion con tu duda.
Para obtener la direccion de (ya explicado): puntero = &var
Para modificar el valor de desde puntero (ya explicado): *puntero = 0x80
Y si quieres que tenga el valor 0x80 por la via estandar, pues: var = 0x80

No sé si te he aclarado algo.

Saludos

Jejeje...
Como comentaba en las ArduTips sobre los punteros:
"Se trata de una materia "avanzada" del lenguaje y no es sencillo (hasta que lo entiendes y piensas que tampoco era para tanto)".
Entonces, aunque Alfaville se ha explicado correctamente, reforzaré su explicación, a ver si conseguimos que saltes esa pequeña piedra que aún tienes delante. Vamos por orden:

AHORA.....como puedo decirle que el valor de 'var' es 0x80 y no 0x08 que es lo que vale ahora???

puntero = 0x80???
*puntero = 0x80???
(*puntero) = 0x80???

Las respuestas correctas son la b y la c. La primera dará error de compilación. Si quieres ir sobre seguro, usa la tercera, que expresa claramente tu intención si la vas a usar con otros operadores. ¿Por qué?
Como dije, el * es un operador llamado de indirección. Como todo operador está sujeto a precedencias. La sentencia *puntero = 0x80 toma el puntero, a continuación aplica el operador de indirección * que "carga" la variable apuntada y le asigna el valor 0x80. Con paréntesis ocurre lo mismo. No hay problemas, pues es el único operador que se aplica.
Sin embargo, la sentencia *puntero++ no carga la variable y la incrementa, como podríamos esperar si leemos de izquierda a derecha; sino que primero hace el incremento y luego carga la variable apuntada. Es decir, sin paréntesis equivale a la sentencia *(puntero++);. La carga posterior de la variable es gratuita, pues no asignamos su valor a nada.
El uso habitual de la expresión es var=*puntero ++; o, si queremos mostrar mejor la operación, var=*(puntero++). Podría pensarse "entonces devolverá el entero situado en las siguientes posiciones de memoria, pues se ha hecho primero el incremento"... Pues no, porque aunque el operador se aplicó antes, es un operador de postincremento, cuya ejecución se guarda para el final (distinto caso sería * ++puntero). El resultado efectivo de var=*puntero++ serían dos instrucciones: var=*puntero; puntero++;
Mucho más claro así, ¿no? Más teniendo en cuenta que el poner las dos instrucciones separadas no supone ningún sobrecosto, sino que una vez compilado resulta exactamente el mismo código ensamblador que la otra opción.
Entiendo que es un poco rollo todo lo que te acabo de exponer, pero luego no es tan complicado como pueda parecer. Lo que no puedes es relajarte lo mínimo con su uso, porque operaciones aparentemente inocentes como esta te pueden dar quebraderos de cabeza. Mi recomendación siempre para éste y cualquier asunto de programación: divide y vencerás.

Noter en el ultimo post casi mi lo aclaras pero es que una vez haces: char *puntero = cadena (sin & porque es un array supongo)
y luego puntero=cadena y te "funciona" igual....apunta las dos veces a la posicion '0' de la cadena

No te líes. Ambas son la misma instrucción, sólo que la primera es una declaración y asignación a la vez. Equivaldría a char *puntero; puntero=cadena;. En este caso no sigo el axioma "divide y vencerás" porque aunque aquí sea válido separarlo en dos pasos, en una declaración de variable global, fuera del flujo de programa, sólo te admitiría la primera opción, y la asignación la tendrías que poner en el setup, por ejemplo.
Para una variable entera puedes declarar int var=10; pero luego no volverás a poner el (int) para asignarle otro valor. Para un puntero lo mismo con el (char *) cuando quieras referirte al puntero. Otra cosa es que quieras "extraer" su variable, para lo que usarás el * como operador, no como declarador de puntero. Pero al igual que var por sí sola es un (int), puntero por sí solo es un (char *) (puntero a carácter).
Si te fijas, aunque en la declaración el tipo no va con paréntesis, cuando nos referimos a una variable o valor existente mencionamos (o deberíamos) su tipo entre paréntesis. El compilador nos lo suele mostrar así en sus advertencias, y es como se utiliza cuando queremos no declarar, sino convertir a otro tipo una variable.

Muchísimas gracias…menuda enciclopedia que es este foro :grinning:

A ver Alfaville perdóname ‘hijo’ (ya sé que está explicado :sob: ) pero es que no me enteraba…
GRACIAS a ti ahora sé que un puntero es sí o sí de tamaño int (bueno o lo que necesite por memorias), pero byte* no puede apuntar a un int aunque ‘le quepa’.

Noter te juro que he leido mil veces que <i++> primero asigna y luego incrementa y que <++i> es al revés…entendedme que le aplicaba la misma lógica, ahora TAMBIÉN GRACIAS a ti se que no es lo mismo con los punteros.

Pero ahora me habeis creado otra duda:
el operador en *puntero++ es de indireccion y en var=puntero++ es de postincremento…
¿y como se sabe como actua el operador? ¿puede ser entonces que: __var=
++puntero;__ es puntero++; y luego var=*puntero;?

PD ¿como que no es patantoooo?, mira tengo la mesaaaa… el tabaco(lleno) es la variable, su memoria es el cuadraico del mantel, el puntero es el boli, con su cuadrico de mantel pero lo relleno con el tabaco(vacio) que es lo que contiene…

VAMOS para hacer un juego de mesa/rol…que conste que quiero derechos por la idea :grin: :grin: :grin:

Saludos amigo y mil gracias una vez mas

vale vale que atontao que estoy......lo del operador lo explicas muy bien en el punto cuatro de Ardutips :smiley: :smiley: