Agora que temos std :: array, quais são os usos restantes para os arrays de estilo C?


88

std::arrayé muito superior aos arrays C. E mesmo se eu quiser interoperar com o código legado, posso apenas usar std::array::data(). Existe alguma razão para eu querer uma matriz da velha escola?

Respostas:


60

A menos que eu tenha esquecido algo (não segui as mudanças mais recentes no padrão muito de perto), a maioria dos usos dos arrays de estilo C ainda permanecem. std::arraypermite a inicialização estática, mas ainda não contará os inicializadores para você. E uma vez que o único uso real de matrizes de estilo C antes std::arrayera para tabelas inicializadas estaticamente ao longo das linhas de:

MyStruct const table[] =
{
    { something1, otherthing1 },
    //  ...
};

usando as funções usuais begine de endmodelo (adotadas em C ++ 11) para iterar sobre eles. Sem nunca mencionar o tamanho, que o compilador determina a partir do número de inicializadores.

EDIT: Outra coisa que esqueci: literais de string ainda são arrays de estilo C; ou seja, com tipo char[]. Não acho que alguém excluiria o uso de literais de string só porque temos std::array.


7
Você pode escrever um modelo de função variável que constrói matrizes sem que você precise especificar o comprimento.
direito

2
Com o C ++ 17 Class Template Deduction, a dedução automática do número de inicializadores é suportada. Por exemplo, "auto a = std :: array {1, 2, 3};"
Ricky65

Nitpick: o tipo de literais de string éconst char[]
Bulletmagnet de

30

Não. Para, uh, ser franco. E em 30 caracteres.

Claro, você precisa de arrays C para implementar std::array, mas essa não é realmente uma razão para que um usuário deseje arrays C. Além disso, não, std::arraynão tem menos desempenho do que um array C e tem uma opção de acesso com verificação de limites. E, finalmente, é completamente razoável para qualquer programa C ++ depender da biblioteca padrão - esse é o objetivo de ser padrão - e se você não tiver acesso a uma biblioteca padrão, então seu compilador não está em conformidade e o question é marcada como "C ++", não "C ++ e aquelas coisas não C ++ que perdem metade da especificação porque a consideraram inadequada.".


1
Hm. E se você estiver escrevendo código C ++ que é chamado de outra linguagem e precisa que algo seja passado como parâmetro?
asveikau

3
As implementações independentes podem deixar de fora quase toda a biblioteca padrão e ainda estar em conformidade. Eu teria sérias dúvidas sobre um compilador que não poderia ser implementado std::arrayem uma implementação independente do C ++ 11, no entanto.
Dennis Zickefoose

11
C ++ 0x Final Draft (Documento N3092) § 1.4.7 "Dois tipos de implementações são definidos: hospedada e independente. Para uma implementação hospedada, esta Norma define o conjunto de bibliotecas disponíveis. Uma implementação independente é aquela em que a execução pode ocorrem sem o benefício de um sistema operacional e tem um conjunto de bibliotecas definidas pela implementação que inclui certas bibliotecas de suporte a linguagem ". A STL não está incluída como uma biblioteca de" suporte a linguagem "em um compilador independente
Earlz

24

Parece que usar arrays multidimensionais é mais fácil com arrays C do que com std::array. Por exemplo,

char c_arr[5][6][7];

em oposição a

std::array<std::array<std::array<char, 7>, 6>, 5> cpp_arr;

Também devido à propriedade de decaimento automático dos arrays C, c_arr[i]no exemplo acima irá decair para um ponteiro e você só precisa passar as dimensões restantes como mais dois parâmetros. Meu ponto é que c_arrnão é caro copiar. No entanto, cpp_arr[i]será muito caro copiar.


1
No entanto, você pode passar arrayde uma função multidimensional para uma função sem perder as dimensões. E se você passá-lo para um template de função, então essa função pode deduzir tanto a dimensão quanto o tamanho de cada dimensão, ou apenas uma das duas. Isso pode ser interessante para bibliotecas de modelos científicos que trabalham principalmente em dimensões arbitrárias.
Sebastian Mach

29
um simples template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;deve resolver qualquer um desses problemas.
Miles Rout

6
Seu exemplo c_arré muito caro para copiar! Você deve fornecer o código para fazer isso sozinho. O ponteiro para o qual ele decairá é um equivalente mais próximo a uma referência do que a uma cópia, e você pode usar std::arraypara passar uma referência se for o que deseja.
ChetS de

1
@MilesRout tecnicamente , que não deve ser std::size_t, em vez de int? desculpe por picuinhas, mas isso o tornaria universal.
robbie de

1
@ robbie0630 Sim, você pode fazer isso size_tse quiser, embora eu não consiga imaginar que existam muitos cenários em que matrizes com mais de 4 bilhões de linhas ou colunas sejam necessárias.
Milhas de

13

Como Sumant disse, arrays multidimensionais são muito mais fáceis de usar com arrays C embutidos do que com std::array.

Quando aninhado, std::arraypode se tornar muito difícil de ler e desnecessariamente prolixo.

Por exemplo:

std::array<std::array<int, 3>, 3> arr1; 

comparado com

char c_arr[3][3]; 

Além disso, nota que begin(), end()e size()todos os valores sem sentido de retorno quando você ninho std::array.

Por essas razões, criei meus próprios contêineres de array multidimensional de tamanho fixo array_2de array_3d. Eles são análogos, std::arraymas para matrizes multidimensionais de 2 e 3 dimensões. Eles são mais seguros e não têm pior desempenho do que os arrays multidimensionais integrados. Não incluí um contêiner para matrizes multidimensionais com dimensões maiores que 3, pois são incomuns. Em C ++ 0x, uma versão de modelo variável pode ser feita que suporta um número arbitrário de dimensões.

Um exemplo da variante bidimensional:

//Create an array 3 x 5 (Notice the extra pair of braces) 

fsma::array_2d <double, 3, 5> my2darr = {{
    { 32.19, 47.29, 31.99, 19.11, 11.19},
    { 11.29, 22.49, 33.47, 17.29, 5.01 },
    { 41.97, 22.09, 9.76, 22.55, 6.22 }
}};

A documentação completa está disponível aqui:

http://fsma.googlecode.com/files/fsma.html

Você pode baixar a biblioteca aqui:

http://fsma.googlecode.com/files/fsma.zip


4
Arrays estilo C de tamanho fixo são fáceis, mas se você quiser variar as dimensões, as coisas ficam complicadas. Por exemplo, dado arr[x][y], você não pode dizer se arré um array de arrays, um array de ponteiros, um ponteiro para um array ou um ponteiro para um ponteiro; todos para implementações são legítimos, dependendo de suas necessidades. E provavelmente a maioria dos casos de uso do mundo real para arrays multidimensionais exige que o tamanho seja determinado no tempo de execução.
Keith Thompson

Eu adoraria ver essa implementação de modelo variadic de matrizes n-dimensionais! Meta-programação no seu melhor!
steffen

3
@steffen Eu fiz uma tentativa alguns anos atrás. Você pode visualizá-lo aqui: code.google.com/p/fsma/source/browse/trunk/… . Caso de teste aqui: code.google.com/p/fsma/source/browse/trunk/… . Tenho certeza de que isso pode ser feito muito melhor.
Ricky65

5

Os arrays de estilo C disponíveis em C ++ são, na verdade, muito menos versáteis do que os arrays C reais. A diferença é que, em C, os tipos de array podem ter tamanhos de tempo de execução . O seguinte é um código C válido, mas não pode ser expresso com matrizes de estilo C C ++ nem com os array<>tipos C ++ :

void foo(int bar) {
    double tempArray[bar];
    //Do something with the bar elements in tempArray.
}

Em C ++, você teria que alocar a matriz temporária no heap:

void foo(int bar) {
    double* tempArray = new double[bar];
    //Do something with the bar elements behind tempArray.
    delete[] tempArray;
}

Isso não pode ser alcançado com std::array<>, porque barnão é conhecido em tempo de compilação, requer o uso de matrizes de estilo C em C ++ ou de std::vector<>.


Embora o primeiro exemplo possa ser expresso com relativa facilidade em C ++ (embora exija new[]e delete[]), o seguinte não pode ser alcançado em C ++ sem std::vector<>:

void smoothImage(int width, int height, int (*pixels)[width]) {
    int (*copy)[width] = malloc(height*sizeof(*copy));
    memcpy(copy, pixels, height*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y][x] = //compute smoothed value based on data around copy[y][x]
        }
    }
    free(copy);
}

A questão é que os ponteiros para as matrizes de linha int (*)[width]não podem usar uma largura de tempo de execução em C ++, o que torna qualquer código de manipulação de imagem muito mais complicado em C ++ do que em C. Uma implementação C ++ típica do exemplo de manipulação de imagem ficaria assim:

void smoothImage(int width, int height, int* pixels) {
    int* copy = new int[height*width];
    memcpy(copy, pixels, height*width*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y*width + x] = //compute smoothed value based on data around copy[y*width + x]
        }
    }
    delete[] copy;
}

Este código faz exatamente os mesmos cálculos que o código C acima, mas precisa realizar o cálculo do índice manualmente onde quer que os índices sejam usados . Para o caso 2D, isso ainda é viável (embora venha com muitas oportunidades de errar no cálculo do índice). No entanto, fica realmente desagradável no caso 3D.

Gosto de escrever código em C ++. Mas sempre que preciso manipular dados multidimensionais, realmente me pergunto se devo mover essa parte do código para C.


7
Deve-se observar que pelo menos Clang e GCC oferecem suporte a VLAs em C ++.
Janus Troelsen

@JanusTroelsen e também que eles são terrivelmente limitados nos tipos de elementos que suportam.
direito

O C11 não torna o VLA opcional? Se sim, então acho que sua resposta é enganosa. Estaria correto quando C99 fosse o padrão, mas não C11.
Bóson Z de

1
@Zboson C99 é um padrão C e existem compiladores que implementam seus recursos de VLA ( gccpor exemplo). O C11 tornou opcionais várias coisas interessantes, e não acho que seja porque eles queiram proibir o recurso. Eu tendo a ver isso como um sinal de que eles queriam abaixar o nível de escrever um compilador totalmente compatível com os padrões: VLAs são bastante difíceis de implementar e muitos códigos podem ser dispensados, então faz sentido para um novo compilador em algum novo plataforma para não ter que implementar VLAs imediatamente.
cmaster - restabelecer monica

-1

Pode ser que std::arraynão seja lento. Mas eu fiz alguns benchmarking usando armazenamento simples e li do std :: array; Veja os resultados de benchmark abaixo (em W8.1, VS2013 Atualização 4):

ARR_SIZE: 100 * 1000
Avrg = Tick / ARR_SIZE;

test_arr_without_init
==>VMem: 5.15Mb
==>PMem: 8.94Mb
==>Tick: 3132
==>Avrg: 0.03132
test_arr_with_init_array_at
==>VMem: 5.16Mb
==>PMem: 8.98Mb
==>Tick: 925
==>Avrg: 0.00925
test_arr_with_array_at
==>VMem: 5.16Mb
==>PMem: 8.97Mb
==>Tick: 769
==>Avrg: 0.00769
test_c_arr_without_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 358
==>Avrg: 0.00358
test_c_arr_with_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 305
==>Avrg: 0.00305

De acordo com as marcas negativas, o código que usei está no pastebin ( link )

O código da classe de benchmark está aqui ;

Não sei muito sobre benchmarks ... Meu código pode estar com falhas


6
Resultados de benchmark sem código de benchmark ou sinalizadores de compilação? Venha, você pode fazer melhor.
R. Martinho Fernandes

FWIW, apenas aquele pequeno trecho de código já mostra que o benchmark está gravemente falho. Um compilador inteligente o suficiente transformará tudo emlong test_arr_without_init() { return ARR_SIZE; }
R. Martinho Fernandes

Aquilo foi apenas um exemplo. Eu pensei que não era grande coisa. Mudei o código para retornar void, usei versão de compilação no VS 2013, com / O2 / Ot / Gl.
K'Prime

Remover o valor de retorno significa que o compilador pode transformar tudo em void test_arr_without_init() {}agora. Você realmente precisa passar por muitos obstáculos para ter certeza de que o código que está medindo é o código que deseja medir.
R. Martinho Fernandes

-6
  1. para implementar algo como std::array
  2. se você não quiser usar o STL ou não puder
  3. Para desempenho

27
Diga-me como std::arrayterá menos desempenho do que um array C.
Xeo

2
Da wikipedia : "A implementação do array não é necessária para fazer a verificação de limite. No entanto, a implementação no boost faz isso para o operador [], mas não para os iteradores." - então o operador [] é mais lento. Não examinei as implementações, mas qualquer código na implementação pode atrapalhar o otimizador.
Lou Franco

19
@Aaron McDaid: Isso é apenas dentro at(), não é operator[], apenas gosto std::vector. Não há diminuição de desempenho ou inchaço de código std::array, o compilador é projetado para otimizar esse tipo de coisa. E, é claro, a adição da função marcada é uma excelente ferramenta de depuração e uma grande vantagem. @Lou Franco: Todo código C ++ pode depender da biblioteca padrão - é para isso que serve. @Earlz: Se você não tem STL disponível, então não é C ++, e ponto final.
Cachorro de

6
@Earlz: O C ++ Standard contém a biblioteca Standard. Se você não pode usar a biblioteca, ela não está em conformidade. E em segundo lugar, você deve ter um compilador de merda para o uso de std::arrayser maior do que o uso de array C equivalente.
Cachorro de

5
@Earlz: Há uma grande diferença entre "não estar em conformidade" e "recursos ausentes que são centenas de páginas nas especificações".
Cachorro de
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.