AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79542518
Accepted
user30097526
user30097526
Asked: 2025-03-29 06:30:27 +0800 CST2025-03-29 06:30:27 +0800 CST 2025-03-29 06:30:27 +0800 CST

Alocador de arena C: alinhamento de ponteiro

  • 772

Eu criei um alocador de arena muito simples em C e fiquei pensando em uma coisa.

Aqui está meu arquivo de cabeçalho (a propósito, se esta API não parecer correta, por favor me avise!).

#ifndef ARENA_H
#define ARENA_H

#include <stddef.h>

struct Arena;

/* Returns a new arena allocator with a capacity of SIZE */
struct Arena *arena_new(size_t size);
/* Allocates SIZE bytes of memory from ARENA aligned with a default alignment
   set to `__alignof__(max_align_t)`. */
void *arena_alloc(struct Arena *arena, size_t size);
/* Allocates SIZE bytes of memory from ARENA with no alignment */
void *arena_alloc_packed(struct Arena *arena, size_t size);
/* Allocates SIZE bytes of memory from ARENA aligned on ALIGN. */
void *arena_alloc_align(struct Arena *arena, size_t size, size_t align);
/* Free all memory allocated with ARENA */
void arena_free(struct Arena *arena);

#endif /* ARENA_H */

Na implementação, arena_alloc_aligncostumo uintptr_tconverter o endereço do ponteiro do bloco de memória para um tipo com o qual posso calcular o alinhamento do endereço.

void *arena_alloc_align(struct Arena *arena, size_t size, size_t align)
{
    assert((align & 1) == 0 && "aligment must be a power of two");

    uintptr_t curr_addr = (uintptr_t)arena->mem_block + arena->cursor;
    size_t padding = 0;

    if (align > 0 && !IS_ALIGNED(curr_addr, align))
        padding = align - (curr_addr & (align - 1));

    if(arena->cursor + padding + size > arena->capacity)
        return NULL;

    arena->cursor = padding + size;
    return (void *)curr_addr + padding;
}

Esse tipo ( uintptr_t) é ok para usar? Tipo, ele é portátil? Porque pelo que eu li, esse tipo é opcional , isso significa que em alguma implementação de compilador ele pode não ser implementado?

Se sim, que tipo posso usar para converter um ponteiro para um tipo que seja grande o suficiente para conter qualquer endereço de memória?

Encontrei algumas outras perguntas relacionadas, mas as pessoas não parecem dar uma alternativa que seja definida no padrão e que não seja opcional. (Posso ter perdido a resposta que estava procurando).

c
  • 2 2 respostas
  • 129 Views

2 respostas

  • Voted
  1. Best Answer
    Andreas Wenzel
    2025-03-29T08:08:17+08:002025-03-29T08:08:17+08:00

    Esse tipo ( uintptr_t) é ok para usar? Tipo, é portátil? Porque pelo que eu li, esse tipo é opcional

    Isso está correto. De acordo com §7.22.1.4 do padrão C23 , o tipo de dado uintptr_té opcional.

    isso significa que em alguma implementação do compilador ele não pode ser implementado?

    Isso significa que um compilador não é obrigado a suportá-lo, então é teoricamente possível que ele não seja implementado em um determinado compilador. No entanto, não tenho conhecimento de nenhum compilador moderno que não o suporte.

    Se um compilador decide não oferecer suporte uintptr_ta uma determinada plataforma, provavelmente é uma plataforma muito antiga ou exótica e provavelmente há uma razão técnica especial para não oferecer suporte a ela.

    Por exemplo, na década de 1980, era comum que as CPUs suportassem apenas aritmética de 16 bits e tivessem apenas um tamanho de registro geral de 16 bits. Mas com 16 bits, você só pode endereçar até 64 KiB de memória. Portanto, para que essas CPUs de 16 bits pudessem endereçar mais de 64 KiB de memória, essas CPUs tinham que usar um modelo de memória segmentada em vez de um modelo de memória plana (que as CPUs modernas usam), o que significa que vários registros de 16 bits tinham que ser usados ​​para armazenar um endereço.

    Para implementar o tipo de dado inteiro uintptr_tem uma plataforma de 16 bits com endereços de memória maiores que 16 bits, o compilador precisaria implementar um tipo de dado inteiro maior que 16 bits, apesar de tal tipo de dado não ser suportado diretamente pela CPU. O compilador poderia, por exemplo, implementar um tipo de dado de 32 bits em software, fazendo com que esse tipo de dado usasse dois registradores de CPU de 16 bits e fazendo com que operações aritméticas nesse tipo de dado de 32 bits usassem internamente várias operações aritméticas de 16 bits.

    Por outro lado, o compilador poderia simplesmente decidir não suportar tal tipo de dado de 32 bits, e suportar apenas tipos de dados de até 16 bits (que são suportados diretamente pela CPU). Nesse caso, o compilador não definiria o tipo de dado uintptr_t, e o compilador ainda estaria em conformidade com o padrão C.

    Este exemplo da década de 1980 talvez não seja um bom exemplo, porque o tipo de dado uintptr_tsó foi introduzido na linguagem C no padrão C99 (que é do ano de 1999), e este padrão também introduziu o tipo de dado long long int, que é necessário ter uma largura de pelo menos 64 bits. No entanto, este exemplo histórico demonstra que pode haver razões técnicas pelas quais um compilador não gostaria de ser obrigado a implementar o tipo de dado uintptr_t, o que é provavelmente o motivo pelo qual o comitê de padrões decidiu tornar este tipo de dado opcional.

    Se sim, que tipo posso usar para converter um ponteiro para um tipo que seja grande o suficiente para conter qualquer endereço de memória?

    Se o compilador decidir não suportar o tipo de dado uintptr_t, então, como descrito acima, isso provavelmente significa que há uma razão técnica especial pela qual não é trivial converter um ponteiro para um inteiro. Em tais situações, é altamente improvável que você consiga resolver o problema simplesmente usando outro tipo de dado que o compilador implementa. Em vez disso, você provavelmente terá que determinar a razão pela qual o tipo de dado uintptr_tnão é suportado nesta plataforma específica e, então, agir de acordo.

    Por outro lado, se a única razão pela qual você quer usar o tipo de dado uintptr_té para determinar o alinhamento, então usar um tipo de dado menor como unsigned intpode ser suficiente. Mas vale a pena notar que mesmo ao usar o tipo de dado uintptr_t, o padrão C não garante que um número par represente um endereço de memória com um alinhamento de 2(mesmo que isso provavelmente seja uma suposição geralmente segura). A única garantia que o padrão fornece é que uintptr_té um "tipo inteiro sem sinal" (o que significa que operações aritméticas podem ser executadas neste tipo de dado) e que converter um void *valor para uintptr_te de volta produzirá um valor de ponteiro que compara igual ao valor do ponteiro original.

    Entretanto, como já foi dito acima, é improvável que tudo isso seja um problema na prática, exceto em plataformas muito antigas ou exóticas.

    • 7
  2. Eric Postpischil
    2025-03-29T07:59:06+08:002025-03-29T07:59:06+08:00

    Este tipo ( uintptr_t) pode ser usado?

    Sim.

    Tipo, é portátil?

    Não. Sim.

    Porque pelo que li, esse tipo é opcional , isso significa que em alguma implementação do compilador ele pode não ser implementado?

    Sim.

    Se sim, que tipo posso usar para converter um ponteiro para um tipo que seja grande o suficiente para conter qualquer endereço de memória?

    Usar uintptr_t.

    Não há uma maneira totalmente portátil de escrever um alocador de memória geral em C; ele deve usar pré-requisitos além do que o padrão C fornece, em relação à manipulação de endereços, conversões de ponteiros, aliasing de tipos e mais. Usar uintptr_té um requisito muito modesto.

    Um alocador de memória geral deve suportar alocação de espaços para objetos de diferentes tamanhos e requisitos de alinhamento. O padrão C não fornece adequadamente maneiras totalmente portáteis de testar e gerenciar alinhamento. (Algo bruto e inadequado pode ser feito definindo o pool de memória como uma grande matriz de caracteres com um requisito de alinhamento máximo, e então os alinhamentos podem ser gerenciados como deslocamentos dentro dessa matriz. No entanto, se uma matriz for definida com um tipo de elemento de caractere, então usá-la para outros tipos, como os clientes do alocador farão, viola as regras de aliasing em C 2024 6.5.)

    Algum alocador de memória estritamente limitado que lida apenas com tipos predeterminados pode ser possível em código estritamente conforme. Além disso, qualquer alocador de memória dependerá de recursos de implementação. Usar uintptr_té uma escolha razoável porque é amplamente fornecido e, se não for, geralmente pode ser definido como o maior tipo inteiro sem sinal. E não há alternativa melhor.

    • 4

relate perguntas

  • Multiplicação mais rápida que *

  • Usando uma macro para comprimento de string no especificador de formato scanf () em C

  • Como você pode definir o tipo de dados de #define para long double?

  • Ponteiros const incompatíveis

  • Mudança de cor não gradual no OpenGL

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve