Ir para conteúdo

Mais coisas que estão no padrão que permitem "macetes"...


fredericopissarra

Posts Recomendados

Existe uma diferença entre declarar e usar algo numa expressão. Por exemplo, a declaração:

char s[] = "fred";

Define um array s que será preenchido com 5 caracteres (o '\0' também conta), mas esses 5 caracteres não serão alocados numa sessão read-only da imagem binária (dado o literal "fred", acima).

Mas, usar uma "string" literal numa expressão, ou chamada de função, pode forçar o compilador a criar um array "read-only":

if ( ! strcmp ( s, "fred" ) ) do_something();

Neste caso os 5 bytes de "fred" serão colocados na sessão .rodata, obrigatoriamente, porque a função strcmp() exige o uso de um ponteiro. O mesmo acontece com a declaração:

char *s = "fred";

Sabendo que esse literal entre aspas é um atalho para uma sequência de bytes (um array), podemos comparar duas "strings" com até 8 chars (contando o '\0' final), assim [compare as funções f() e g()]:

#include <string.h>

int f( char *s ) { return ! strcmp( s, "fred" ); }

int g( char *s )
{
  long long a, b;

  a = *(long long *)s;
  b = *(long long *)"fred";

  return ( ( a & 0xffffffffffULL ) - b ) == 0;
}

A função f() é esquisita. Repare como usei o literal "fred" como ponteiro long long e o dereferenciei...

Outra coisa é que tive que mascarar os bits superiores de a, a partir do 6º byte porque, na comparação, o 5º byte precisa ser 0. f() parece mais complicada e, portanto, supostamente, o código fica mais lento, certo? Errado: Essa assertiva quase nunca é verdadeira. O tamanho e performance do código depende do que ele faz, não da quantidade de linhas e expressões que ele contém. Eis a listagem em assembly das duas rotinas:

section .rodata

ro_fredstr: db 'f', 'r', 'e', 'd', 0, 0, 0, 0

section .text

f:
  mov rsi,rdi
  lea rdi,[ro_fredstr]
  mov ecx,5
  rep cmpsb
  seta al
  sbb al,0
  test al,al
  sete al
  movzx eax,al
  ret
  
g:
  mov rax,0xffffffffff
  and rax,[rdi]
  cmp rax,[ro_fredstr]
  sete al
  movzx eax,al
  ret

Claro, isso só funciona com comparações com strings de até 8 caracteres e com tamanho previamente conhecido (temos que criar a máscara de acordo!). Não é lá muito "usável" no dia-a-dia, mas ilustra o ponto de que podemos criar rotinas mais rápidas usando esse tipo de macete.

Outro detalhe interessante sobre strings literais em expressões: Como elas podem ser alocadas como arrays e a especificação nos diz, explicitamente, que o operador [] é um atalho para uma expressão usando ponteiros, isto é:

c = a[i];  // isto é a mesma coisa que...
c = *(a+i); // ... isto!

Podemos escrever, sem problemas, algo assim:

c = "fred"[2]; // c será 'e'.

E como qualquer adição segue a propriedade comutativa (a+b = b+a), uma construção, válida, mas até mais estranha, é possível:

c = 2["fred"];

Interessante, não?

Link para o comentário
Compartilhar em outros sites

44 minutos atrás, Aof disse:

a = *(long long *)s;
 b = *(long long *)"fred";

so nao entedi essa parte.

sei que o "*" antes do "(" significa que e um ponteiro, mas "long long *" complicou.

Nope... o * antes de (long long *) é um operador de indireção... ele pega o valor inteiro na variável ponteiro e o usa para obter o dado para onde esse endereço aponta. O (long long *) é um operador de casting (conversão) que, neste caso, converte o ponteiro do tipo char * para o tipo long long *.

Assim, o operador * será obrigado a acessar 8 bytes (de um long long), não apenas 1 (de um char).

Link para o comentário
Compartilhar em outros sites

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...