Apoiador Nibble Fabiano Furtado Postado Junho 10, 2022 em 18:25 Apoiador Nibble Compartilhar Postado Junho 10, 2022 em 18:25 Pessoal, tudo bem? Uma dúvida básica sobre qual é a melhor maneira de se fazer uma contagem de ítens dentro de um array. Segue o trecho do código em C como exemplo: ...typedef struct { char * name; unsigned char age; } person_t; ... uint16_t c; person_t p[] = { {"Fulano", 32}, {"Ciclano", 41}, {NULL} ); ... Um detalhe é que esse array é dinâmico e não posso calcular o seu total de ítens usando sizeof(p)/sizeof(person_t) pois preciso fazer isso em tempo de execução. Por isso o uso do "NULL" ao final, marcando o fim do array. Formas: 1a) Mais específica pois faço referência ao campo "name": for ( c = 0 ; p[c].name != NULL ; c++ ); 2a) Mais genérica pois uso apenas aritmética de ponteiros, sem a referência ao campo "name": for( c = 0 ; (void *)(*(uint64_t *)(p+c)) != NULL ; c++ ); Os dois códigos acima geram o mesmo ASM e pergunto: essa segunda forma com aritmética de ponteiros é a melhor maneira de se fazer essa contagem? Queria algo mais genérico, independende de arquitetura e que também não faca referência aos campos da "struct". Talvez esse "uint64_t" não funcione em 32 bits, tornando essa forma dependente da arquitetura 64 bits. Obrigado! Citar Link para o comentário Compartilhar em outros sites More sharing options...
fredericopissarra Postado Junho 10, 2022 em 20:08 Compartilhar Postado Junho 10, 2022 em 20:08 (editado) sizeof p te dará a quantidade de bytes do objeto p (o identificador do array).sizeof p[0] te dará a quantidade de bytes de um elemento (o primeiro) do objeto p[0] (o elemento do array). Assim, a quantidade de elementos do array pode ser calculada como sizeof p / sizeof p[0]. A vantagem de usar essa aproximação é que sizeof p / sizeof p[0] será avaliada em tempo de compilação para uma constante. Note que, com isso, não é necessário usar o elemento final NULL:person_t p[] = { { "Fulano", 32 }, { "Ciclano", 41 } }; for ( int i = 0; i < sizeof p / sizeof p[0]; i++ ) printf( "\"%s\", %d anos.\n", p[i].name, p[i].age ); Note também que a sua segunda técnica (com o último item com NULL) assume que o ponteiro p+c, ao ser derreferenciado, te dará o ponteiro name. A técnica é válida (mas pode ser problemática -- especialmente com um casting para `uint64_t`, que será válido apenas no modo x86-64), mas seria mais "correto" fazer ( desde que o primeiro elemento apontado por q seja um char * ).// q aponta para um elemento de p... *(char **)q é o ponteiro `name`... for ( person_t *q = p; *(char **)q; q++ ) printf( "\"%s\", %d anos.\n", q->name, q->age ); Mas, isso parece mais complicado do que usar sizeof, não é? Editado Junho 10, 2022 em 20:27 por fredericopissarra Citar Link para o comentário Compartilhar em outros sites More sharing options...
Apoiador Nibble Fabiano Furtado Postado Junho 10, 2022 em 20:23 Autor Apoiador Nibble Compartilhar Postado Junho 10, 2022 em 20:23 13 minutos atrás, fredericopissarra disse: sizeof p te dará a quantidade de bytes do objeto p (o identificador do array).sizeof p[0] te dará a quantidade de bytes de um elemento (o primeiro) do objeto p[0] (o elemento do array). Assim, a quantidade de elementos do array pode ser calculada como sizeof p / sizeof p[0]. A vantagem de usar essa aproximação é que sizeof p / sizeof p[0] será avaliada em tempo de compilação para uma constante. Note que, com isso, não é necessário usar o elemento final NULL:person_t p[] = { { "Fulano", 32 }, { "Ciclano", 41 } }; for ( int i = 0; i < sizeof p / sizeof p[0]; i++ ) printf( "\"%s\", %d anos.\n", p[i].name, p[i].age ); Sim, eu concordo. Entretanto, conforme informei, o array é dinâmico e terá de ser avaliado em runtime. Citar Link para o comentário Compartilhar em outros sites More sharing options...
fredericopissarra Postado Junho 10, 2022 em 23:29 Compartilhar Postado Junho 10, 2022 em 23:29 Se você só tem um ponteiro e aloca o espaço dinamicamente, terá que mander o registro do tamanho em algum lugar. Não tem como obter o tamanho do buffer pós-facto. Citar Link para o comentário Compartilhar em outros sites More sharing options...
Apoiador Nibble Fabiano Furtado Postado Junho 15, 2022 em 23:56 Autor Apoiador Nibble Compartilhar Postado Junho 15, 2022 em 23:56 Em 10/06/2022 em 20:29, fredericopissarra disse: Se você só tem um ponteiro e aloca o espaço dinamicamente, terá que mander o registro do tamanho em algum lugar. Não tem como obter o tamanho do buffer pós-facto. Oi Frederico, concordo novamente com você. Entretanto, estou "contando" em runtime o número de ítens do array alocado dinamicamente através do uso de um ítem NULL em sua última posição. Desde que não seja permitido que os ítens do array sejam nulos, essa técnica te retorna o número de ítens deste array, com o contra de usar uma posição deste array para isso (o úitimo item NULL). Eu só gostaria de saber se a 2a forma de contagem que descrevi no enunciado é a mais correta ou se há alguma outra forma melhor de se fazer isso. Obrigado! Citar Link para o comentário Compartilhar em outros sites More sharing options...
fredericopissarra Postado Junho 16, 2022 em 09:42 Compartilhar Postado Junho 16, 2022 em 09:42 (editado) A segunda forma é... confusa. E potencialmente não portável. Ao converter um ponteiro para uint64_t você tem o potencial de bagunçar o endereço (para isso existe o tipo uintptr_t ou intptr_t... mas, mesmo assim, não recomando). Com sua estrutura eu faria: for ( person_t *q = p, c = 0; q->name; q++, c++ ); // Aqui c conterá a contagem (declare c como unsigned int, por exemplo). // Declará-lo como uint16_t te dará, no máximo 65535 elementos e, pior, // códigos que lidam com WORDs costumam ser maiores que com DWORDs... // Prefira 'int' e derivados, se puder. Com relação à segunda alegação, veja isso: ; Opcodes Instruções 66 FF C1 INC CX FF C1 INC ECX 48 FF C1 INC RCX Note que para lidar com CX o compilador tem que adicionar o prefixo 0x66 nos opcodes... É sempre bom evitar tipos de tamanho diferente de 'int', se puder (instruções que lidam com registradores de 64 bits adicionam o prefixo REX [0x4?]). Ainda... podemos fazer, também, o seguinte: person_t *q = p; for ( ; q->name; q++ ) ; c = q - p; Já que a subtração de dois ponteiros do mesmo tipo nos dá a quantidade de elementos. Seria melhor definir, nesse caso, c como ptrdiff_t. PS: Tanto as especificações POSIX, quanto a documentação da glibc, proíbem a nomeação de identificadores com o sufixo _t (minúsculo), reservando-o para bibliotecas nativas. Eu evito isso nomeando meus tipos com o sufixo _T (maiúsculo), como em person_T. Editado Junho 16, 2022 em 10:16 por fredericopissarra 2 Citar Link para o comentário Compartilhar em outros sites More sharing options...
Apoiador Nibble Fabiano Furtado Postado Junho 18, 2022 em 20:45 Autor Apoiador Nibble Compartilhar Postado Junho 18, 2022 em 20:45 Em 16/06/2022 em 06:42, fredericopissarra disse: A segunda forma é... confusa. E potencialmente não portável. Ao converter um ponteiro para uint64_t você tem o potencial de bagunçar o endereço (para isso existe o tipo uintptr_t ou intptr_t... mas, mesmo assim, não recomando). ... PS: Tanto as especificações POSIX, quanto a documentação da glibc, proíbem a nomeação de identificadores com o sufixo _t (minúsculo), reservando-o para bibliotecas nativas. Eu evito isso nomeando meus tipos com o sufixo _T (maiúsculo), como em person_T. EXCELENTE!!! Muito obrigado pela explicação! 1 1 Citar Link para o comentário Compartilhar em outros sites More sharing options...
Posts Recomendados
Participe da conversa
Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.