2
respostas

Dúvidas: Ponteiros, alocação dinâmica, sizeof

Olá, estou com algumas dúvidas que envolvem ponteiros, alocação dinâmica de memória e o operador sizeof.

Estava tudo bem enquanto estava experimentando os conceitos com os seguintes testes:

#include <stdio.h>
#include <stdlib.h>
#define TAMANHO 5

int main()
{
    int array[TAMANHO] = {10, 20, 30, 40, 50};

    // Verificando o tamanho que um inteiro ocupa
    printf("sizeof(int) = %lu\n", sizeof(int)); // Saída: sizeof(int) = 4

    // Usando sizeof para verificar o tamanho que o array ocupa em bytes
    printf("sizeof(array) = %lu\n", sizeof(array)); // Saída: sizeof(array) = 20 

    // Multiplicando o tamanho de um inteiro pela quantidade de elementos do array,
    // para confirmar que a expressao anterior está correta.
    printf("sizeof(int) * TAMANHO = %lu\n", sizeof(int) * TAMANHO); // Saída: sizeof(int) * TAMANHO = 20

    // Dividir o tamanho que o array ocupa em bytes, pelo tamanho da primeira posição do array
    // Também funciona para descobrir quantas posições o array possui.
    printf("sizeof(array) / sizeof(array[0]) = %lu\n", sizeof(array) / sizeof(array[0])); // Saída: sizeof(array) / sizeof(array[0]) = 20

    return 0;
}

Até que resolvi usar alocação dinâmica para trabalhar com os arrays:

#include <stdio.h>
#include <stdlib.h>
#define TAMANHO 5

int main()
{
    int *array = malloc(sizeof(int) * TAMANHO);

    //Tentando determinar quanto espaço eu aloquei na memória
    printf("sizeof(array) = %lu\n", sizeof(array)); // Saída: sizeof(array) = 8 (Não era o que eu esperava)

    free(array);
    return 0;
}

Gostaria de confirmar se as seguintes afirmações estão corretas:

  • sizeof é um operador que faz avaliações em tempo de compilação e o compilador não tem como saber, nesse caso, o quanto de espaço foi alocado em tempo de execução;
  • Um ponteiro aponta para o primeiro byte de todo o espaço alocado, e o programa não tem como saber onde esse espaço termina. Portanto sizeof() retorna o tamanho do ponteiro em si.
  • É impossível descobrir quantas posiçoes um array possui, se a única referência que eu tenho para ele é um ponteiro apontando para o primeiro byte?

Continuando, tentei criar um array com a sintaxe clássica, mas com número de elementos determinado pelo usuário:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int tamanhoarray;
    scanf("%d", &tamanhoarray); // Assumindo que o usuário digitou 5.

    // Como é possível que isso possa ser feito sem malloc?
    int array[tamanhoarray];

    // Usando sizeof com um array cuja quantidade de elementos foi definida em tempo de execução.
    printf("Tamanho do array em bytes: %lu\n", sizeof(array)); // Saída: Tamanho do array em bytes: 20 

    // Descobrindo quantas posições o array possui:
    printf("Numero de posicoes: %lu\n", sizeof(array) / sizeof(array[0])); // Saída: Numero de posicoes: 5 
    return 0;
}

Dúvidas sobre esse bloco:

  • Como é possível alocar um array com tamanho desconhecido em tempo de compilação, sem o uso de malloc?
  • Se a afirmação de que sizeof é um operador de tempo de compilação está correta, como é possível que ele tenha conseguido medir esse array?
  • A divisão aparentemente só não funciona se usar ponteiro. Em qualquer outra situação funciona?
  • Tirando o caso de usar variável global, para que usar malloc, sendo que o último bloco funciona?
2 respostas

Olá, Ítalo! Vamos esclarecer suas dúvidas uma por uma.

Primeiramente, você está correto em suas afirmações sobre o operador sizeof e a alocação dinâmica de memória. O sizeof é um operador de tempo de compilação, portanto, ele não tem como saber o tamanho de um bloco de memória alocado dinamicamente em tempo de execução. Quando você usa sizeof em um ponteiro, ele retorna o tamanho do ponteiro em si, não do bloco de memória para o qual o ponteiro está apontando. E sim, se a única referência que você tem para um array é um ponteiro para o primeiro elemento, você não tem como descobrir quantos elementos o array tem, a menos que você tenha armazenado essa informação em algum lugar.

Agora, vamos para as suas dúvidas sobre o último bloco de código. O que está acontecendo aqui é que você está usando um recurso do C chamado VLA (Variable Length Array, ou Array de Comprimento Variável). Em C99, você pode declarar arrays cujo tamanho é determinado em tempo de execução. No entanto, isso não é considerado uma boa prática, pois pode levar a problemas de segurança e portabilidade. O sizeof funciona com VLAs porque, mesmo que o tamanho do array seja determinado em tempo de execução, uma vez que o array é declarado, seu tamanho se torna fixo e conhecido.

A divisão sizeof(array) / sizeof(array[0]) funciona para arrays, mas não para ponteiros, porque o sizeof de um ponteiro não é o mesmo que o sizeof de um array. Quando você usa sizeof em um array, ele retorna o tamanho total do array (ou seja, o tamanho de um elemento multiplicado pelo número de elementos). Quando você usa sizeof em um ponteiro, ele retorna o tamanho do ponteiro, não do bloco de memória para o qual o ponteiro está apontando.

Finalmente, a razão para usar malloc em vez de VLAs é que malloc permite um controle mais preciso sobre a memória. Com malloc, você pode alocar e desalocar memória conforme necessário, o que pode ser muito útil em programas grandes e complexos. Além disso, malloc permite a alocação de grandes quantidades de memória que podem não caber na pilha se você tentar alocá-las como um array local.

Espero ter ajudado e bons estudos!

Olá, Matheus.

Obrigado pela resposta. Ajudou a esclarecer todas as minhas dúvidas.

No entanto, oriundas dela, outras dúvidas apareceram:

  • Quais problemas de segurança existem ao utilizar VLA?
  • É correto afirmar que VLAs alocam espaço na stack, enquanto malloc o fazem na heap?
  • Tendo em vista que o C99 existe há 24 anos, seria portabilidade realmente uma questão a se considerar? Digo, qualquer compilador atual já não suporta C99, ou a preocupação seria compilar código em ambientes legados?

Agradeço novamente,

Italo.