Ir para o conteúdo
AJBM

[Resolvido] Apontadores e Strings

Mensagens Recomendadas

AJBM

Boas!

Eu tenho o seguinte código.

char *copia(char *dest, char *ori) {
char *tmp = dest;
while (*dest++ = *ori++);
return tmp;
}
int main(int argc, char** argv) {
char name[] = "Nome";
char tmp[4];
copia(tmp, name);
printf("%s", tmp);
return (EXIT_SUCCESS);
}

A minha duvida é como é que o apontador ori sabe onde é o fim do vector nome?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pikax

esse codigo e' inseguro, por causa do buffer overflow.

Em C o final de um array de caracteres e' o '\0' e apos isso normalmente e' seguido por um NULL, em C um NULL e' considerado um 0, while(0) e' falso, por isso sai do ciclo.

EDIT: o \0 e' um NULL

Editado por pikax

Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
AJBM

ok.

EDIT: o \0 e' um NULL

Tens a certeza?

o NULL representa o endereço de memoria nº zero , e '\0' representa o carácter cujo o código ASCII é 0.

Penso que são coisas diferentes.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pikax

Tens a certeza?

o NULL representa o endereço de memoria nº zero , e '\0' representa o carácter cujo o código ASCII é 0.

Penso que são coisas diferentes.

Sim, o NULL em C e' 0, o \0 e' diferente ao caracter 0!

mas ve por ti proprio:

#include<stdio.h>
int main()
{
 if('0'=='\0')
printf("iguais");
 else
printf("diferentes");
}

EDIT: http://ideone.com/fpCTJI

Editado por pikax

Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pikax

Em C os caracteres sao tratados como numeros, e' por isso que: '9'-'0' = 9

O valor 0 da tabela ASCI e' o 0 num inteiro, o NULL e' uma macro do 0... e' por isso que C++ adicionou a keyword nullptr devido que o 0 nao devera' ser considerado NULL, mas isso e' em C++


Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
AJBM

acho que o AJBM não se referiu ao '0' - ascii 48, mas sim ao ASCII 0

Exato.

O que eu queria dizer e´que o null representa um endereço de memoria 0, e o \0 representa o valor 0, os valores são iguais mas significam coisas diferentes.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nelsonr

Em C os caracteres sao tratados como numeros, e' por isso que: '9'-'0' = 9

O valor 0 da tabela ASCI e' o 0 num inteiro, o NULL e' uma macro do 0... e' por isso que C++ adicionou a keyword nullptr devido que o 0 nao devera' ser considerado NULL, mas isso e' em C++

Sim eu sei, mas estava-me a referir ao teu exemplo, que usas o '0' - ascii 48 no teste sobre o que o AJBM disse, mas ele nunca se referiu a esse caracter

Editado por nelsonr

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pikax

Sim eu sei, mas estava-me a referir ao teu exemplo, que usas o '0' - ascii 48 no teste sobre o que o AJBM disse, mas ele nunca se referiu a esse caracter

abre o exemplo do IDEone, tem la' esta linha de codigo:

printf("\n%s",NULL==0?"null==0":"null!=0");

Editado por pikax

Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
HappyHippyHippo

Exato.

O que eu queria dizer e´que o null representa um endereço de memoria 0, e o \0 representa o valor 0, os valores são iguais mas significam coisas diferentes.

0xffffffff é 4294967295 ou -1 ? qual é a diferença ?


IRC : sim, é algo que ainda existe >> #p@p

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
AJBM

Eu vi isto no livro de Luis Damas.

Null e um outro nome para terminador de strings \0.

Falso, NULL- representa o endereço de memoria numero 0.

'\0' -representa o carater cujo o codigo asci e 0.

Eu o que tiro disto é que null e '\0' são coisas diferentes, apesar de o valor em si ser igual.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pikax

para o compilador de C e' a mesma coisa, mas pode causar confusao a outro programador.


Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

Conclusão importante para o OP: o ciclo while termina porque uma atribuição devolve em si mesma um valor, apesar de este ser geralmente descartado. No entanto, dentro de um ciclo como o while, o ciclo termina quando é feita a atribuição do valor 0 ou \0 (pormenores já debatidos acima).

Portanto, quando atribuímos '\0' a *dest, o ciclo vai receber por sua vez o \0, e termina.

É assim que se sabe quando é que a string chegou ao fim: aproveitando o facto de uma atribuição retornar um valor per se, valor este igual ao último valor atribuído.


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
AJBM

Ja agora aproveito para tirar outra duvida.

char name[4 + 1], *ptr;
gets(name);
ptr = name;
printf("%s %s", name, ptr);

se o name for joao

O resultado do printf e joao joao.

Supostamente não devia ser joao e o endereço de memoria de name?

Se eu puser %d da-me o endereço de memoria %s da-me o valor para que o ptr esta apontar.

Alguém me pode dizer porque?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

Sabes que o printf e o scanf recebem como primeiro argumento a formatação do que deve ser escrito e lido, respectivamente, certo?

Então, se ptr é um apontador, deverá ser indicado no printf %p (de pointer), e não %s, que é para Strings, nem %d, que é para inteiros.


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

O valor literal 0 (zero) tem significado diferente consoante o contexto no qual é encontrado. Assim:

int *ptr = 0;  // null pointer
char c   = 0;  // ASCII #0, ou '\0'
int  i   = 0;  // inteiro 0
double d = 0;  // double 0

int *ptr2 = '\0';  // também funciona; null pointer
int j     = '\0';  // também funciona; inteiro 0
double f  = '\0';  // também funciona; double 0

Embora em todos estes casos valor seja zero, a sua representação interna (o bit pattern em memória) não é necessariamente zero. A conversão de valor literal para bit pattern é da responsabilidade do compilador. Mas a representação interna não nos interessa; o que nos interessa é o que ela significa: 0.

NULL e '\0' são sempre garantidamente iguais, uma vez que '\0' é basicamente 0 (zero), e a comparação NULL == 0 é garantida pelo standard como sempre verdadeira, independentemente da representação interna de NULL. Logo, é como o HappyHippyHippo já tentou indicar: tudo depende de como vemos o problema, mas o valor é o mesmo.

A decisão de utilizar NULL, '\0', ou 0 é puramente estilística, mas com uma nuance: nos sistemas onde NULL é definida como ((void*)0) (por oposição a apenas 0), a atribuição de NULL a uma variável simples (ou seja, não-apontador) deverá emitir um aviso:

int zero = NULL;  // warning: initialization makes integer from pointer without a cast

Este aviso deve-se à cast que envolve o zero na definição de NULL -- o compilador passa a saber que os tipos de dados não se alinham (int != void*).

No caso dos tipos de dados de vírgula flutuante (float, double) o caso é pior; é impossível utilizar um destes tipos de dados para armazenar um endereço de memória, e como tal o programa nem compila:

double zero = NULL;  // error: incompatible types when initializing type 'double' using type 'void *'

Assim, fica aqui a dica: utilizem os símbolos (ou seja, constantes, macros, etc) apropriados consoante o contexto: NULL no contexto de apontadores, '\0' em caso de caracteres ASCII (#define NUL ('\0') também pode ser adequado, tendo em conta que o ASCII #0 é também conhecido como NUL), e 0 (zero) nos contextos numéricos. Embora para o compilador seja (quase) completamente igual, a diferença está na forma como outros programadores lerão o código no futuro (ou o próprio autor!).

Relativamente à segunda questão, a chave está em compreender que quando defines uma array, o seu nome é na realidade o mesmo que um apontador para o seu primeiro elemento:

int arr[] = { 1, 2, 3, 4 };

arr == &arr[0];  // true!

Posto isto, quando utilizas printf() com o especificador %s, a função espera que lhe passes um apontador para um char (que no teu caso tanto name como ptr ambos são, e apontam para o mesmo endereço), e vai imprimindo os caracteres um a um até chegar ao '\0'. Isso explica o facto de obteres joao joao.

Só para me certificar que percebeste: é impossível em C passar uma array completa a uma função. Embora tu passasses name directamente à printf(), na realidade isso é traduzido para algo como &name[0], ie. o endereço do primeiro elemento de name (ou seja, um apontador em tudo igual a ptr.

Como o thoga31 referiu, o especificador apropriado para imprimir o endereço para o qual um apontador aponta é %p:

int i = 42;
int *j = &i;

printf("%p", NULL);       // (nil)
printf("%p", (void*)&i);  // 0xbfff9342
printf("%p", (void*)j);   // 0xbfff9342

Logo, por extensão do raciocínio, estas linhas terão o mesmo output:

printf("%p", (void*)&name[0]);
printf("%p", (void*)ptr);

PS.: o (void*) que utilizo nas printf() serve para calar um aviso emitido pelo compilador, mas explicar o motivo completamente foge ao âmbito deste post.

  • Voto 2

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.