Tengo este código de muestra:
struct { char a[2]; char b[2]; } buff; char buffer1[5] = "ABCD";
Para copiar el buffer1
a los miembros de la estructura, hago lo siguiente:
char c[3],d[3]; memcpy(&buff,buffer1,4); sprintf(c,"%2.2s",buff.a); sprintf(d,"%2.2s",buff.b); printf("c=%s,d=%s",c,d);
Cuando imprimo las variables c y d, obtengo los valores en la variable c y d correctamente como: c="AB"
y c="CD"
.
Bueno, mi pregunta es, aunque estoy obteniendo el resultado correctamente, ¿ memcpy
afectará algo relacionado con la terminación de caracteres nulos o tendrá otras consecuencias inesperadas relacionadas con la memoria?
Hay dos problemas aquí:
1) Como se mencionó en los comentarios, es probable que olvide incluir un espacio para el carácter terminador '\0'
(es decir, NUL
en ASCII). El especificador de formato %s
para la función printf
espera que la cadena de caracteres tenga un formato válido. Sin embargo, nada le impide imprimir una secuencia de caracteres, como aquí:
#include <stdio.h> #include <string.h> struct { char a[2]; char b[2]; } buff; char buffer1[5] = "ABCD"; int main(void) { memcpy(&buff, buffer1, 4); printf("First member: %c%c\n", buff.a[0], buff.a[1]); printf("Second member: %c%c\n", buff.b[0], buff.b[1]); return 0; }
2) Un problema más grave es que el compilador puede incluir un relleno arbitrario entre los miembros de la estructura (así como después del último miembro), por lo que es posible que memcpy
no funcione como se esperaba. En lugar de copiar así (ya que puede poner bytes de la matriz en "todos" no utilizados). Sugeriría copias individuales de cada miembro o tal vez usar la macro offsetof()
.
De N1570 6.7.2.1/15
estructura y unión :
Puede haber relleno sin nombre dentro de un objeto de estructura, pero no al principio.
y 6.7.2.1/17
:
Puede haber relleno sin nombre al final de una estructura o unión.
Por lo tanto, debe dividir su memcpy
en dos llamadas, por ejemplo:
memcpy(&buff.a, buffer1, 2); /* or replace 2 with sizeof buff.a */ memcpy(&buff.b, buffer1+2, 2);
buffer1
termina en nulo. Sin embargo, los miembros de datos de buff
no lo son, ya que no se les agrega un terminador nulo. ¡Además, no tienes espacio para el terminador nulo!
Entonces, no, no lo afectará, ya que no está allí. Obtendrá resultados inesperados cuando intente utilizar las funciones de stdio.h
, que esperan una cadena terminada en nulo.
Además, es posible que desee consultar mi ejemplo aquí , sobre el bucle en una estructura, que de alguna manera es relevante. Le sugiero que copie a un miembro de datos a la vez y no de una sola vez.
Eso podría no funcionar como se esperaba. Puede haber bytes de relleno entre los campos de una estructura. Estos se insertan entre los campos para que el siguiente campo comience en una dirección determinada. Este es principalmente el tamaño de palabra de la CPU o el tamaño del bus, o un múltiplo del mismo para la alineación de caché.
Esto puede ser cierto incluso para char []
, ya que eso permitiría mover/copiar eficientemente dichas matrices mediante transferencias de palabras.
Para otros tipos, el relleno es muy probable y, además, debe preocuparse por la comodidad.
Recuerda también que memcpy
& friends hacen exactamente lo que sus nombres implican: operan en un bloque de memoria. No les importan las cadenas, no tienen idea de "cadenas". Insertar cualquier byte NUL los haría inútiles, por lo que no lo hacen.
Tenga en cuenta que este enfoque es similar a la conversión de tipos de punteros incompatibles. Hay pocos lugares en los que estás a salvo, pero la mayoría de las veces este es el camino al infierno (también conocido como comportamiento indefinido ).