matriz - matrices en dev c++
Inicialización de una matriz normal con un valor predeterminado (13)
1) Cuando usa un inicializador, para una estructura o una matriz como esa, los valores no especificados son esencialmente construidos por defecto. En el caso de un tipo primitivo como ints, eso significa que se pondrán a cero. Tenga en cuenta que esto se aplica recursivamente: podría tener una matriz de estructuras que contienen matrices y si especifica solo el primer campo de la primera estructura, todo el resto se inicializará con ceros y constructores predeterminados.
2) El compilador probablemente generará un código de inicialización que sea al menos tan bueno como lo podría hacer a mano. Tiendo a preferir dejar que el compilador haga la inicialización por mí, cuando sea posible.
Esta pregunta ya tiene una respuesta aquí:
Notas de C ++: la inicialización de la matriz tiene una buena lista sobre la inicialización de las matrices. tengo un
int array[100] = {-1};
esperando que esté lleno con -1 pero no, solo el primer valor 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.
¿Qué me estoy perdiendo aquí? ¿No se puede inicializar si el valor no es cero?
2: ¿Es la inicialización predeterminada (como anteriormente) más rápida que el bucle habitual a través de toda la matriz y asigna un valor o hace lo mismo?
C ++ 11 tiene otra opción (imperfecta):
std::array<int, 100> a;
a.fill(-1);
Con {} asignas los elementos como están declarados; el resto se inicializa con 0.
Si no hay = {}
para inicializar, el contenido no está definido.
Debería ser una característica estándar, pero por alguna razón no está incluida en el estándar C ni en 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 podías 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 firmar ...
¿Por qué no puede C / C ++ 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 se tratara de una matriz de 1.000,00 bytes? Necesitaría escribir un script para escribirlo por mí, o recurrir a hacks con assembly / etc. Esto no tiene sentido.
Es perfectamente portátil, no hay razón para que no esté en el idioma.
Simplemente piratéelo 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 piratearlo es mediante el preprocesamiento ... (El código a continuación no cubre los casos perimetrales, pero está escrito para demostrar rápidamente lo que se puede 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);
En C ++, también es posible utilizar la programación meta y las plantillas variadas. La siguiente publicación muestra cómo hacerlo: crear mediante programación matrices estáticas en tiempo de compilación en C ++ .
En el lenguaje de programación V4 de C ++, Stroustrup recomienda usar vectores o valarrays sobre matrices integradas. Con valarrary''s, cuando los creas, puedes iniciarlos a un valor específico como:
valarray <int>seven7s=(7777777,7);
Para inicializar una matriz de 7 miembros con "7777777".
Esta es una forma en C ++ de implementar la respuesta utilizando una estructura de datos en C ++ en lugar de una matriz "C antigua".
Cambié a usar el valarray como un intento en mi código para tratar de usar C ++ ''isms v. C''isms ...
Hay una extensión para el compilador gcc que permite la sintaxis:
int array[100] = { [0 ... 99] = -1 };
Esto pondría todos los elementos a -1.
Esto se conoce como "Inicializadores designados", consulte here para obtener más información.
Tenga en cuenta que esto no está implementado para el compilador gcc c ++.
La página a la que enlazaste ya dio la respuesta a la primera parte:
Si se especifica un tamaño de matriz explícito, pero se especifica una lista de iniciación más corta, los elementos no especificados se establecen en cero.
No hay una forma integrada de inicializar toda la matriz a un valor distinto de cero.
En cuanto a lo que es más rápido, se aplica la regla habitual: "El método que otorga más libertad al compilador es probablemente más rápido".
int array[100] = {0};
simplemente le dice al 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 deben inicializarse los elementos, y así sucesivamente. Por supuesto, es probable que el compilador optimice eso, pero el punto es que aquí está especificando el problema en exceso, lo que obliga al compilador a trabajar más para obtener el mismo resultado.
Finalmente, si desea establecer la matriz en un valor distinto de cero, debe (al menos en C ++) usar std::fill
:
std::fill(array, array+100, 42); // sets every value in the array to 42
De nuevo, podrías hacer lo mismo con una matriz, pero esto es más conciso y le da más libertad al compilador. Solo está diciendo que desea que toda la matriz se llene con el valor 42. No dice nada sobre el orden en que debe hacerse, o cualquier otra cosa.
La página que has enlazado indica.
Si se especifica un tamaño de matriz explícito, pero se especifica una lista de iniciación más corta, los elementos no especificados se establecen en cero.
Problema de velocidad: cualquier diferencia sería despreciable 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 constante de los valores predeterminados (inicializados en el momento de la compilación) y luego memcpy
en la matriz modificable.
Otra forma de inicializar la matriz a un valor común, sería generar la lista de elementos en una serie de definiciones:
#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 )
Inicializar 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: Se introdujo DUPx para habilitar la sustitución de macros en parámetros a DUP
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 here .
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 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>
, ya que es una construcción bastante prolija. El verdadero trabajo 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 copiarlo, 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 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í, utilizamos nuestros índices de paquete de parámetros variadic simplemente 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 preocupamos por los valores (Así que lo dejamos en el vacío, para silenciar las advertencias 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 del paquete variadic ...
expanda 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, anulandolo, ignorando ese resultado vacío y luego devolviendo el valor, que se copia en la matriz. .
El argumento final, al que llamamos std::forward
on, es una optimización menor. Si alguien pasa una cadena 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
asegura que hagamos esto.
El código completo, incluidos los 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.");
}
Usando la sintaxis que usaste,
int array[100] = {-1};
dice "establece el primer elemento en -1
y el resto en 0
", ya que todos los elementos omitidos se establecen en 0
.
En C ++, para establecerlos todos en -1
, puedes usar algo como std::fill_n
(de <algorithm>
):
std::fill_n(array, 100, -1);
En C portátil, tienes que rodar tu propio bucle. Hay extensiones de compilador o puede depender del comportamiento definido por la implementación como acceso directo, si eso es aceptable.