Pages: [1]   Go Down
Author Topic: Lio con tamaño variables char  (Read 1899 times)
0 Members and 1 Guest are viewing this topic.
Orduña- Bizkaia
Offline Offline
Sr. Member
****
Karma: 0
Posts: 317
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Buenas, con el tema de las memorias y demas y comprobando tamaños y cuanto ocupan las variables me ha surgido una duda, y espero  que me la podais aclarar.
char nombre[]="12345";
Serial.println (sizeof nombre);  //me dice 6, si tiene 5 digitos no deberia ser 4 en vez de 6????


byte test [sizeof nombre];
byte err = readEEPROM (0x50,4, test, sizeof test); //lo leo de la memoria eeprom

int f=atoi((char *) test); //convierto la cadena a integet sin problema y integer ahora contiene 12345

char str[0]; //declaro str como cadena y pongo 0 porque no me deja ponerla vacia

itoa (f, str, 10);  10 significa base decimal para convertir el integer a char, y ahora aqui mi duda si en un principio el char tenia un tamaño de 6 segun esto ahora tiene un valor de 0 su tamaño, como se como esto???? porque funcionar funciona.
Serial.println (str);

Logged

Euskadi
Offline Offline
God Member
*****
Karma: 16
Posts: 735
Arduinotarrak
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola,
en el lenguaje C la cadena char siempre tiene un carácter final, el carácter null, representado como '\0'. Por eso,  tu cadena char nombre[]="12345" ocupa 6 bytes, los cinco dígitos más el null. Y también por eso, para leer una cadena char se testea el null
while(nombre[n] != '\0')
   ...

En lo que declaras como char str[0] en realidad no puedes guardar nada, tan solo el null

Quote
me dice 6, si tiene 5 digitos no deberia ser 4 en vez de 6?
Una cosa es que el índice de las posiciones de la cadena sea 0,1,2,3... Y otra el tamaño, que se expresa de forma normal, empezando a contar desde 1.




  
« Last Edit: February 27, 2013, 12:27:33 am by curro92 » Logged

Málaga, Spain
Offline Offline
Edison Member
*
Karma: 41
Posts: 2182
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Realmente debería devolverte 2 (arquitectura de 8 bits, pero los punteros ocupan 16bits) porque es el tamaño de un puntero. Por alguna razón el gcc esta devolviendo el tamaño de lo que contiene.

Lo que se debería usar es strlen y como bien dicen un "string" tiene al final el '\0' por lo que la longitud sería 6.
Logged

   

Orduña- Bizkaia
Offline Offline
Sr. Member
****
Karma: 0
Posts: 317
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

vale str con sizeof me devuelve 0 mientras que con strlen me devulve 5, q si he leido bien cuenta hasta el \0 asique veo que no es 0 , jeje, ya me cuadra mas, lo del 4 se me fue quise poner 5 se q cuenta desde 1 pero asi mas claro agua, ahora una cuestion con punteros y referencias, estoy leyendo una memoria eeprom y lo estoy haciendo con ambas y realmente funciona pero quisiera saber si es la mejor manera.

Code:

//funcion de lectura, la escritura lo hago practicamente igual
void eeprom_read_page( int deviceaddress, unsigned int eeaddress,
  byte *buffer, int length )
  {
   Wire.beginTransmission(deviceaddress);
   Wire.write((int)(eeaddress >> 8));   // MSB
   Wire.write((int)(eeaddress & 0xFF)); // LSB
   Wire.endTransmission();
   Wire.requestFrom(deviceaddress,length);
   int c = 0;
   for ( c = 0; c < length; c++ )
   if (Wire.available()) buffer[c] = Wire.read();
  } 
char ji[] = "12345"; //el dato almacenado en la posicion 0 y sucesivas

//comienzo lectura
char pi3[0];
Serial.println (sizeof ji);
byte *Dato3 = (byte *) &pi3; //con esta linea me ahorro el convertir nada paso el byte *buffer a int o a char, pero no entiendo muy bien como funciona ni si ocupa demasiado o se puede mejorar.
eeprom_read_page(0x50,0,Dato3,sizeof ji);
delay(10);
Serial.println(pi3); //saco el valor por el serial monitor y va pero esto es un char o un *char

La verdad que tengo un cacao con los punteros y refencia de aupa.
Logged

Málaga, Spain
Offline Offline
Edison Member
*
Karma: 41
Posts: 2182
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Milagroso que funcione. Si puedo te comento algo.
Logged

   

Orduña- Bizkaia
Offline Offline
Sr. Member
****
Karma: 0
Posts: 317
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

jaja, si a mi tb me parece raro que funcione pero como funciona pues ale, pero como no lo entiendo del todo por eso he preguntado, me quedo a la espera.
Un saludo y gracias de antemano.
Logged

Palencia, Spain
Offline Offline
God Member
*****
Karma: 25
Posts: 568
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Espero no liar más el asunto, pero intentaré explicar un poco.
Efectivamente, es muy probable que el programa te funcione, pero cabría la posibilidad de que en un momento determinado te corrompa alguna variable de tu programa.
En c, cualquier puntero ocupa lo mismo dependiendo de la arquitectura de la memoria. En arduino, creo que dos bytes, lo que vendría a ser un unsigned int conteniendo una dirección de memoria. El declarar un tipo de variable para el puntero, es para que el compilador nos avise si mezclamos churras con merinas, y para las funciones de incremento/decremento (si incrementamos un puntero a char, aumentará la dirección que contiene en 1, si es un puntero a int, en 2...). Ahora viene la pregunta interesante: ¿A qué dirección apunta el puntero?. Pues potencialmente a cualquier punto de la memoria que le indiquemos, de ahí su peligro. Si lo declaramos, sencillamente, apunta a 0000h o, lo que es lo mismo, es un puntero nulo.
Cuando inicializamos un puntero o declaramos un array, la variable realmente contiene un puntero al tipo de datos del array; pero además ese puntero señala a una posición de memoria en la que hemos reservado un determinado espacio o incluso hemos escrito un contenido.
En el caso de la declaración un tanto rara que has hecho (char pi3[0]) supongo que pi3 apunta a una dirección de memoria no nula, pero reserva 0 bytes en esa zona, con lo que si se almacena algo "a continuación", cuando escribes usando un puntero a esa zona, cabe la posibilidad de que estés machacando esa potencial variable. Una solución sería declarar un tamaño de seguridad de buffer, suficiente para lo que vayas a escribir en él (char pi3[20]), aunque si no vas a usar para nada el tipo char, no veo porqué no puedes declarar directamente byte dato3[20] y ahorrarte esa conversión de char* a byte* (dato3 es un byte * pero además apunta a una zona de 20 bytes reservados).
Lo corto aquí, porque como dije al principio, no sé si estoy aclarando o liando más. Saludos.
« Last Edit: February 27, 2013, 06:11:07 pm by noter » Logged

Orduña- Bizkaia
Offline Offline
Sr. Member
****
Karma: 0
Posts: 317
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

No no, lo estas dejando bastante claro, veo logico lo de char pi3[0] que apunta a una zona pero sin un bufer, y si a continuacion iria otra variable se corromperian, por ese lado perfecto poner pi3[20].

Lo del char a byte es porque en un futuro pondré lcd que creo q trabaja con char, me queda confirmalo ya q lo tengo abandonado hace tiempo jeje, sino lo haria como bien dices con un byte Dato3[20] por ejemplo. Otra razon por la que lo hice asi es que si quiero que sea un int con byte *Dato3 = (byte *) &pi3; tambien me sirve para pasar de byte a int.

Ahora me queda una cosa por entender de byte *Dato3 = (byte *) &pi3;
Aqui se pasan las direciones de memoria al puntero que apunta al contenido de la memoria no??? trabajando con char o byte me puedo ahorrar esa linea tal que asi y funciona
char pi3[20];
eeprom_read_page(0x50,0,(byte*)pi3,sizeof ji);

Sin embargo con un int tengo que ponerla ya q sino no me va,quedando asi la cosa
int pi2;
byte *Dato2 = (byte *) &pi2;
eeprom_read_page(0x50,26,Dato2,2);



Logged

Palencia, Spain
Offline Offline
God Member
*****
Karma: 25
Posts: 568
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Efectivamente, veo que lo entiendes y me alegro de no haberte liado.
Si vas a acceder a esas posiciones de memoria para escribir o leer un byte, un char o un int, lo lógico es que realices la asignación de dicha dirección a un puntero del tipo adecuado (en algunos compiladores más permisivos te permitiría usar el mismo puntero, dándote un warning, pero eso es un arma de doble filo). Por otro lado, el hecho de que condenses el código de conversión en una sola línea, no significa que el código resultante vaya a ser necesariamente de menor tamaño o más rápido, pues el compilador internamente va a tener que crear una variable temporal, así que por legibilidad del código, quizás sea mejor declarar el puntero a byte (al ser local, también es una variable temporal) y asignarle la dirección del char (byte dato3*=(byte*) pi3), aunque tu forma condensada es correcta.
En la instrucción byte *Dato3 = (byte *) &pi3;, te sobra el operador de indirección (&), ya que pi3 es ya literalmente un puntero a char que apunta a esos 20 bytes reservados. Puedes hacer la prueba y verás que la instrucción char* copiacadena=pi3 no te da ningún error. Lo correcto sería, entonces, byte *Dato3 = (byte *) pi3;.
RECUERDA: un array es un puntero asignado a ese array. Lo que pasa es que si utilizas un puntero seguido del índice de elemento, te devuelve el valor de ese elemento, es decir pi3[0] es lo mismo que decir *pi3. Por ejemplo, en el caso que te proponía char* copiacadena=pi3, podría obtener el caracter en la 3ª posición de la cadena, tanto usando copiacadena[2] como pi3[2] y rizando el rizo, *(copiacadena + 2) o *(pi3 + 2).
... Y vuelvo a cortar, que estoy yéndome más allá de lo que preguntabas e igual te lío.

EDITO: sólo para decirte que con el int sí que tienes que usar la indirección & porque el int, al contrario que un array, no es un puntero. Sí lo sería si hubieras declarado int pi2[1], y entonces la asignación byte *Dato2 = (byte *) pi2 sería igualmente correcta.
Saludos
« Last Edit: February 28, 2013, 10:34:26 am by noter » Logged

Orduña- Bizkaia
Offline Offline
Sr. Member
****
Karma: 0
Posts: 317
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

En primer lugar gracias por la explicación, te explicas super claro, vamos que he salido de dudas y me alegro primero por entenderlo del todo y segundo por haberlo hecho bien desde el principio, la verdad que hacia meses que no tocaba arduino y sabia por donde sonaba la flauta pero no quien la tocaba jajaja. Pero expongo mis conclusiones por si lo he entendido bien, siento ser igual un poco pesado pero me gusta saber como funciona, aunque mi nivel no es muy alto por lo menos entender lo que hago.

-lo de omitir el & es la razon por la que me funciona la omision de esa linea ya que asigno la direccion directamente al char que como dices es un puntero a los 20 bytes que reservo.
-no sabia que se podia declarar el int de esa forma, pero lo he probado y el compilador me da este error: "array must be initialized with a brace-enclosed initializer", esto ya lo pregunto por curiosidad.
-Entiendo que con int se ha que poner & porque ocupa dos bytes, y a diferencia del char que en si es un puntero a esas direciones ,aqui con & le tienes que pasar al puntero esas dos direcciones que ocupa no???
Logged

Palencia, Spain
Offline Offline
God Member
*****
Karma: 25
Posts: 568
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

En primer lugar, encantado de ayudarte, ya que veo que realmente tienes interés en aprender. No sé muy bien por qué te da ese error que me dices, pero he probado brevemente el siguiente código (sólo compilar, ya que no tengo aquí mi mega) y me compila sin errores:

void setup(){
}

void loop(){
int pi2[1];
byte *Dato2 = (byte *)pi2;
}

Supongo que en tu caso has intentado asignar directamente valor en la declaración. Esto, se haría:
int pi2[1]={2354}; //// o bien
int pi2[]={2354};

Evidentemente, es un poco tontería declarar un array para un solo elemento, y es más lógico trabajar con un int y usar el operador & para obtener su puntero, pero te lo mostré así con fin meramente didáctico.

El meollo de la cuestión no es si se trata de byte o de int, sino de si declaras una variable, o un puntero o array. De hecho, si declaras, por ejemplo, char caracter='p', caracter no es un puntero, y si necesitaras pasar su dirección deberías usar &caracter; pero cuando declaras una cadena, directamente lo haces con un puntero a char (char *cadena="cadena") o un array de char (char cadena[]="cadena").
Como curiosidad te comento que para facilitar el tratamiento de cadenas, en C se usa un caracter especial (\0) para marcar el final, con lo que las funciones no necesitan recibir el parámetro de longitud de la cadena para saber dónde terminar de leer, así que si declaras, por ejemplo, char micadena[]="hola", el array contiene 5 caracteres porque le agrega el \0 al final. Nada te impediría declararlo así: char micadena[]={'h','o','l','a'} y el tamaño del array será de 4 caracteres, pero como intentes enviar micadena como parámetro a funciones de cadena el resultado puede ser imprevisible.
Logged

Orduña- Bizkaia
Offline Offline
Sr. Member
****
Karma: 0
Posts: 317
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Correcto el int con su valor entre llaves funciona bien, jeje esque no habia puesto las llaves de ahi el error.

Gracias noter, me queda mucho mas claro el tema de los punteros que lo tenia como un tema un poco ahi a medias, y no acababa de entender.

Si tengo alguna otra duda y no te importa ya te dire.

Muchas gracias a todos también.

Un saludo y ahora al siguiente nivel.
Logged

Palencia, Spain
Offline Offline
God Member
*****
Karma: 25
Posts: 568
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ya que estábamos charlando sobre arrays y punteros, sólo puntualizar el cuidado con el que hay que tratarlos. Justamente este viernes me he tirado toda una mañana depurando una aplicación que ando realizando. El caso es que tras muchos Serial.print en diferentes puntos, me di cuenta de que estaba continuamente reiniciándose. Al final resultó ser un error garrafal con un array que declaré como un buffer de pantalla, algo así como byte bufferpantalla[65], pero que tenía que inicializar fuera del objeto donde lo creé por tratarse de un miembro estático; así que en las inicializaciones lo hice a la ligera:
byte pantalla::bufferpantalla[65]="";
¡¡¡TOMA YA!!! Supongo que pensé (mejor dicho, no pensé smiley-red) algo así como "escribo una cadena vacía en el buffer y ya está: el buffer contendrá \0 y otros 64 bytes que me da igual lo que sean", pero ¿qué hice realmente? Cuando se asigna una cadena entre comillas, digamos que lo que el compilador hace es imprimir esa cadena en una zona de memoria y asignar la dirección de donde la ha escrito. Por lo tanto, por mucho que yo le diga que bufferpantalla tiene longitud de 65, lo que estoy asignando al puntero bufferpantalla es realmente el espacio reservado para una cadena vacía. Resultado: cuando posteriormente escribo en lo que yo creía que era un hueco de 65 bytes, lo escribía en un hueco de, supongo, 1 byte (el \0 de fin de cadena), machacando 64 bytes correspondientes a vete tú a saber qué. Aún así, me puedo dar por contento con que "sólo" me haya llevado unas 4 horas encontrar el bug, porque por fin tuve un momento de lucidez cuando rastreaba por esa zona de código. Supongo que esto me hará tener un poquito más de cuidado cuando juegue con punteros o arrays, pero he querido poner este post por si alguno "escarmienta en pellejo ajeno" y le puedo evitar un quebradero de cabeza.
Saludos.
« Last Edit: March 03, 2013, 12:21:13 pm by noter » Logged

Orduña- Bizkaia
Offline Offline
Sr. Member
****
Karma: 0
Posts: 317
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

xDD, a mi me paso una vez algo asi por haber sobrepasado la ram del arduino y se me corrompian los datos y hasta que me di cuenta ,que por aquel entonces era super novato pues me tire días, pero bueno de los fallos se aprende, al final supongo que la tendrias que dejar solo declarada bufferpantalla[65] y listo.
A veces hacemos lo facil dificil.
Logged

Palencia, Spain
Offline Offline
God Member
*****
Karma: 25
Posts: 568
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Efectivamente, así fue. Finalmente, y como el contenido inicial era indiferente lo dejé como pantalla::bufferpantalla[65];.  La confusión vino porque la declaré en la clase como static byte pantalla[65], y los miembros estáticos deben ser además inicializados aparte. Normalmente un char, byte, int, o incluso una estructura se inicializa con un igual a y su valor inicial. En este caso, como me daba igual el contenido inicial, y mis neuronas debían estar de vacaciones, tuve el desliz de asignarle una cadena vacía (si no me hubiera dado igual el contenido, habría asignado una cadena de 64 caracteres y no habría ocurrido esto). Después de la búsqueda del bug no veas cómo eché de menos uno de esos avisos de cast del compilador que en otras ocasiones tanto me fastidiaban smiley-cry.
Logged

Pages: [1]   Go Up
Jump to: