Como você compara duas instâncias de estruturas para igualdade no padrão C?
Como você compara duas instâncias de estruturas para igualdade no padrão C?
Respostas:
C não fornece recursos de idioma para fazer isso - você precisa fazer isso sozinho e comparar cada membro da estrutura por membro.
0.0, -0.0 NaN
é um problema memcmp()
. Ponteiros que diferem na representação binária podem apontar para o mesmo local (por exemplo, DOS: seg: offset) e, portanto, são iguais. Alguns sistemas têm vários ponteiros nulos que se comparam igualmente. O mesmo para obscuro int
com os tipos -0 e ponto flutuante com codificações redundantes. (Intel long double, decimal64, etc.) Esses problemas não fazem diferença, são calloc()
usados ou não, ou preenchimento.
==
não está trabalhando com estruturas (como eu), consulte stackoverflow.com/questions/46995631/…
Você pode ser tentado a usar memcmp(&a, &b, sizeof(struct foo))
, mas pode não funcionar em todas as situações. O compilador pode adicionar espaço de buffer de alinhamento a uma estrutura, e não é garantido que os valores encontrados nos locais de memória no espaço de buffer tenham um valor específico.
Porém, se você usar calloc
ou memset
o tamanho total das estruturas antes de usá-las, poderá fazer uma comparação superficial com memcmp
(se sua estrutura contiver ponteiros, ela corresponderá apenas se o endereço apontado pelos ponteiros for o mesmo).
memcmp
condição de que a memória foi limpa primeiro. O que está quase funcionando, mas não está correto. Muitas vezes, a questão também não define "igualdade"; portanto, se você entender "igualdade de bytes da representação do objeto", memcmp
fará exatamente isso (se a memória está limpa ou não).
Se você fizer muito isso, sugiro escrever uma função que compare as duas estruturas. Dessa forma, se você alterar a estrutura, precisará alterar apenas a comparação em um único local.
Quanto a como fazê-lo .... Você precisa comparar todos os elementos individualmente
Você não pode usar o memcmp para comparar estruturas de igualdade devido a caracteres de preenchimento aleatório em potencial entre campos em estruturas.
// bad
memcmp(&struct1, &struct2, sizeof(struct1));
O exemplo acima falharia em uma estrutura como esta:
typedef struct Foo {
char a;
/* padding */
double d;
/* padding */
char e;
/* padding */
int f;
} Foo ;
Você precisa usar a comparação entre membros para estar seguro.
@Greg está correto em que é necessário escrever funções de comparação explícitas no caso geral.
É possível usar memcmp
se:
NaN
.-Wpadded
com clang para verificar isso) OU as estruturas são explicitamente inicializadas memset
na inicialização.BOOL
) que tenham valores distintos, mas equivalentes.A menos que você esteja programando para sistemas incorporados (ou escrevendo uma biblioteca que possa ser usada neles), eu não me preocuparia com alguns dos casos de canto no padrão C. A distinção entre ponteiro versus ponto distante não existe em nenhum dispositivo de 32 ou 64 bits. Nenhum sistema não incorporado que eu conheço possui vários NULL
ponteiros.
Outra opção é gerar automaticamente as funções de igualdade. Se você definir suas definições de estrutura de uma maneira simples, é possível usar um processamento de texto simples para lidar com definições simples de estrutura. Você pode usar libclang para o caso geral - uma vez que usa o mesmo front-end que o Clang, ele lida com todos os casos de canto corretamente (exceto erros).
Eu não vi essa biblioteca de geração de código. No entanto, parece relativamente simples.
No entanto, também é o caso de que tais funções de igualdade geradas frequentemente façam a coisa errada no nível do aplicativo. Por exemplo, duas UNICODE_STRING
estruturas no Windows devem ser comparadas superficial ou profundamente?
memset
, etc., não garante o valor dos bits de preenchimento após mais de escrita para um elemento de estrutura, consulte: stackoverflow.com/q/52684192/689161
Observe que você pode usar o memcmp () em estruturas não estáticas sem se preocupar com preenchimento, desde que não inicialize todos os membros (de uma só vez). Isso é definido por C90:
{0, }
também zerará quaisquer bytes de preenchimento?
Depende se a pergunta que você está fazendo é:
Para descobrir se eles são o mesmo objeto, compare os ponteiros com as duas estruturas de igualdade. Se você deseja descobrir em geral se eles têm o mesmo valor, é necessário fazer uma comparação profunda. Isso envolve comparar todos os membros. Se os membros forem ponteiros para outras estruturas, você também precisará recursá-las.
No caso especial em que as estruturas não contêm ponteiros, você pode executar um memcmp para realizar uma comparação bit a bit dos dados contidos em cada uma, sem precisar saber o que os dados significam.
Certifique-se de saber o que 'igual' significa para cada membro - é óbvio para ints, mas mais sutil quando se trata de valores de ponto flutuante ou tipos definidos pelo usuário.
memcmp
não compara estrutura, memcmp
compara o binário e sempre há lixo na estrutura; portanto, sempre sai falso em comparação.
Compare elemento por elemento, é seguro e não falha.
Se as estruturas contiverem apenas primitivas ou se você estiver interessado em igualdade estrita, poderá fazer algo assim:
int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs) { retornar memcmp (lhs, rsh, sizeof (struct my_struct)); }
No entanto, se suas estruturas contiverem ponteiros para outras estruturas ou uniões, será necessário escrever uma função que compare os primitivos corretamente e faça chamadas de comparação com as outras estruturas, conforme apropriado.
Lembre-se, no entanto, de que você deve ter usado o memset (& a, sizeof (struct my_struct), 1) para zerar o intervalo de memória das estruturas como parte da inicialização do ADT.
Este exemplo compatível usa a extensão do compilador #pragma pack do Microsoft Visual Studio para garantir que os membros da estrutura sejam compactados da maneira mais rígida possível:
#include <string.h>
#pragma pack(push, 1)
struct s {
char c;
int i;
char buffer[13];
};
#pragma pack(pop)
void compare(const struct s *left, const struct s *right) {
if (0 == memcmp(left, right, sizeof(struct s))) {
/* ... */
}
}