Eu tenho escrito coisas como
char *x=NULL;
na suposição de que
char *x=2;
criaria um char
ponteiro para o endereço 2.
Mas, no Tutorial de programação GNU C diz que int *my_int_ptr = 2;
armazena o valor inteiro2
em qualquer endereço aleatório que esteja my_int_ptr
quando for alocado.
Isso parece implicar que o meu próprio char *x=NULL
está atribuindo qualquer valor deNULL
elenco a char
a algum endereço aleatório na memória.
Enquanto
#include <stdlib.h>
#include <stdio.h>
int main()
{
char *x=NULL;
if (x==NULL)
printf("is NULL\n");
return EXIT_SUCCESS;
}
na verdade, imprime
é nulo
quando eu o compilo e executo, fico preocupado em estar contando com um comportamento indefinido, ou pelo menos um comportamento subespecificado, e que devo escrever
char *x;
x=NULL;
em vez de.
c
pointers
initialization
fagricipni
fonte
fonte
int *x = whatever;
faz e o queint *x; *x = whatever;
faz.int *x = whatever;
realmente se comporta comoint *x; x = whatever;
, não*x = whatever;
.Respostas:
TL; DR Sim, muito.
A afirmação real feita no guia parece
Bem, eles estão errados, você está certo.
Para a declaração, ( ignorando, por enquanto, o fato de que o ponteiro para a conversão de inteiro é um comportamento definido pela implementação )
my_int_ptr
é uma variável (do tipo ponteiro paraint
), tem um endereço próprio (tipo: endereço do ponteiro para inteiro), você está armazenando um valor de2
em que endereço.Agora,
my_int_ptr
sendo um tipo de ponteiro, podemos dizer que ele aponta para o valor de "tipo" no local da memória apontado pelo valor mantidomy_int_ptr
. Então, você está essencialmente atribuindo o valor de variável do ponteiro, não o valor da localização da memória apontada pelo ponteiro.Então, para conclusão
inicializa a variável de ponteiro
x
paraNULL
, não o valor no endereço de memória apontado pelo ponteiro .Este é o mesmo que
Expansão:
Agora, sendo estritamente conforme, uma declaração como
é ilegal, pois envolve violação de restrição. Para ser claro,
my_int_ptr
é uma variável de ponteiro, tipoint *
2
tem tipoint
, por definição.e eles não são tipos "compatíveis", então esta inicialização é inválida porque está violando as regras de atribuição simples, mencionadas no capítulo §6.5.16.1 / P1, descrito na resposta de Lundin .
Caso alguém esteja interessado em como a inicialização está ligada a restrições de atribuição simples, citando
C11
, capítulo §6.7.9, P11fonte
2
é umint
, a atribuição é um problema. Mas é mais do que isso.NULL
também pode ser umint
, umint 0
. É só quechar *x = 0;
está bem definido echar *x = 2;
não está. 6.3.2.3 Ponteiros 3 (BTW: C não define um literal inteiro , apenas literal de string e literal composto .0
É uma constante inteira )char *x = (void *)0;
, em se conformar? ou é apenas com outras expressões que produz o valor0
?0
são especiais: elas convertem implicitamente em ponteiros nulos separadamente das regras usuais para converter explicitamente expressões inteiras gerais para tipos de ponteiro.int *p = somePtrExpression
é IMHO um tanto horrível, pois parece que está definindo o valor de,*p
mas na verdade está definindo o valor dep
.O tutorial está errado. No ISO C,
int *my_int_ptr = 2;
é um erro. No GNU C, significa o mesmo queint *my_int_ptr = (int *)2;
. Isso converte o inteiro2
em um endereço de memória, de alguma forma conforme determinado pelo compilador.Ele não tenta armazenar nada no local endereçado por esse endereço (se houver). Se você continuasse a escrever
*my_int_ptr = 5;
, ele tentaria armazenar o número5
no local endereçado por esse endereço.fonte
Para esclarecer porque o tutorial está errado,
int *my_int_ptr = 2;
é uma "violação de restrição", é um código que não tem permissão para compilar e o compilador deve fornecer um diagnóstico ao encontrá-lo.Conforme 6.5.16.1 Atribuição simples:
Nesse caso, o operando esquerdo é um ponteiro não qualificado. Em nenhum lugar ele menciona que o operando correto pode ser um número inteiro (tipo aritmético). Portanto, o código viola o padrão C.
O GCC é conhecido por se comportar mal, a menos que você diga explicitamente que ele é um compilador C padrão. Se você compilar o código como
-std=c11 -pedantic-errors
, ele dará um diagnóstico correto como deve ser feito.fonte
void *
, é chamada de constante de ponteiro nula”. Observe o penúltimo ponto em sua cotação. Portanto,int* p = 0;
é uma forma legal de escreverint* p = NULL;
. Embora o último seja mais claro e convencional.int m = 1, n = 2 * 2, * p = 1 - 1, q = 2 - 1;
legal também.intptr_t
explicitamente para um dos tipos permitidos no lado direito. Ou seja,void* a = (void*)(intptr_t)b;
é válido no ponto 4, mas(intptr_t)b
não é um tipo de ponteiro compatível, nem avoid*
, nem uma constante de ponteiro nula evoid* a
não é um tipo aritmético nem_Bool
. O padrão diz que a conversão é legal, mas não que seja implícita.int *my_int_ptr = 2
Isso está completamente errado. Se isto estiver realmente escrito, por favor, obtenha um livro ou tutorial melhor.
int *my_int_ptr = 2
define um ponteiro inteiro que aponta para o endereço 2. Você provavelmente terá uma falha se tentar acessar o endereço2
.*my_int_ptr = 2
, ou seja, sem oint
na linha, armazena o valor dois em qualquer endereço aleatório para o qualmy_int_ptr
esteja apontando. Tendo dito isso, você pode atribuirNULL
a um ponteiro quando ele estiver definido.char *x=NULL;
é perfeitamente válido C.Edit: Enquanto escrevia isso, eu não sabia que a conversão de inteiro para ponteiro é um comportamento definido pela implementação. Por favor, veja as boas respostas de @MM e @SouravGhosh para detalhes.
fonte
Muita confusão sobre os ponteiros C vem de uma escolha muito ruim que foi feita originalmente em relação ao estilo de codificação, corroborada por uma pequena escolha muito ruim na sintaxe da linguagem.
int *x = NULL;
está correto C, mas é muito enganoso, eu diria até sem sentido, e tem dificultado o entendimento da língua para muitos novatos. Isso nos faz pensar que mais tarde poderíamos fazer o*x = NULL;
que é obviamente impossível. Veja, o tipo da variável não éint
, e o nome da variável não*x
, nem o*
na declaração desempenha qualquer papel funcional em colaboração com o=
. É puramente declarativo. Então, o que faz muito mais sentido é o seguinte:int* x = NULL;
que também é C correto, embora não adira ao estilo de codificação original K&R. Isso torna perfeitamente claro que o tipo éint*
, e a variável de ponteiro éx
, portanto, torna-se evidente até mesmo para os não iniciados que o valorNULL
está sendo armazenado emx
, que é um ponteiro paraint
.Além disso, torna mais fácil derivar uma regra: quando a estrela está fora do nome da variável, então é uma declaração, enquanto a estrela sendo anexada ao nome é a desreferenciação do ponteiro.
Então, agora fica muito mais compreensível que mais adiante podemos fazer
x = NULL;
ou,*x = 2;
em outras palavras, torna mais fácil para um novato ver comovariable = expression
leva apointer-type variable = pointer-expression
edereferenced-pointer-variable = expression
. (Para os iniciados, por 'expressão' quero dizer 'rvalue'.)A escolha infeliz na sintaxe da linguagem é que, ao declarar variáveis locais, você pode dizer o
int i, *p;
que declara um inteiro e um ponteiro para um inteiro, o que leva a crer que o*
é uma parte útil do nome. Mas não é, e essa sintaxe é apenas um caso especial peculiar, adicionado por conveniência e, em minha opinião, nunca deveria ter existido, porque invalida a regra que propus acima. Pelo que eu sei, em nenhum outro lugar da linguagem esta sintaxe é significativa, mas mesmo se for, ela aponta para uma discrepância na maneira como os tipos de ponteiro são definidos em C. Em todos os outros lugares, em declarações de variável única, em listas de parâmetros, em membros de estrutura, etc., você pode declarar seus ponteiros como emtype* pointer-variable
vez detype *pointer-variable
; é perfeitamente legal e faz mais sentido.fonte
int *x = NULL; is correct C, but it is very misleading, I would even say nonsensical,
... Eu tenho que concordar para discordar.It makes one think
.... pare de pensar, leia um livro C primeiro, sem ofensa.int* somePtr, someotherPtr
declare duas dicas, na verdade, eu costumava escrever,int* somePtr
mas isso leva ao bug que você descreve.create
vez decreat
. :) A questão é, é como é e precisamos nos moldar para nos adaptar a isso. Tudo se resume a escolha pessoal no final do dia, concordo.Eu gostaria de acrescentar algo ortogonal às muitas respostas excelentes. Na verdade, inicializar em
NULL
está longe de ser uma prática ruim e pode ser útil se esse ponteiro puder ou não ser usado para armazenar um bloco de memória alocado dinamicamente.Visto que de acordo com o padrão ISO-IEC 9899
free
é um nop quando o argumento éNULL
, o código acima (ou algo mais significativo na mesma linha) é legítimo.fonte
void*
é convertido conforme necessário. Mas ter um código que funciona com um compilador C e C ++ pode trazer benefícios.const
ponteiros declarados em res medias , mas mesmo quando um ponteiro precisa ser mutável (como um usado em um loop ou porrealloc()
), configurando-o paraNULL
detectar bugs onde ele foi usado antes é definido com seu valor real. Na maioria dos sistemas, o desreferenciamentoNULL
causa um segfault no ponto de falha (embora haja exceções), enquanto um ponteiro não inicializado contém lixo e a gravação nele corrompe a memória arbitrária.NULL
, mas pode ser muito difícil diferenciar um ponteiro lixo de um válido. Portanto, é útil garantir que todos os ponteiros sejam sempre válidos ouNULL
, desde o momento da declaração.este é um ponteiro nulo
fonte
Isto está certo.
Esta função é correta para o que faz. Ele atribui o endereço de 0 ao ponteiro char x. Ou seja, ele aponta o ponteiro x para o endereço de memória 0.
Alternativo:
Meu palpite sobre o que você queria é:
fonte
char* x = 0; if (x == 0)
será verdadeiro. Os ponteiros não são necessariamente inteiros.