Pregunta

Esta pregunta ya tiene respuesta aquí:

Notas de C++:Inicialización de matriz 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 así, solo el primer valor lo es y el resto son 0 mezclados con valores aleatorios.

El código

int array[100] = {0};

funciona bien y establece cada elemento en 0.

Que me estoy perdiendo aqui..¿No se puede inicializarlo si el valor no es cero?

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

¿Fue útil?

Solución

Usando la sintaxis que usaste,

int array[100] = {-1};

dice "establecer el primer elemento en -1 y el resto a 0"ya que todos los elementos omitidos están configurados en 0.

En C++, para configurarlos todos en -1, puedes usar algo como std::fill_n (de <algorithm>):

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

En el portátil C, tienes que crear tu propio bucle.Hay extensiones del compilador o puede depender del comportamiento definido por la implementación como acceso directo si eso es aceptable.

Otros consejos

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

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

Esto establecería todos los elementos a -1.

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

Tenga en cuenta que esto no se aplica para el compilador GCC C ++.

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

  

Si un tamaño de la matriz es explícita   especificada, sino una más corta   Lista initiliazation se especifica, el   elementos no especificados se establecen en cero.

No hay manera integrada para inicializar toda la matriz a un valor distinto de cero.

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

int array[100] = {0};

simplemente le dice al compilador "set estos 100 enteros a cero", que el compilador puede optimizar libremente.

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

es mucho más específico. Se le dice al compilador para crear una variable de iteración i, se dice que el Para en el que los elementos deben ser inicializado, y así sucesivamente. Por supuesto, el compilador es probable que optimizar esa distancia, pero el punto es que aquí está el problema overspecifying, obligando al compilador que trabajar más para llegar al mismo resultado.

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

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

Una vez más, se podría hacer lo mismo con una matriz, pero esto es más concisa, y le da el compilador más libertad. Sólo estás diciendo que desea toda la matriz llena con el valor 42. Usted no dice nada sobre el orden en que se debe hacer, o cualquier otra cosa.

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

std::array<int, 100> a;
a.fill(-1);

Con {} asignar los elementos que estén declaradas; el resto se inicializa con 0.

Si no hay = {} a inicializar, el contenido no está definido.

La página se enlazó estados

  

Si se especifica un tamaño de la matriz explícita, pero se especifica una lista initiliazation más corta, los elementos no especificados se establecen en cero.

tema de la velocidad: Las diferencias serían insignificantes para las matrices de este pequeño. Si se trabaja con grandes conjuntos y la velocidad es mucho más importante que el tamaño, se puede tener una matriz const de los valores por defecto (inicializado en tiempo de compilación) y luego memcpy a la matriz modificable.

Otro modo de inicialización de 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 )

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 permitir la sustitución de macros en los parámetros de DUP

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

Hay un ejemplo aquí .

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

Nuestra interfaz tiene un tamaño de 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 lo realiza una de las dos primeras funciones.

La primera sobrecarga es bastante sencilla:Se construye un std::array de tamaño 0.No es necesario copiarlo, simplemente lo construimos.

La segunda sobrecarga es un poco más complicada.Reenvía el 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 es 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 el primer tamaño - 1 argumentos copiando el valor que pasamos.Aquí, utilizamos nuestros índices de paquetes de parámetros variados solo 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 los convertimos en 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 asegurar que la expansión del paquete variado ... 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 la inicialización de nuestra matriz, pero en realidad, estamos evaluando el índice, convirtiéndolo en nulo, ignorando ese resultado nulo y luego devolviendo el valor, que se copia en la matriz. .

El argumento final, el que llamamos std::forward adelante, es una optimización menor.Si alguien pasa un std::string 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 garantiza que hagamos esto.

El código completo, incluidos 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.");
}

1) Cuando se utiliza un inicializador, para una estructura o una matriz de esa manera, los valores no especificados son esencialmente predeterminado construidos. En el caso de un tipo primitivo como enteros, eso significa que se pondrá a cero. Tenga en cuenta que esto se aplica de forma recursiva:. Podría tener una serie de estructuras que contienen matrices y si especifica sólo el primer campo de la primera estructura, entonces todo el resto será inicializado con ceros y constructores por defecto

2) El compilador probablemente generará código de inicialización que es al menos tan buena como la que podría hacer a mano. Yo tiendo a preferir dejar que el compilador de hacer la inicialización para mí, cuando sea posible.

En C ++, también es posible utilizar la programación meta y las plantillas variadic. El siguiente post muestra cómo hacerlo: crear mediante programación en matrices estáticas tiempo de compilación en C ++ .

En el C ++ V4 lenguaje de programación, Stroustrup recomienda el uso de vectores o valarrays sobre arrays incorporado. Con la valarrary, cuando se crea ellos, puede init a un valor específico como:

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

Para inicializar una matriz 7 miembros de largo con "7777777".

Esta es una forma de C ++ de la aplicación de la respuesta por medio de una estructura de datos C ++ en lugar de un "viejo y simple C" matriz.

Me cambié a usar el valarray como un intento en mi código para tratar de utilizar C ++ 'ismos v. C'isms ....

En caso de ser una característica estándar, pero por alguna razón no está incluido en el estándar de C ++ o C ...

#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 que podría 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é no en C / C ++ acaba de ponerlo en práctica. ¿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 era un conjunto de 1.000,00 bytes? Que había necesidad de escribir un guión para escribir para mí, o recurrir a hacks con el montaje / etc. Esto no tiene sentido.

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

Sólo entrar ilegalmente en él como en:

#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 entrar ilegalmente en él es a través de pre-procesamiento ... (Código de abajo no cubre los casos límite, pero está escrito para demostrar rápidamente lo que se podí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);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top