Ir para conteúdo

fredericopissarra

Membros
  • Postagens

    420
  • Registro em

  • Última visita

  • Dias Ganhos

    160

Tudo que fredericopissarra postou

  1. Existem diversas maneiras de implementar listas, especialmente circulares... Eu recomendo a implementação de lista circular com encadeamento duplo e com um nó sentinela "cabeça" (head) vazio. Isso facilita um bocado a implementação e permite a implementação via polimorfismo (mesmo em C). Assim, uma lista vazia sempre terá um nó cabeça onde os links apontam para si mesmo... adicionar nós é questão de mudar os links apenas. Isso também facilita a alocação, já que num novo nó só tem que obedecer a ordem dos links do nó "cabeça", com membros adicionados. A decisão de como a lista será implementada é o primeiro passo (deixo para sua pesquisa e tentativa de implementação -- não mostrarei aqui). Buscar por um nó da lista é simplesmente percorrer todos os elementos "depois" do nó "cabeça" até voltar a ele, comparando as "chaves" com a desejada... Contar quantos nós existem é simplesmente percorrer toda lista à partir do próximo nó depois da "cabeça" até retornar a ela. Apagar um nó é só modificar os links e dealocar a memória do nó. Depois que tiver algum trabalho feito, poste aqui... Não acho que alguém vá fazer o trabalho por você... []s Fred
  2. Se fosse para dar uma nota, a minha seria ZERO. 1º - Isso ai não é C (é C++) 2º - A apresentação do código está horrível; 3º - A apresentação do programa, funcionando, também está ruim; 3º - Existem vários erros (não de "compilação", mas de como o troço supostamente deveria funcionar).
  3. "vetor original" me lembra avaliação de escolas de samba...
  4. Não sabia que overlays ainda eram usados na época dos PEs. Eram muito comuns na época do MS-DOS.
  5. Não faz parte de sua dúvida, mas eis um código que não é recursivo e usa aritmética de múltipla precisão e te permite calcular o fatorial de qualquer valor inteiro, positivo, dentro da faixa de um unsigned long int. // Calcula fatorial usando aritmética de multipla precisão. // // Compilar com: // cc -O2 -o factmp factmp.c -lgmp // // Não esquecer de instalar libgmp-dev. // #include <stdio.h> #include <stdlib.h> #include <gmp.h> #include <errno.h> #include <time.h> int main(int argc, char *argv[]) { char *p; mpz_t r; unsigned long n, c, total; time_t t1, t2; if ( argc != 2 ) { fprintf( stderr, "Usage: %s <value>\n", argv[0] ); return EXIT_FAILURE; } // O valor deve ser inteiro e dentro da faixa de unsigned long int. errno = 0; n = strtoul( *++argv, &p, 10 ); if ( errno == ERANGE || *p ) { fputs( "ERROR: Invalid value.\n", stderr ); return EXIT_FAILURE; } // Inicializa r e seta-o com 1. mpz_init_set_ui(r, 1); t1 = time( NULL ); c = 0; total = n; while ( n >= 1 ) { mpz_mul_ui( r, r, n-- ); printf( "%.2f%% complete... \r", 100.0 * ++c / total ); } putchar( '\n' ); t2 = time( NULL ); mpz_out_str( stdout, 10, r ); putchar('\n'); // Joga r no lixo. mpz_clear(r); printf( "Total time: %lu seconds.\n", t2 - t1 ); return EXIT_SUCCESS; } Mas, atenção: Se você usar valores muito grandes (maiores que 100000, por exemplo), o tempo de calculo será bem grande (tente!). []s Fred
  6. Considere: int fact( int x ) { if ( x < 0 ) return 1; return x * fact( x - 1 ); } Se x = 0, inicialmente, temos um return 0 * fact( -1 ); E essa chamada, recursiva, retornará 1 (já que o novo x < 0)... Dai, 0 * 1 = 0. Note que, por definição, n! = 1 se n <= 1. E fatorial só é definido para valores positivos... A rotina DEVERIA ser: unsigned int fact( unsigned int x ) { if ( x <= 1 ) return 1; return x * fact( x - 1 ); } Sendo que a função causará overflow se x > 12.
  7. Eu vi o que o "code" contém... e ele contém um monte de erros. Você assume que syscall vai manter o conteúdo dos registradores e não é isso o que acontece (se acontece é por sorte!). []s Fred
  8. A essa aqui, no seu texto: ; "\x31\xC9\xF7\xE1\xB0\x0B\x51\x68\x2F\x2F\x73\x68\x68\x2F\x62\x69\x6E\x89\xE3\xCD\x80" 31 c9 xor ecx,ecx f7 e1 mul ecx b0 0b mov al,0xb 51 push ecx 68 2f 2f 73 68 push 0x68732f2f 68 2f 62 69 6e push 0x6e69622f 89 e3 mov ebx,esp cd 80 int 0x80 Obviamente isso assume que `execve` não falhará (cadê o `ret` depois do `int 0x80`?) e você dá um exemplo de uso: __asm__ ("movl $0xffffffff, %eax\n\t" "movl %eax, %ebx\n\t" "movl %eax, %ecx\n\t" "movl %eax, %edx\n\t" "movl %eax, %esi\n\t" "movl %eax, %edi\n\t" "movl %eax, %ebp\n\t" // Calling the shellcode "call shellcode"); É desse call que estou falando... E há outro aspecto a ser considerado... Na SysV ABI, EBX (ou RBX no modo x86-64) tem que ser preservado entre chamadas... Esse mov ebx,esp altera EBX sem preservá-lo, o que levará (possivelmente, se execve falhar e se tivéssemos um ret ali no final) o código chamador a se comportar de forma errática... Seu shellcode deveria preservar EBX.
  9. Uma abordagem interessante, mas há alguns "erros"... O primeiro é que a syscall execve() espera que o segundo argumento seja a lista argv incluindo argv[0]... Passar NULL para args pode funcionar, mas está fora do padrão. Segundo, no modo x86-64 esse macete dos dois pushes com literais não funcionará... E note que você está empurrando "\bin\\sh" (com essas duas barras no meio", quando poderia empurrar "\bin\sh\0" (8 bytes), embora esteja empurrando um zero antes para garantir o fim da string. No caso, se tentar fazer: push `\sh\0` push `/bin` No modo x86 isso gerará a string, na pilha: "\bin",0,0,0,0,"\sh\0",0,0,0,0. PUSH com um imediato de 64 bits não existe na ISA. Terceiro, execve() pode falhar... dependendo de como a chamada será feita, o programa crasha (aliás, você não quis dizer jmp shellcode ao invés de um call, acima? Se execve() falhar seu processo provavelmente crasha (não tem um ret)...). O meu desafio para vocês é fazer um código executável menor que esses 33 bytes (de código, não de imagem binária) para o modo x86-64: ; test.asm (NASM) ; ; nasm -felf64 -o test.o test.asm ; ld -s -o test test.o ; bits 64 default rel ; Usando endereços relativos ao RIP. ; código fica "position independent". ; ESSE é o default da SysV ABI. global _start _start: lea rdi,[sh] ; Calculo do endereço efetivo para deixar ; o código "PIC" (ou PIE). ; PUSH sh/POP rdi usa 1 byte a menos, mas ; perderíamos o PIC/PIE. xor edx,edx ; envp = NULL; push rdx ; argv[1] = NULL; push rdi ; argv[0] = "/bin/sh"; ; mov rsi,rsp push rsp pop rsi ; rsi = &argv. ; sys_execve espera, como argumentos: ; int execve( const char *path, char **argv, char **envp ); ; Por padrão argv[0] deve ser o mesmo ponteiro path. ; mov eax,59 push byte 59 ; mov eax,sys_execve (5 bytes) - push/pop (2 bytes) pop rax syscall ; SE execve falhar, sai do processo mov edi,eax ; mov eax,60 push byte 60 ; mov eax,sys_exit pop rax ; syscall sh: db `/bin/sh\0` []s Fred
  10. Também não existe. Tente: mov ax,cs mov ds,ax
  11. Ao usar um endereço efetivo com "registradores" de 16 bits você tem que seguir as regras do 8086. O endereço "base" é sempre BX ou BP, nenhum outro e o índice é sempre SI ou DI, nenhum outro. Assim: mov ax,[si][di] não existe, nem mesmo mov ax,[si+di+0].
  12. 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.
  13. Para sua avaliação: #include <stdio.h> #include <stdlib.h> #include <string.h> int saque ( unsigned int *restrict notas, const unsigned int *restrict valores, unsigned int nvalores, unsigned int valor ) { // Zera os contadores. memset ( notas, 0, nvalores * sizeof ( int ) ); for ( int i = 0; i < nvalores; i++ ) { // casos especiais // Problemas com R$ 6,00 e R$ 8,00 restantes... if ( valores[i] == 5 ) { // Assume que o último elemento equivale às // notas de R$ 2,00... revise isso se não for o caso. if ( valor == 6 ) { notas[nvalores - 1] = 3; valor = 0; break; } else if ( valor == 8 ) { notas[nvalores - 1] = 4; valor = 0; break; } } while ( valor >= valores[i] ) { notas[i]++; valor -= valores[i]; } } return valor == 0; } int main ( void ) { static const unsigned int valores[7] = { 200, 100, 50, 20, 10, 5, 2 }; unsigned int notas[7]; unsigned int valor; fputs ( "Quantidade desejada: ", stdout ); fflush ( stdout ); if ( scanf ( "%u", &valor ) == 1 ) { if ( ! saque ( notas, valores, 7, valor ) ) { error: fputs ( "Valor inválido.\n", stdout ); return EXIT_FAILURE; } // Mostrar notas. for ( int i = 0; i < 7; i++ ) printf ( "R$ %3u,00 -> %u notas.\n", valores[i], notas[i] ); } else goto error; return EXIT_SUCCESS; } []s Fred
  14. 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.
  15. 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 é?
  16. A bem da verdade, for é a mesma coisa que while, só que abreviado: for (inicialização; condição; incremento) statement; inicializacao; while (condicao) { statement; incremento; }
  17. Sorry... só vi agora... As tabelas de descritores de segmentos são montadas pelo sistema operacional... No caso, é uma estrutura fixa. As regras para essas estruturas podem ser lidas no Intel Software Development Manual, volume 3. Disponível aqui: Intel SDM
  18. Você já está fazendo isso com aqueles dois loops `for`, lá em cima...
  19. Você ainda não entendeu que `char` não é um "caractere"... é um tipo INTEIRO onde, dentro dele CABE o código de um. Experimente isso: printf( "%d\n", 'a' );
  20. O mais complicado você já fez: PS: Sorry.... não precisa do header cstring não...
×
×
  • Criar Novo...