O que é size_t em C?


626

Estou me confundindo com size_tC. Sei que ele é retornado pelo sizeofoperador. Mas o que exatamente é isso? É um tipo de dados?

Digamos que eu tenho um forloop:

for(i = 0; i < some_size; i++)

Devo usar int i;ou size_t i;?


11
Se essas são suas únicas opções, use intse some_sizeestiver assinado, size_tse não estiver assinado.
Nate

8
@ Nate Isso está incorreto. O POSIX tem um tipo ssize_t, mas o tipo realmente correto a ser usado é ptrdiff_t.
Steven Stewart-Gallus

2
As respostas não são tão claras quanto na programação de baixo nível: C, montagem e execução de programas no Intel® 64 . Conforme declarado no livro, o uso de um índice int ipode não ser suficiente para lidar com uma grande variedade. Portanto, usando size_t ivocê pode endereçar mais índices, mesmo que você tenha uma grande variedade que não deve ser um problema. size_té um tipo de dados: geralmente um, unsigned long intmas isso depende do seu sistema.
bruno

Respostas:


461

Da Wikipedia :

De acordo com a norma ISO C de 1999 (C99), size_té um tipo inteiro não assinado de pelo menos 16 bits (consulte as seções 7.17 e 7.18.3).

size_té um tipo de dados não assinado definido por vários padrões C / C ++, por exemplo, o padrão C99 ISO / IEC 9899, ​​definido em stddef.h. 1 Pode ser importado ainda mais pela inclusão de stdlib.hcomo este arquivo inclui internamente stddef.h.

Este tipo é usado para representar o tamanho de um objeto. As funções de biblioteca que aceitam ou retornam tamanhos esperam que sejam do tipo ou tenham o tipo de retorno size_t. Além disso, o tamanho do operador baseado em compilador usado com mais freqüência deve ser avaliado como um valor constante compatível com size_t.

Como implicação, size_té um tipo garantido para manter qualquer índice de matriz.


4
"Funções de biblioteca que aceitam ou retornam tamanhos esperam que sejam do tipo ... size_t" Exceto que stat () usa off_t para o tamanho de um arquivo
Draemon

64
@ Demra Esse comentário reflete uma confusão fundamental. size_té para objetos na memória. O padrão C nem sequer define stat()ou off_t(essas são definições POSIX) ou nada a ver com discos ou sistemas de arquivos - ele se interrompe nos FILEfluxos. O gerenciamento de memória virtual é completamente diferente dos sistemas de arquivos e do gerenciamento de arquivos no que diz respeito aos requisitos de tamanho, portanto, mencionar off_té irrelevante aqui.
jw013

3
@ jw013: Eu dificilmente chamaria isso de confusão fundamental, mas você faz uma observação interessante. Ainda assim, o texto citado não diz "tamanhos de objetos na memória" e "deslocamento" dificilmente é um bom nome para um tipo de tamanho, independentemente de onde ele esteja armazenado.
Draemon

30
@Draemon Bom ponto. Esta resposta cita a Wikipedia, que neste caso não tem a melhor explicação, na minha opinião. O padrão C em si é muito mais claro: define size_tcomo o tipo do resultado do sizeofoperador (cerca de 7,17p2 <stddef.h>). A Seção 6.5 explica exatamente como as expressões C funcionam (6.5.3.4 para sizeof). Como você não pode aplicar sizeofa um arquivo de disco (principalmente porque C nem define como funcionam os discos e os arquivos), não há espaço para confusão. Em outras palavras, culpe a Wikipedia (e esta resposta por citar a Wikipedia e não o padrão C real).
jw013

2
@ Demra - Eu também concordo com a avaliação da "confusão fundamental". Se você não leu os padrões C / C ++, pode pensar que "objeto" se refere a "programação orientada a objetos", o que não acontece. Leia o padrão C, que não possui nenhum desses objetos OOP, mas ainda possui objetos, e descubra. A resposta pode te surpreender!
Heath Hunnicutt

220

size_té um tipo não assinado. Portanto, não pode representar valores negativos (<0). Você o usa quando está contando algo e tem certeza de que não pode ser negativo. Por exemplo, strlen()retorna a size_tporque o comprimento de uma string deve ser pelo menos 0.

No seu exemplo, se seu índice de loop sempre for maior que 0, pode fazer sentido usar size_tou qualquer outro tipo de dados não assinado.

Ao usar um size_tobjeto, você deve garantir que, em todos os contextos em que ele é usado, incluindo aritmética, deseja valores não negativos. Por exemplo, digamos que você tenha:

size_t s1 = strlen(str1);
size_t s2 = strlen(str2);

e você deseja encontrar a diferença dos comprimentos de str2e str1. Você não pode fazer:

int diff = s2 - s1; /* bad */

Isso ocorre porque o valor atribuído diffsempre será um número positivo, mesmo quando s2 < s1, porque o cálculo é feito com tipos não assinados. Nesse caso, dependendo do seu caso de uso, é melhor usar int(ou long long) para s1e s2.

Existem algumas funções no C / POSIX que podem / devem usar size_t, mas não por motivos históricos. Por exemplo, o segundo parâmetro para fgetsideal deve ser size_t, mas é int.


8
@Alok: Duas perguntas: 1) qual é o tamanho size_t? 2) por que devo preferir size_talgo assim unsigned int?
Lazer

2
@ Lazer: o tamanho de size_té sizeof(size_t). O padrão C garante que SIZE_MAXserá pelo menos 65535. size_té o tipo retornado pelo sizeofoperador e é usado na biblioteca padrão (por exemplo, strlenretornos size_t). Como disse Brendan, size_tnão precisa ser o mesmo que unsigned int.
Alok Singhal

4
@Lazer - sim, size_té garantido que seja um tipo não assinado.
Alok Singhal

2
@ Celeritas não, quero dizer que um tipo não assinado pode representar apenas valores não negativos. Eu provavelmente deveria ter dito "Não pode representar valores negativos".
Alok Singhal

4
@JasonOster, o complemento de dois não é um requisito no padrão C. Se o valor de s2 - s1estourar um int, o comportamento é indefinido.
Alok Singhal

73

size_t é um tipo que pode conter qualquer índice de matriz.

Dependendo da implementação, pode ser um dos seguintes:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

Veja como size_té definido na stddef.hminha máquina:

typedef unsigned long size_t;

4
Certamente typedef unsigned long size_tdepende do compilador. Ou você está sugerindo que é sempre assim?
chux - Restabelece Monica

4
@chux: De fato, apenas porque uma implementação a define como tal, não significa que todos o fazem. Caso em questão: Windows de 64 bits. unsigned longé de 32 bits, size_té de 64 bits.
Tim Čas

2
qual é o propósito de size_t exatamente? Quando eu posso criar uma variável para mim como: "int mysize_t;" ou "long mysize_t" ou "long mysize_t não assinado". Por que alguém deveria ter criado essa variável para mim?
midkin 31/07

1
@midkin size_tnão é uma variável. É um tipo que você pode usar quando quiser representar o tamanho de um objeto na memória.
Arjun Sreedharan

1
é verdade que size_tsempre há 32 bits na máquina de 32 bits e 64 bits da mesma forma?
John Wu

70

Se você é do tipo empírico ,

echo | gcc -E -xc -include 'stddef.h' - | grep size_t

Saída para o Ubuntu 14.04 GCC 4.8 de 64 bits:

typedef long unsigned int size_t;

Observe que stddef.hé fornecido pelo GCC e não glibc src/gcc/ginclude/stddef.hno GCC 4.2.

Aparições C99 interessantes

  • malloctoma size_tcomo argumento, portanto, determina o tamanho máximo que pode ser alocado.

    E como também é retornado por sizeof, acho que limita o tamanho máximo de qualquer matriz.

    Consulte também: Qual é o tamanho máximo de uma matriz em C?


1
Eu tenho o mesmo ambiente, no entanto, eu testei por 32 bits, passando a opção "-m32" do GCC, o resultado foi: "typedef unsigned int size_t". Obrigado por compartilhar este comando incrível @Ciro, isso me ajudou muito! :-)
silvioprog 21/03

2
O assunto em si não é confuso. É a mente confusa que tenta fazer muitas perguntas e dar muitas respostas. Surpreende-me que esta resposta e a de Arjun Sreedharan ainda não impeçam as pessoas de perguntar e responder.
biocyberman

1
Ótima resposta, porque na verdade diz o que size_té , pelo menos em uma distribuição Linux popular.
Andrey Portnoy


19

Como ninguém ainda o mencionou, a principal significância linguística de size_té que o sizeofoperador retorna um valor desse tipo. Da mesma forma, o significado primário de ptrdiff_té que subtrair um ponteiro de outro produzirá um valor desse tipo. As funções de biblioteca que o aceitam fazem isso porque permitirão que essas funções funcionem com objetos cujo tamanho exceda UINT_MAX em sistemas onde esses objetos possam existir, sem forçar os chamadores a desperdiçarem o código que passa um valor maior que "int não assinado" em sistemas onde o tipo maior seria suficiente para todos os objetos possíveis.


Minha pergunta sempre foi: se sizeof nunca existisse, haveria necessidade de size_t?
decano P

@ DeanP: Talvez não, embora houvesse uma pergunta de que tipo de argumento deve ser usado para coisas como malloc(). Pessoalmente, eu gostaria de ter visto versões que aceitam argumentos do tipo int, longe long long, com algumas implementações promovendo tipos mais curtos e outras implementando, por exemplo, lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}[em algumas plataformas, chamar imalloc(123)seria mais barato do que chamar lmalloc(123);, e até mesmo em uma plataforma onde size_t16 pedaços, código que quer alocar tamanho calculado em um `valor long` ...
supercat

... deve poder contar com a falha na alocação se o valor for maior do que o alocador pode suportar.
Supercat

11

Para explicar por que é size_tnecessário existir e como chegamos aqui:

Em termos pragmáticos, size_te ptrdiff_tsão garantidos para ser 64 bits de largura em uma implementação de 64 bits, 32 bits de largura em uma implementação de 32 bits, e assim por diante. Eles não podiam forçar nenhum tipo existente a significar isso, em todos os compiladores, sem quebrar o código legado.

Um size_tou ptrdiff_tnão é necessariamente o mesmo que um intptr_tou uintptr_t. Eles eram diferentes em algumas arquiteturas que ainda estavam em uso quando size_te ptrdiff_tforam adicionados ao padrão no final dos anos 80, e tornando-se obsoleto quando C99 acrescentou muitos novos tipos, mas ainda não passaram (como Windows de 16 bits). O x86 no modo protegido de 16 bits tinha uma memória segmentada em que a maior matriz ou estrutura possível podia ter apenas 65.536 bytes de tamanho, mas um farponteiro precisava ter 32 bits de largura, mais largo que os registros. Nesses, intptr_tteria 32 bits de largura, mas size_teptrdiff_tpode ter 16 bits de largura e caber em um registro. E quem sabia que tipo de sistema operacional poderia ser escrito no futuro? Em teoria, a arquitetura i386 oferece um modelo de segmentação de 32 bits com ponteiros de 48 bits que nenhum sistema operacional jamais usou.

O tipo de deslocamento de memória não pode ser longporque muito código legado supõe que longseja exatamente 32 bits de largura. Essa suposição foi incorporada às APIs do UNIX e do Windows. Infelizmente, muitos outros códigos herdados também assumiram que a longé grande o suficiente para conter um ponteiro, um deslocamento de arquivo, o número de segundos decorridos desde 1970 e assim por diante. O POSIX agora fornece uma maneira padronizada de forçar a última suposição a ser verdadeira, em vez da anterior, mas nenhuma é uma suposição portátil a ser feita.

Não poderia ser intporque apenas um punhado de compiladores nos anos 90 tinham int64 bits de largura. Então eles realmente ficaram estranhos mantendo long32 bits de largura. A próxima revisão do Padrão declarou ilegal intque fosse maior do que long, mas intainda tem 32 bits de largura na maioria dos sistemas de 64 bits.

Não poderia ser long long int, o que de qualquer maneira foi adicionado mais tarde, pois foi criado para ter pelo menos 64 bits de largura, mesmo em sistemas de 32 bits.

Portanto, era necessário um novo tipo. Mesmo que não fosse, todos esses outros tipos significavam algo diferente de um deslocamento dentro de uma matriz ou objeto. E se houvesse uma lição do fiasco da migração de 32 para 64 bits, seria específico sobre quais propriedades um tipo precisava ter, e não usar uma que significasse coisas diferentes em programas diferentes.


Não concorde com " size_te tenha a ptrdiff_tgarantia de ter 64 bits de largura em uma implementação de 64 bits" etc. A garantia é exagerada. O intervalo de size_té impulsionado principalmente pela capacidade de memória da implementação. "uma implementação de n bits" é principalmente a largura nativa do processador de números inteiros. Certamente muitas implementações usam um tamanho de memória e largura de barramento de processador semelhantes, mas existem inteiros nativos amplos com memória insuficiente ou processadores estreitos com muita memória e separam essas duas propriedades de implementação.
chux - Restabelece Monica em 23/01

8

size_te intnão são intercambiáveis. Por exemplo, no Linux de size_t64 bits, o tamanho é de 64 bits (ou seja sizeof(void*)), mas inté de 32 bits.

Observe também que size_tnão está assinado. Se você precisar de uma versão assinada, existe ssize_tem algumas plataformas e isso seria mais relevante para o seu exemplo.

Como regra geral, eu sugeriria o uso intpara a maioria dos casos gerais e somente o uso size_t/ ssize_tquando houver uma necessidade específica (com, mmap()por exemplo).


3

Em geral, se você está começando em 0 e subindo, sempre use um tipo não assinado para evitar um estouro levando você a uma situação de valor negativo. Isso é extremamente importante, porque se os limites de sua matriz forem inferiores ao máximo do seu loop, mas o loop máximo for superior ao máximo do seu tipo, você contornará negativo e poderá ocorrer uma falha de segmentação (SIGSEGV ) Portanto, em geral, nunca use int para um loop começando em 0 e subindo. Use um não assinado.


3
Não posso aceitar sua argumentação. Você diz que é melhor que o bug de estouro silenciosamente leve ao acesso a dados válidos em sua matriz?
MAF-soft

1
@ maf-soft está correto. se o erro não for detectado, será pior do que uma falha no programa. por que essa resposta foi votada?
yoyo_fun

Se ele acessar dados válidos em sua matriz, não será um erro, porque o tipo não assinado não excederá o limite do tipo assinado. O que é essa lógica pessoal? Digamos que, por algum motivo, você use char para iterar mais de 256 elementos do array ... assinado irá exceder em 127 e 128 o elemento será sigsegv, mas se você usar não assinado, ele passará por todo o array conforme o esperado. Então, novamente, quando você estiver usando um int, suas matrizes não vai ser muito maior do que 2 bilhões de elementos para que de qualquer forma não importa ...
roxo Ice

1
Não consigo imaginar nenhuma situação em que o excesso de número inteiro não seja um erro, seja positivo ou negativo. Só porque você não recebe um segfault não significa que você vê o comportamento correto! E você pode experimentar uma falha de segmentação, ou não, se seu deslocamento é positivo ou negativo; tudo depende do seu layout de memória. @ PurpleIce, não acho que você esteja dizendo a mesma coisa que esta resposta; seu argumento parece ser que você deve escolher um tipo de dados grande o suficiente para armazenar o maior valor que deseja inserir nele, o que é apenas senso comum.
Soren Bjornstad 01/04/19

Dito isto, prefiro usar um tipo não assinado para índices de loop semântica ; se sua variável nunca for negativa, você também pode indicar isso no tipo que escolher. Também poderia permitir que o compilador localizasse um bug em que o valor acabasse negativo, embora o GCC pelo menos seja bastante terrível em detectar esse erro específico (em uma ocasião eu inicializei um sem sinal para -1 e não recebi um aviso). Da mesma forma, um size_t é semanticamente apropriado para índices de matriz.
Soren Bjornstad 01/04/19

3

size_t é um tipo de dados inteiro não assinado. Nos sistemas que usam a GNU C Library, isso será int sem sinal ou int longo sem sinal. size_t é comumente usado para indexação de array e contagem de loop.


1

size_t ou qualquer tipo não assinado pode ser visto como variável de loop, pois as variáveis ​​de loop geralmente são maiores ou iguais a 0.

Quando usamos um objeto size_t , precisamos garantir que, em todos os contextos em que ele é usado, incluindo aritmética, desejemos apenas valores não negativos. Por exemplo, o programa a seguir daria definitivamente o resultado inesperado:

// C program to demonstrate that size_t or
// any unsigned int type should be used 
// carefully when used in a loop

#include<stdio.h>
int main()
{
const size_t N = 10;
int a[N];

// This is fine
for (size_t n = 0; n < N; ++n)
a[n] = n;

// But reverse cycles are tricky for unsigned 
// types as can lead to infinite loop
for (size_t n = N-1; n >= 0; --n)
printf("%d ", a[n]);
}

Output
Infinite loop and then segmentation fault

1

size_té um tipo de dados inteiro não assinado que pode atribuir apenas 0 e valores maiores que 0. Ele mede bytes do tamanho de qualquer objeto e retorna pelo sizeofoperador. consté a representação da sintaxe de size_t, mas sem constvocê pode executar o programa.

const size_t number;

size_tusado regularmente para indexação de array e contagem de loop. Se o compilador é 32-bitque iria trabalhar unsigned int. Se o compilador é 64-bitque iria trabalhar unsigned long long inttambém. Existe para o tamanho máximo de size_tacordo com o tipo de compilador.

size_tjá definem no <stdio.h>arquivo de cabeçalho, mas também pode definir por <stddef.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>cabeçalhos.

  • Exemplo (com const)
#include <stdio.h>

int main()
{
    const size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Resultado -: size = 800


  • Exemplo (sem const)
#include <stdio.h>

int main()
{
    size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Resultado -: size = 800


-3

Do meu entendimento, size_té um unsignednúmero inteiro cujo tamanho de bit é grande o suficiente para armazenar um ponteiro da arquitetura nativa.

Assim:

sizeof(size_t) >= sizeof(void*)

16
Não é verdade. O tamanho do ponteiro pode ser maior que o size_t. Vários exemplos: compiladores C no modo real x86 podem ter 32 bits FARou HUGEponteiros, mas size_t ainda é 16 bits. Outro exemplo: o Watcom C costumava ter um ponteiro de gordura especial para memória estendida com 48 bits de largura, mas size_tnão tinha. No controlador incorporado com arquitetura Harvard, você também não tem correlação, porque ambos dizem respeito a diferentes espaços de endereço.
Patrick Schlüter

1
E nessa stackoverflow.com/questions/1572099/... há mais exemplos AS / 400 com 128 ponteiros bit e 32 bitsize_t
Patrick Schlüter

Isso é flagrantemente falso. No entanto, vamos mantê-lo aqui
Antti Haapala
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.