Inicialización de una matriz normal con un valor predeterminado [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Notas de C++: La inicialización de matrices tiene una buena lista sobre la inicialización de matrices. Tengo un

int array[100] = {-1};

Esperando que esté lleno con -1 pero no es, solo el primer valor es y el resto son 0 mezclados con aleatorio valor.

El código

int array[100] = {0};

Funciona muy bien y establece cada elemento en 0.

Qué me estoy perdiendo aquí.. No se puede inicializar si el valor no es cero ?

2: ¿La inicialización predeterminada (como la anterior ) es más rápida que el bucle habitual a través de toda la matriz y asigna un valor o hace lo mismo?

Author: jww, 2009-07-01

13 answers

Usando la sintaxis que usaste,

int array[100] = {-1};

Dice: "establecer el primer elemento a -1 y el resto a 0" ya que todos los elementos omitidos se establecen a 0.

En C++, para establecer todos a -1, puede usar algo como std::fill_n (desde <algorithm>):

std::fill_n(array, 100, -1);

En portable C, tienes que rodar tu propio bucle. Hay extensiones de compilador o puede depender del comportamiento definido por la implementación como un acceso directo si es aceptable.

 296
Author: Evan Teran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-08 23:21:12

Hay una extensión para el compilador gcc que permite la sintaxis:

int array[100] = { [0 ... 99] = -1 };

Esto establecería todos los elementos en -1.

Esto se conoce como "Inicializadores designados" ver aquí para más información.

Tenga en cuenta que esto no está implementado para el compilador de c++ de gcc.

 124
Author: Callum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-30 20:22:06

La página a la que enlazaste ya dio la respuesta a la primera parte:

Si un tamaño de matriz explícito es especificado, pero más corto se especifica la lista de inicialización, los elementos no especificados se ponen a cero.

No hay una forma integrada de inicializar toda la matriz a un valor distinto de cero.

En cuanto a cuál es más rápido, se aplica la regla habitual: "El método que da al compilador la mayor libertad es probablemente más rápido".

int array[100] = {0};

Simplemente le dice a la compilador "establecer estos 100 ints a cero", que el compilador puede optimizar libremente.

for (int i = 0; i < 100; ++i){
  array[i] = 0;
}

Es mucho más específico. Le dice al compilador que cree una variable de iteración i, le dice el orden en el que los elementos deben inicializarse, y así sucesivamente. Por supuesto, es probable que el compilador optimice eso, pero el punto es que aquí está sobreespecificando el problema, forzando al compilador a trabajar más duro para llegar al mismo resultado.

Finalmente, si desea establecer la array a un valor distinto de cero, debe (en C++, al menos) usar std::fill:

std::fill(array, array+100, 42); // sets every value in the array to 42

De nuevo, podría hacer lo mismo con un array, pero esto es más conciso, y le da al compilador más libertad. Solo estás diciendo que quieres que toda la matriz llena con el valor 42. No dices nada sobre en qué orden debe hacerse, o cualquier otra cosa.

 29
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-30 20:18:27

C++11 tiene otra opción (imperfecta):

std::array<int, 100> a;
a.fill(-1);
 9
Author: Timmmm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-11 09:20:27

Con {} se asignan los elementos tal como se declaran; el resto se inicializa con 0.

Si no hay = {} para inicializar, el contenido es indefinido.

 8
Author: 0x6adb015,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-30 20:13:35

La página a la que ha vinculado indica

Si se especifica un tamaño de matriz explícito, pero se especifica una lista de inicialización más corta, los elementos no especificados se ponen a cero.

Problema de velocidad: Cualquier diferencia sería insignificante para matrices tan pequeñas. Si trabaja con matrices grandes y la velocidad es mucho más importante que el tamaño, puede tener una matriz const de los valores predeterminados (inicializados en tiempo de compilación) y luego memcpy a la matriz modificable.

 8
Author: laalto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-30 20:14:46

Otra forma de inicializar la matriz a un valor común, sería generar realmente la lista de elementos en una serie de define:

#define DUP1( X ) ( X )
#define DUP2( X ) DUP1( X ), ( X )
#define DUP3( X ) DUP2( X ), ( X )
#define DUP4( X ) DUP3( X ), ( X )
#define DUP5( X ) DUP4( X ), ( X )
.
.
#define DUP100( X ) DUP99( X ), ( X )

#define DUPx( X, N ) DUP##N( X )
#define DUP( X, N ) DUPx( X, N )

La inicialización de una matriz a un valor común se puede hacer fácilmente:

#define LIST_MAX 6
static unsigned char List[ LIST_MAX ]= { DUP( 123, LIST_MAX ) };

Nota: DUPx introducido para habilitar la sustitución de macro en parámetros a DUP

 4
Author: Steen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-23 08:39:57

Usando std::array, podemos hacer esto de una manera bastante sencilla en C++14. Es posible hacerlo solo en C++11, pero un poco más complicado.

Nuestra interfaz es un tamaño en tiempo de compilación y un valor predeterminado.

template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
    return std::array<std::decay_t<T>, 0>{};
}

template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
    return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}


template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
    return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}

La tercera función es principalmente por conveniencia, por lo que el usuario no tiene que construir un std::integral_constant<std::size_t, size> ellos mismos, ya que es una construcción bastante prolija. El trabajo real es realizado por una de las dos primeras funciones.

La primera sobrecarga es bastante sencilla: Construye un std::array de tamaño 0. No es necesario copiar, solo lo construimos.

La segunda sobrecarga es un poco más complicada. Reenvía a lo largo del valor que obtuvo como fuente, y también construye una instancia de make_index_sequence y simplemente llama a alguna otra función de implementación. ¿Cómo se ve esa función?

namespace detail {

template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
    // Use the comma operator to expand the variadic pack
    // Move the last element in if possible. Order of evaluation is well-defined
    // for aggregate initialization, so there is no risk of copy-after-move
    return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}

}   // namespace detail

Esto construye los primeros argumentos de tamaño-1 copiando el valor que pasamos. Aquí, usamos nuestros índices de paquetes de parámetros variádicos como algo para expandir. Hay entradas de tamaño-1 en ese paquete (como especificamos en la construcción de make_index_sequence), y tienen valores de 0, 1, 2, 3, ... tamaño 2. Sin embargo, no nos importan los valores (por lo que lo lanzamos a void, para silenciar cualquier advertencia del compilador). La expansión del paquete de parámetros expande nuestro código a algo como esto (asumiendo tamaño == 4):

return std::array<std::decay_t<T>, 4>{ (static_cast<void>(0), value), (static_cast<void>(1), value), (static_cast<void>(2), value), std::forward<T>(value) };

Usamos esos paréntesis para asegurarnos de que la expansión de paquetes variádicos ... expande lo que queremos, y también para asegurarnos de que estamos usando el operador de coma. Sin los paréntesis, parecería que estamos pasando un montón de argumentos a nuestra inicialización de matriz, pero en realidad, estamos evaluando el índice, lanzándolo a void, ignorando ese resultado void, y luego devolviendo el valor, que se copia en la matriz.

El argumento final, el que llamamos std::forward on, es una optimización menor. Si alguien pasa una cadena std::temporal y dice "hacer una matriz de 5 de estos", nos gustaría tener 4 copias y 1 movimiento, en lugar de 5 copias. El std::forward asegura que hagamos esto.

El código completo, incluyendo encabezados y algunas pruebas unitarias:

#include <array>
#include <type_traits>
#include <utility>

namespace detail {

template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
    // Use the comma operator to expand the variadic pack
    // Move the last element in if possible. Order of evaluation is well-defined
    // for aggregate initialization, so there is no risk of copy-after-move
    return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}

}   // namespace detail

template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
    return std::array<std::decay_t<T>, 0>{};
}

template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
    return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}

template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
    return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}



struct non_copyable {
    constexpr non_copyable() = default;
    constexpr non_copyable(non_copyable const &) = delete;
    constexpr non_copyable(non_copyable &&) = default;
};

int main() {
    constexpr auto array_n = make_array_n<6>(5);
    static_assert(std::is_same<std::decay_t<decltype(array_n)>::value_type, int>::value, "Incorrect type from make_array_n.");
    static_assert(array_n.size() == 6, "Incorrect size from make_array_n.");
    static_assert(array_n[3] == 5, "Incorrect values from make_array_n.");

    constexpr auto array_non_copyable = make_array_n<1>(non_copyable{});
    static_assert(array_non_copyable.size() == 1, "Incorrect array size of 1 for move-only types.");

    constexpr auto array_empty = make_array_n<0>(2);
    static_assert(array_empty.empty(), "Incorrect array size for empty array.");

    constexpr auto array_non_copyable_empty = make_array_n<0>(non_copyable{});
    static_assert(array_non_copyable_empty.empty(), "Incorrect array size for empty array of move-only.");
}
 3
Author: David Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-03 04:53:05

1) Cuando se utiliza un inicializador, para una estructura o una matriz como esa, los valores no especificados se construyen esencialmente por defecto. En el caso de un tipo primitivo como ints, eso significa que serán puestos a cero. Tenga en cuenta que esto se aplica recursivamente: podría tener una matriz de estructuras que contengan matrices y si especifica solo el primer campo de la primera estructura, entonces todo el resto se inicializará con ceros y constructores predeterminados.

2) El compilador probablemente generará el inicializador código que es al menos tan bueno como se puede hacer a mano. Tiendo a preferir dejar que el compilador haga la inicialización por mí, cuando sea posible.

 1
Author: Boojum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-30 20:15:56

Para el caso de una matriz de elementos de un solo byte, puede usar memset para establecer todos los elementos en el mismo valor.

Hay un ejemplo aquí.

 1
Author: Steve Melnikoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-01 07:31:49

En C++, también es posible usar meta programación y plantillas variádicas. El siguiente post muestra cómo hacerlo: Crear programáticamente arrays estáticos en tiempo de compilación en C++.

 1
Author: ingomueller.net,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:24

En el lenguaje de programación C++ V4, Stroustrup recomienda usar vectores o valarrays sobre arrays integrados. Con valarrary, cuando los creas, puedes inicializarlos a un valor específico como:

valarray <int>seven7s=(7777777,7);

Para inicializar un array de 7 miembros largos con "7777777".

Esta es una forma de C++ de implementar la respuesta usando una estructura de datos de C++ en lugar de una matriz "C simple".

Cambié a usar el valarray como un intento en mi código para intentar usar C++'isms v.C'isms....

 0
Author: Astara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-14 01:21:50

Debería ser una característica estándar, pero por alguna razón no está incluida en C ni C++estándar...

#include <stdio.h>

 __asm__
 (
"    .global _arr;      "
"    .section .data;    "
"_arr: .fill 100, 1, 2; "
 );

extern char arr[];

int main() 
{
    int i;

    for(i = 0; i < 100; ++i) {
        printf("arr[%u] = %u.\n", i, arr[i]);
    }
}

En Fortran se puede hacer:

program main
    implicit none

    byte a(100)
    data a /100*2/
    integer i

    do i = 0, 100
        print *, a(i)
    end do
end

Pero no tiene números sin signo...

¿Por qué C/C++ no puede simplemente implementarlo? Es realmente tan difícil? Es tan tonto tener que escribir esto manualmente para lograr el mismo resultado...

#include <stdio.h>
#include <stdint.h>

/* did I count it correctly? I'm not quite sure. */
uint8_t arr = {
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
};    

int main() 
{
    int i;

    for(i = 0; i < 100; ++i) {
        printf("arr[%u] = %u.\n", i, arr[i]);
    }
}

¿Y si fuera una matriz de 1.000.00 bytes? Tendría que escribir un script para escribirlo por mí, o recurrir a hacks con assembly / etc. Esto es disparate.

Es perfectamente portátil, no hay razón para que no esté en el idioma.

Simplemente hackearlo como:

#include <stdio.h>
#include <stdint.h>

/* a byte array of 100 twos declared at compile time. */
uint8_t twos[] = {100:2};

int main()
{
    uint_fast32_t i;
    for (i = 0; i < 100; ++i) {
        printf("twos[%u] = %u.\n", i, twos[i]);
    }

    return 0;
}

Una forma de hackearlo es a través del preprocesamiento... (El código a continuación no cubre casos extremos, pero está escrito para demostrar rápidamente lo que se podría hacer.)

#!/usr/bin/perl
use warnings;
use strict;

open my $inf, "<main.c";
open my $ouf, ">out.c";

my @lines = <$inf>;

foreach my $line (@lines) {
    if ($line =~ m/({(\d+):(\d+)})/) {
        printf ("$1, $2, $3");        
        my $lnew = "{" . "$3, "x($2 - 1) . $3 . "}";
        $line =~ s/{(\d+:\d+)}/$lnew/;
        printf $ouf $line;
    } else {
        printf $ouf $line;
    }
}

close($ouf);
close($inf);
 -4
Author: Dmitry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-03 11:55:15