O que acontece quando uma função embutida é passada como um parâmetro em C?
-
06-07-2019 - |
Pergunta
Hoje eu estava escrevendo algum código C para classificar uma matriz de estruturas usando o QuickSort com uma função de comparador personalizada para determinar sua ordem.
No começo, escrevi com a chamada para a função comparadora codificada na função Quicksort. Então eu pensei que talvez fosse mais agradável passar essa função como um argumento para uma função genérica do QuickSort.
No meu código original, eu havia declarado a função do comparador inline
. No meu novo código, eu mantive o inline
Declaração, embora isso não tenha realmente feito muito sentido para mim, já que a função estava sendo passada como um parâmetro. No entanto, o compilador não reclamou!
Minha pergunta é: é o inline
Declaração tendo algum efeito aqui, ou é apenas uma recomendação ao compilador que está sendo ignorado?
Código original:
typedef struct _CGRect {
CGPoint origin;
CGSize size;
} CGRect;
typedef enum _NSComparisonResult {
NSOrderedAscending = -1,
NSOrderedSame,
NSOrderedDescending
} NSComparisonResult;
static inline NSComparisonResult CGRectCompareRowsFirst(CGRect r1, CGRect r2)
{
if (r1.origin.y < r2.origin.y)
return NSOrderedAscending;
else if (r1.origin.y > r2.origin.y)
return NSOrderedDescending;
else
{
if (r1.origin.x < r2.origin.x)
return NSOrderedAscending;
else if (r1.origin.x > r2.origin.x)
return NSOrderedDescending;
else
return NSOrderedSame;
}
}
static void CGRectQuicksortRowsFirst(CGRect *left, CGRect *right)
{
if (right > left) {
CGRect pivot = left[(right-left)/2];
CGRect *r = right, *l = left;
do {
while (CGRectCompareRowsFirst(*l, pivot) == NSOrderedAscending) l++;
while (CGRectCompareRowsFirst(*r, pivot) == NSOrderedDescending) r--;
if (l <= r) {
CGRect t = *l;
*l++ = *r;
*r-- = t;
}
} while (l <= r);
CGRectQuicksortRowsFirst(left, r);
CGRectQuicksortRowsFirst(l, right);
}
}
static void CGRectSortRowsFirst(CGRect *array, int length)
{
CGRectQuicksortRowsFirst(array, array+length-1);
}
Novo Código:
static inline NSComparisonResult CGRectCompareRowsFirst(const void *s1, const void *s2)
{
CGRect r1 = *(CGRect *)s1, r2 = *(CGRect *)s2;
if (r1.origin.y < r2.origin.y)
return NSOrderedAscending;
else if (r1.origin.y > r2.origin.y)
return NSOrderedDescending;
else
{
if (r1.origin.x < r2.origin.x)
return NSOrderedAscending;
else if (r1.origin.x > r2.origin.x)
return NSOrderedDescending;
else
return NSOrderedSame;
}
}
static void quick(CGRect *left, CGRect *right, NSComparisonResult(*f)(const void *, const void *))
{
if (right > left) {
CGRect pivot = left[(right-left)/2];
CGRect *r = right, *l = left;
do {
while (f(&*l, &pivot) == NSOrderedAscending) l++;
while (f(&*r, &pivot) == NSOrderedDescending) r--;
if (l <= r) {
CGRect t = *l;
*l++ = *r;
*r-- = t;
}
} while (l <= r);
quick(left, r, f);
quick(l, right, f);
}
}
static void CGRectSortRowsFirst(CGRect *array, int length)
{
quick(array, array+length-1, CGRectCompareRowsFirst);
}
Solução
O Inline é apenas uma recomendação para o compilador e pode ser ignorado. Isso pode acontecer por vários motivos, como se a função for complexa demais para ser inlinada com segurança. Se você o passar como um parâmetro para uma função como acima, o compilador criará a versão não-linada cujo endereço será passado para a função.
Pode ser possível que o compilador ainda possa incluir a função - por exemplo, durante a geração de código, o compilador poderia utilizar o dica de função embutida substituindo a chamada por meio de um ponteiro de função para apenas a função expandida; Não tenho certeza se algum compilador atual faria isso.
As versões inlinadas e não enlutadas podem e geralmente coexistem em um programa compilado.
Outras dicas
A palavra -chave 'inline' é simplesmente um sinalizador do compilador que diz para lidar com ela de maneira diferente, pois copiará o corpo da função e a substituirá pela chamada de função real. Este é um aumento de desempenho se você tiver uma pequena função que é reutilizada em muitos locais em seu código. É uma coisa interessante de usar com acessadores e modificadores. No seu caso, acho que você poderia deixar o que está. Você não está fazendo nada pesado. A diferença provavelmente não será perceptível.