vectores resueltos programacion imprimir ejercicios ejemplos cadenas arreglos arreglo c++ c arrays

c++ - resueltos - vectores en c



¿Diferencia al inicializar y poner a cero una matriz en c/c++? (6)

Quizás char myarr[16]={0x00}; no es un buen ejemplo para empezar, ya que las inicializaciones explícitas e implícitas de los miembros utilizan ceros, lo que hace que sea más difícil explicar lo que está sucediendo en esa situación. Pensé que un ejemplo de la vida real, con valores distintos de cero podría ser más ilustrativo:

/** * Map of characters allowed in a URL * * !, /, (, ), *, -, ., 0-9, A-Z, _, a-z, ~ * * Allowed characters are set to non-zero (themselves, for easier tracking) */ static const char ALLOWED_IN_URL[256] = { /* 0 1 2 3 4 5 6 7 8 9*/ /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 30 */ 0, 0, 0, ''!'', 0, 0, 0, 0, 0, ''/''', /* 40 */ ''('', '')'', ''*'', 0, 0, ''-'', ''.'', 0, ''0'', ''1'', /* 50 */ ''2'', ''3'', ''4'', ''5'', ''6'', ''7'', ''8'', ''9'', 0, 0, /* 60 */ 0, 0, 0, 0, 0, ''A'', ''B'', ''C'', ''D'', ''E'', /* 70 */ ''F'', ''G'', ''H'', ''I'', ''J'', ''K'', ''L'', ''M'', ''N'', ''O'', /* 80 */ ''P'', ''Q'', ''R'', ''S'', ''T'', ''U'', ''V'', ''W'', ''X'', ''Y'', /* 90 */ ''Z'', 0, 0, 0, 0, ''_'', 0, ''a'', ''b'', ''c'', /* 100 */ ''d'', ''e'', ''f'', ''g'' , ''h'', ''i'', ''j'', ''k'', ''l'', ''m'', /* 110 */ ''n'', ''o'', ''p'', ''q'', ''r'', ''s'', ''t'', ''u'', ''v'', ''w'', /* 120 */ ''x'', ''y'', ''z'', 0, 0, 0, ''~'', };

Esta es una tabla de búsqueda que se puede usar cuando se codifica una cadena URL. Solo los caracteres permitidos en una URL se establecen en un valor distinto de cero. Un cero significa que el carácter no está permitido y debe tener codificación URL ( %xx ). Observe que la tabla termina abruptamente con una coma después del carácter tilde. Ninguno de los caracteres que siguen a la tilde está permitido, por lo que debe establecerse en cero. Pero en lugar de escribir muchos más ceros para llenar la tabla hasta 256 entradas, dejamos que el compilador inicialice implícitamente el resto de las entradas a cero.

En c (o tal vez c ++), ¿cuál es la diferencia entre

char myarr[16]={0x00};

y

char myarr[16]; memset(myarr, ''/0'', sizeof(myarr));

??

editar: pregunto esto porque en vc ++ 2005 el resultado es el mismo ...
editar más: y

char myarr[16]={0x00,}; ?
Tal vez pueda obtener una respuesta más completa y no ambigua, ya que algunas respuestas a continuación se refieren a este tipo de código, es decir. ponga la coma justo antes de cerrar llaves. También el resultado es el mismo en vc ++ 2005.


ISO / CEI 9899: TC3 6.7.8, párrafo 21:

Si hay menos inicializadores en una lista adjunta que no hay elementos o miembros de un agregado, o menos caracteres en un literal de cadena utilizado para inicializar una matriz de tamaño conocido que no hay elementos en la matriz, el resto del agregado inicializarse implícitamente de la misma manera que los objetos que tienen una duración de almacenamiento estática.

Las matrices con duración de almacenamiento estática se inicializan en 0 , por lo que la especificación C99 garantiza que los elementos de matriz no explícitamente inicializados también se establezcan en 0 .

En mi primera edición de esta publicación, lancé algunas tonterías sobre el uso de literales compuestos para asignar a una matriz después de la inicialización. Eso no funciona. Si realmente desea usar literales compuestos para establecer los valores de una matriz, debe hacer algo como esto:

#define count(ARRAY) (sizeof(ARRAY)/sizeof(*ARRAY)) int foo[16]; memcpy(foo, ((int [count(foo)]){ 1, 2, 3 }), sizeof(foo));

Con algunos macro magic y el operador __typeof__ no estándar, esto se puede acortar considerablemente:

#define set_array(ARRAY, ...) / memcpy(ARRAY, ((__typeof__(ARRAY)){ __VA_ARGS__ }), sizeof(ARRAY)) int foo[16]; set_array(foo, 1, 2, 3);


La diferencia importante es que el primer valor predeterminado inicializa la matriz de una manera específica del elemento: los punteros recibirán un valor de puntero nulo , que no necesita ser 0x00 (como en todos los bits cero), los booleanos serán falsos . Si el tipo de elemento es un tipo de clase que no es el llamado POD (tipo de datos antiguo simple), entonces solo puede hacer el primero, porque el segundo solo funciona para los casos más simples (donde no tiene virtual funciones , constructores definidos por el usuario, etc.). En contraste, la segunda forma de usar el memset establece todos los elementos de la matriz en todos los bits cero. Eso no es siempre eso lo que quieres. Si su matriz tiene punteros, por ejemplo, no se establecerán en null-punteros necesariamente.

El primero inicializará los elementos de la matriz, excepto el primero, que se establece en 0 explícitamente. Si la matriz es local y en la pila (es decir, no es estática), el compilador a menudo hace un memset para borrar la matriz. Si la matriz no es local o estática, la primera versión puede ser considerablemente más eficiente . El compilador ya puede poner los inicializadores, en tiempo de compilación, en el código ensamblador generado, por lo que no requiere código de tiempo de ejecución en absoluto. Alternativamente, la matriz puede disponerse en una sección que se saca a cero automáticamente (también para punteros, si tienen una representación de todos los bits cero) cuando el programa comienza de manera rápida (es decir, en la página).

El segundo hace un memset explícitamente sobre toda la matriz. La optimización de los compiladores generalmente reemplazará a un memset para regiones más pequeñas con código de máquina en línea que simplemente hace un bucle usando etiquetas y ramas.

Aquí está el código ensamblador generado para el primer caso. Mi gcc no está muy optimizado, así que recibimos una llamada real a memset (16 bytes en la parte superior de la pila siempre están asignados, incluso si no tenemos locales. $ N es un número de registro):

void f(void) { int a[16] = { 42 }; } sub $29, $29, 88 ; create stack-frame, 88 bytes stw $31, $29, 84 ; save return address add $4, $29, 16 ; 1st argument is destination, the array. add $5, $0, 0 ; 2nd argument is value to fill add $6, $0, 64 ; 3rd argument is size to fill: 4byte * 16 jal memset ; call memset add $2, $0, 42 ; set first element, a[0], to 42 stw $2, $29, 16 ; ldw $31, $29, 84 ; restore return address add $29, $29, 88 ; destroy stack-frame jr $31 ; return to caller

Los detalles sangrientos del Estándar C ++. El primer caso anterior inicializará por defecto los elementos restantes.

8.5 :

Inicializar a cero el almacenamiento de un objeto de tipo T significa:

  • si T es un tipo escalar, el almacenamiento se establece en el valor de 0 (cero) convertido a T ;
  • si T es un tipo de clase no-unión, el almacenamiento para cada miembro de datos no estático y cada subobjeto de clase base tiene cero inicialización;
  • si T es un tipo de unión, el almacenamiento para su primer miembro de datos se inicializa en cero;
  • si T es un tipo de matriz, el almacenamiento de cada elemento se inicializa en cero;
  • si T es un tipo de referencia, no se realiza ninguna inicialización.

Para inicializar por defecto un objeto de tipo T significa:

  • si T es un tipo de clase no POD, se llama al constructor predeterminado para T
  • si T es un tipo de matriz, cada elemento se inicializa por defecto;
  • de lo contrario, el almacenamiento para el objeto se inicializa en cero.

8.5.1 :

Si hay menos inicializadores en la lista que miembros en el agregado, entonces cada miembro no inicializado explícitamente se inicializará por defecto (8.5).


Dado el hecho difícil de disputar de que = { 0 } es infinitamente más legible que memset(..., ..., ... sizeof ...) , entonces lo siguiente desalentaría explícitamente el uso de memset :

En Visual Studio 2005, compilación para Windows Mobile, versión de lanzamiento completamente optimizada:

; DWORD a[10] = { 0 }; mov r3, #0 mov r2, #0x24 mov r1, #0 add r0, sp, #4 str r3, [sp] bl memset add r4, sp, #0 mov r5, #0xA ; DWORD b[10]; ; memset(b, 0, sizeof(b)); mov r2, #0x28 mov r1, #0 add r0, sp, #0x28 bl memset add r4, sp, #0x28 mov r5, #0xA

Prácticamente lo mismo.


Prácticamente son lo mismo. Se garantiza que la primera forma inicia todo el tipo a 0x00 (incluso espacio de relleno entre los elementos de la estructura, por ejemplo), y esto se define desde C90. Desafortunadamente, gcc da una advertencia para el primer formulario con la opción -Wldsing-field-initializers. Más detalles aquí:

http://www.pixelbeat.org/programming/gcc/auto_init.html


La definición de los valores iniciales en la declaración de la variable ocurre en un lugar diferente al de memset.

Para el primer caso, los ceros se definen de alguna forma en el binario como memoria de inicio cero (o distinta de cero dependiendo de en qué se inicializa), y usted espera que el cargador respete eso, ABSOLUTAMENTE no tiene nada que ver con los estándares del lenguaje C. El último, usar memset depende de la biblioteca C que también trabajarías. Tengo más fe en la biblioteca.

Hago un montón de código incrustado en el que se aprende a evitar el mal hábito de inicializar variables como parte de la declaración de variables y, en cambio, hacerlo dentro del código.

Para los sistemas operativos estándar, Linux, Windows, etc., el init durante la declaración de variables está bien, obtendrá un aumento de rendimiento imperceptible, pero si está ejecutando un sistema operativo, se encuentra en una plataforma lo suficientemente rápida como para no ver esa diferencia.

Dependiendo del tipo binario, el primer caso de init durante la declaración puede hacer que el binario sea más grande. Esto es extremadamente fácil de probar. Compila tu binario como se muestra arriba, luego cambia el tamaño de la matriz de [16] a [16000] y vuelve a compilar. Luego, compile sin = = 0x00} y compare los tres tamaños binarios.

Para la mayoría de los sistemas que la mayoría de los programadores verán, no existe una diferencia funcional. Recomiendo el memset como un hábito. A pesar de lo que los estándares dicen que muchos, si no la mayoría de los compiladores de C (de los cuales la mayoría de los programadores nunca verán en sus carreras), no les gustará esa inicialización porque la cantidad de elementos no coincide con el tamaño. La mayoría de los compiladores no se ajustan a los estándares, incluso si lo afirman. En su lugar, desarrolle buenos hábitos que eviten los atajos o casi cualquier cosa que debería funcionar para el estándar X pero es diferente del estándar anterior M. (evite cualquier compilador genio o trucos basados ​​en estándares).