Ir ao conteúdo
  • Cadastre-se

C Printf atribuindo "p" ao início da frase.


Posts recomendados

Eu fiz um programa que deve copiar o conteúdo de "v1" para "v2". Até chega à funcionar, exceto pelo fato do primeiro caractere do printf de "v2" estar sendo substituído pela letra "p". E sim, eu sei que dá pra fazer com strcpy, entretanto foi pedido que o programa fosse feito sem o uso desta função e eu não faço ideia do porquê disso acontecer. Ainda sou novato em linguagem C, e toda a critica será bem aceita :)

Segue o código: 

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
//#include <string.h>

main()
{
	setlocale(LC_ALL, "Portuguese");

	char v1[100], v2[100];
	int aux;
	
	printf("Digite uma palavra: ");
	scanf("%[^\n]s", v1);
//	strcpy(v2, v1);

	while(aux[v1, v2] != '\0') 
    {
		aux++;
		v2[aux] = v1[aux];
	}

	printf("\nPalavra V1: %s\n", v1);
	getch();
	printf("Palavra V2: %s", v2);
	getch();
}

 

Link para o comentário
Compartilhar em outros sites

Veja assim:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
//#include <string.h>

main(){
	setlocale(LC_ALL, "Portuguese");

	char v1[100], v2[100];
	int i=0;
	
	printf("Digite uma palavra: ");
	scanf("%[^\n]s", v1);
//	strcpy(v2, v1);

	while(v1[i] != '\0'){
		v2[i] = v1[i];
		i++;
	}

	printf("\nPalavra V1: %s\n", v1);
	getch();
	printf("Palavra V2: %s", v2);
	getch();
	return 0;
}

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

	while(aux[v1, v2] != '\0') 		/* Está fazendo a operação de checagem com um inteiro */
    {
      /* Isso sempre vai copiar o proxímo caractere */
		aux++;
		v2[aux] = v1[aux];
	}

Essa sentença está copiando lixo da memoria.

 

Funcionamento de uma string.

C_Strings.png.22aef3025f6635d3c3ca1293869ef342.png

 

getch não é mais usado, então de preferência a usar o getchar().

 

Outra maneira de fazer a mesma coisa do seu algorítimo.

/* Inclusão de bibliotecas no código */
#include <stdio.h>    /* Entrada e saída padrão */

/* Constantes */
#define T_FRASE 80    /* Declara um identificador para o número 80 */

/* Procedimentos */

/* Copia fonte para destino */
void StrCpy(char *destino, char *fonte);

/* Retira o caractere de nova linha da string */
void CleanNewLine(char *str);

              /* Função principal */
int main(void)
{
  /* str será usado para pegar uma frase do usuário */
  /* saída será usado na copia da frase que está em str */
  char str[T_FRASE],saida[T_FRASE];
  /* Pede uma frase para o usuário */
  printf("\nDigite uma frase:\t");
  fgets(str,T_FRASE,stdin);   /* Pega uma string da entra padrão stdin */
  CleanNewLine(str);      /* Retira o caractere de nova linha da string */
  StrCpy(saida,str);      /* Copia string fonte para a string destino */
  /* Mostra a frase digitada */
  printf("\nFrase digitada.\n\t%s",saida);
  getchar();    /* Pausa o programa */
  return(0);    /* Retorna 0 para o sistema operacional */
}

/* Implementações */

void StrCpy(char *destino, char *fonte)
{
  unsigned short cont;
  /* Executa até encontrar um 0 '\0' */
  for(cont=0; fonte[cont]!='\0'; cont++)
    destino[cont]=fonte[cont];    /* Passa o caractere da fonte para destino */
  cont++;   /* Incrementa */
  destino[cont]='\0';    /* Adiciona o fim da string */
}

void CleanNewLine(char *str)
{
  unsigned short cont;
  /* Procura por uma nova linha ou por 0 na string */
  for(cont=0; str[cont]!='\n'&&str[cont]!='\0'; cont++);
  /* Checa se há uma nova linha */
  if(str[cont]=='\n')
    str[cont]='\0';     /* Troca o caractere de nova linha pela terminação da string */
}

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

4 horas atrás, Leonardo Rocha De Araujo disse:

E sim, eu sei que dá pra fazer com strcpy, entretanto foi pedido que o programa fosse feito sem o uso desta função e eu não faço ideia do porquê disso acontecer

 

O provável porque disso acontecer: mostrar ao aluno como funciona a declaração de strings em C e o uso de vetores e índices.


Escrever

   v2 = strcpy(v2,v1);	// nao ensina nada...

É como o cara que diz pra você que para limpar a tela em C no Windows você escreve

    system("cls"); // isso nao e um programa. Apenas uma intermediacao

Aqui você usa system() e passa um comando do sistema, como em strcpy() você passa os endereços de origem e destino e a função que alguém escreveu copia os valores e retorna o endereço de destino. Seu programa não "fez" isso de fato. É como alguém que te diz o caminho para um endereço fazendo uma cara de intelectual e explicando como usar o app Waze e entrar com o endereço...
 

E aparentemente vai ser legal você escrever esse programa porque parece que você não conhece a mecânica dessas coisas ainda...

    scanf("%[^\n]s", v1); // nao funciona assim

você pode escrever "%s" ou "%[^\n]". O primeiro caso lê uma string mas vai parar no primeiro whitespace: espaço, tab ou newline. O segundo caso lê qualquer coisa exceto um enter. O 's' perdido ao final tem um significado importante em scanf() e é uma espécie de marcador obrigatório. Não vou explicar muito porquenão tem a ver com o problema aqui. Pergunte de novo se qiser os detalhes. Em resumo esse 's' perdido exigiria que o próximo caracter na entrada fosse exatamente um 's'. E se não for a rotina retorna, sem mexer no resto do que tem pra ler. É muito útil em certos casos, mas nunca no final da máscara, e é um lado mais obscuro e raro de scanf(). 

Se ficou curioso escreva de novo e de mostro um uso disso.

 

Provavelmente você quer a segunda opção para poder ler uma linha com espaços. E scanf() retorna um int com o número de campos lidos. LEIA o retorno de scanf() SEMPRE
 

Citação

Se o usuário teclar enter direto para testar com uma string vazia seu programa vai cancelar...

 

Mas se ele digitar algo o programa vai colocar o que o usuário digitou em

    char v1[100]; 

uma letra depois da outra. Sugiro usar nomes mais indicativos, como origem e destino.

 

E o sistema vai colocar um terminador, o código 0, o tal null , logo em seguida. Isso vai marcar o final da string e elas são por isso ditas null-terminated

 

O vetor é acessado pelo nome e índice, então se o cara digitou só um 'a'' você vai ter

    v[0] = 'a';
    v[1] = 0; // se o cara digitou so um 'a'

. Se o usuário não digitou nada e só teclou ENTER mesmo assim você vai ter uma alteração e

    v[0] = 0;  se o cara digitou enter direto

indicando que a string está vazia.

 

Então para copiar basta você copiar tudo de v1 para v2 até encontrar uma posição de v1 com valor zero. Como mesmo que a string esteja vazia você precisa copiar o tal terminador, provavelmente um while() não seja a solução mais simples. Em um loop onde você executa ao menos uma vez um do() é mais esperto.

 

char original[100];
char destino[100];
int  i;
	
// ...
    i = 0;
    do
    {
        destino[i] = original[i];
        i = i + 1;
    } while(original[i] != 0);

Mesmo que original esteja vazio você precisa copiar o terminador 0  ou seu programa vai cancelar assim que tentar acessar destino. C não inicializa as variáveis por definição e mesmo que fosse o caso não seria esperto contar com isso: ia funcionar só na primeira vez...

 

@KXSY é preciso copiar o terminador ao final, o \0. O código que postou não faz isso. Veja o trecho abaixo...

void StrCpy(char *str1, char *str2)
{
  unsigned short cont;
  for(cont=0; str2[cont]; cont++)
    str1[cont]=str2[cont];
}

Não vai funcionar. Ao usar str2[cont] como condição no for vai sair ao encontar o null e não vai copiar o próprio e essencial null. Quando a string de origem estiver vazia a de destino vai por exemplo ficar intocada.


Talvez fosse melhor usar, como na documentação oficial, nomes significativos como origem, destino ou como nos manuais source e destination para um exemplo num forum de iniciantes. 

 

E usar str2[cont] como condição de saída do for implica entender TRÊS coisas ao menos:

  1. Em C o último byte da string é sempre zero.
  2. O for em C encerra quando o segundo comando avaliar como falso. Nesse caso str2[cont]. Isso seria o mesmo que escrever (str2[cont] == 0) que seria mais legível.
  3. Em C zero é falso e não zero é verdadeiro então str2[cont] igual a zero é o que força a saída do loop, e isso acontece então no final da string de origem.

Talvez então num forum para iniciantes não seja indicado escrever um código assim, envolvendo essas "sutilezas". E ironicamente não está funcionando exatamente nessa linha.

 

Não me leve a mal por levantar essas aspectos

 

 

adicionado 18 minutos depois

De todo modo, em relação ao resultado estranho na saída, já sabe que 

  • a máscara de scanf() está errada.

  • Esse comando 
        while(aux[v1, v2] != '\0') { ... }
    também
  • Curtir 1
Link para o comentário
Compartilhar em outros sites

1 hora atrás, arfneto disse:

@KXSY é preciso copiar o terminador ao final, o \0. O código que postou não faz isso. Veja o trecho abaixo...

Realmente, eu acabei esquecendo.

 

Mas eu acabei percebendo algo interessante nesse erro.

Interessante.thumb.png.d5086001ddfd5ec700cb83bf282274f7.png

As paginas de memoria no linux contêm bem menos lixo do que no windows, fazendo assim meu código rodar perfeitamente sem nenhum "segment fault" (Vou ser sincero, porque só rodei o código uma vez!).

 

1 hora atrás, arfneto disse:

Não vai funcionar. Ao usar str2[cont] como condição no for vai sair ao encontar o null e não vai copiar o próprio e essencial null. Quando a string de origem estiver vazia a de destino vai por exemplo ficar intocada.


Talvez fosse melhor usar, como na documentação oficial, nomes significativos como origem, destino ou como nos manuais source e destination para um exemplo num forum de iniciantes. 

 

E usar str2[cont] como condição de saída do for implica entender TRÊS coisas ao menos:

  1. Em C o último byte da string é sempre zero.
  2. O for em C encerra quando o segundo comando avaliar como falso. Nesse caso str2[cont]. Isso seria o mesmo que escrever (str2[cont] == 0) que seria mais legível.
  3. Em C zero é falso e não zero é verdadeiro então str2[cont] igual a zero é o que força a saída do loop, e isso acontece então no final da string de origem.

Talvez então num forum para iniciantes não seja indicado escrever um código assim, envolvendo essas "sutilezas". E ironicamente não está funcionando exatamente nessa linha.

Segui ás dicas e dei um tapa no código!

 

1 hora atrás, arfneto disse:

Não me leve a mal por levantar essas aspectos

Nada. tá de boas ;)

Link para o comentário
Compartilhar em outros sites

18 minutos atrás, KXSY disse:

As paginas de memoria no linux contêm bem menos lixo do que no windows, fazendo assim meu código rodar perfeitamente sem nenhum "segment fault" (Vou ser sincero, porque só rodei o código uma vez!)

 

Entendo.

 

Mas veja que isso --- um segment fault por exemplo --- em um programa de produção é o melhor que pode acontecer nesses casos. Rodar "perfeitamente" aí sim é o diabo, porque pode corromper memória em algum lugar e dar um erro nada a ver, e só às vezes. Isso foi um dos maiores argumentos para a criação dessas linguagens gerenciadas, com garbage colletor, sem alocação e tal, tipo java e C# e parentes.

 

Tanto faz linux --- que era UNIX --- ou Windows. Ou MACOS que é linux, ou Android que é linux :D 

 

Apenas considere que em algumas situações o Linux inicializa tudo com zero. Sim, zero, exatamente o que faltou na sua string de destino. E vai te dar a falsa impressão de que está tudo ok. Isso nada tem a ver com C ou C++. O standard diz o contrário: você não pode contar com nada a respeito do valor original de variáveis não inicializadas.

 

Citação

Vou sair um pouco do tópico aqui: o fato da área estar zerada é uma questão de segurança. Quando o sistema operacional aloca uma região de memória para um processo a área é zerada, para impedir a injeção de código malicioso. Mas se a região alocada já fazia parte da área alocada para seu programa o sistema não faz nada.

 

veja esse saída de um programa que usa a sua função StrCpy() e começa com as strings vazias, e que independe da plataforma:

***** inicio
origem(0):[]
destino(0): []

***** muda origem
origem(5): [teste]
destino(0): []

***** usa StrCpy para copiar
origem(5): [teste]
destino(5): [teste]

***** muda origem de novo
origem(3): [fim]
destino(5): [teste]

***** usa StrCpy para copiar de novo
origem(3): [fim]
destino(5): [fimte]

E o programa

#define  _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "string.h"

void StrCpy(char*, const char*);

int main(int argc, char** argv)
{
    char origem[200];
    char* po = origem;
    char destino[200];
    char* pd = destino;
    
    printf("***** inicio\n");
    memset(origem, 0, 200); // zera origem
    memset(destino, 0, 200); // e destino
    printf("origem(%d):[%s]\ndestino(%d): [%s]\n\n",
        strlen(origem), origem,
        strlen(destino), destino
    );

    printf("***** muda origem\n");
    po = strcpy(po, "teste");
    destino[0] = 0;
    printf("origem(%d): [%s]\ndestino(%d): [%s]\n\n",
        strlen(origem), origem,
        strlen(destino), destino
    );

    printf("***** usa StrCpy para copiar\n");
    StrCpy(pd, po);
    printf("origem(%d): [%s]\ndestino(%d): [%s]\n\n",
        strlen(origem), origem,
        strlen(destino), destino
    );

    printf("***** muda origem de novo\n");
    po = strcpy(po, "fim");
    printf("origem(%d): [%s]\ndestino(%d): [%s]\n\n",
        strlen(origem), origem,
        strlen(destino), destino
    );

        printf("***** usa StrCpy para copiar de novo\n");
    StrCpy(pd, po);
    printf("origem(%d): [%s]\ndestino(%d): [%s]\n\n",
        strlen(origem), origem,
        strlen(destino), destino
    );
    return 0;
}   // main()


void StrCpy(char* str1, const char* str2)
{
    unsigned short cont;
    for (cont = 0; str2[cont]; cont++)
        str1[cont] = str2[cont];
};  // StrCpy()

Roda "perfeitamente". Sem crash, mas a string de destino era pra ser "fim"e ficou com "fimte". Inofensivo? Nem tanto. E se fosse um checksum ou código de autenticação?

 

Isso porque a segunda string a ser copiada era menor que a primeira. Se fosse maior seria a loteria: se não tiver um null antes do final da área alocada inicialmente --- esses 200 bytes no exemplo ---  você vai ter um crash assim que tentar acessar fora dessa região. . . E depois de uma série de operações de cópia não da pra saber o que tem lá, certo? 

 

 

adicionado 6 minutos depois

Em tempo, direto do manual
 

malloc.png.f6e15e2067fa04f4093c5fbdc0c51c5d.png

 

 

Link para o comentário
Compartilhar em outros sites

11 horas atrás, arfneto disse:

Mas veja que isso --- um segment fault por exemplo --- em um programa de produção é o melhor que pode acontecer nesses casos. Rodar "perfeitamente" aí sim é o diabo, porque pode corromper memória em algum lugar e dar um erro nada a ver, e só às vezes. Isso foi um dos maiores argumentos para a criação dessas linguagens gerenciadas, com garbage colletor, sem alocação e tal, tipo java e C# e parentes.

Não é bem assim isso acontece com qualquer linguagem de programação, só basta o programador chamar alguma coisa errada na hora certa (ou não chamar) e você ganha um bug.

My favorite bug: segfaults in Java.

11 horas atrás, arfneto disse:

Tanto faz linux --- que era UNIX --- ou Windows. Ou MACOS que é linux, ou Android que é linux :D

Eu acho um erro generalizar, porque android não é linux. ele roda os apps em uma maquina virtual sendo assim pouco importa o que está por debaixo do capo.

11 horas atrás, arfneto disse:

Apenas considere que em algumas situações o Linux inicializa tudo com zero. Sim, zero, exatamente o que faltou na sua string de destino. E vai te dar a falsa impressão de que está tudo ok. Isso nada tem a ver com C ou C++. O standard diz o contrário: você não pode contar com nada a respeito do valor original de variáveis não inicializadas.

Eu sabia que tinha que terminar com zero, mas eu citei essa parte do linux porque quando você está programando para embarcados que tem o kernel linux isso seria uma mão na roda para o reaproveitamento de ciclos e memoria.

Link para o comentário
Compartilhar em outros sites

10 horas atrás, KXSY disse:

Não é bem assim isso acontece com qualquer linguagem de programação

 

Entendo. eu falava mais especificamente, no contexto dessa discussao: corrupção de memória gravando além da área alocada, como no caso de "rogue pointers"

 

10 horas atrás, KXSY disse:

porque android não é linux. ele roda os apps em uma maquina virtual sendo assim pouco importa o que está por debaixo do capo

 

Você pode escrever apps para android em C++ e rodar no sistema nativo, Linux.

 

10 horas atrás, KXSY disse:

mas eu citei essa parte do linux porque quando você está programando para embarcados que tem o kernel linux isso seria uma mão na roda para o reaproveitamento de ciclos e memoria

 

Bem, muitos acham o contrário. Em especial em sistemas Linux embarcados. E vou explicar porque: inicializar blocos de memória alocados é um serviço do sistema e não da linguagem. Como mostrei antes a documentação descarta isso na linguagem explicitamente.

 

Isso só é feito por segurança, para evitar a injeção de código malicioso durante a alocação de memória. E só acontece se o bloco alocado vier do pool do sistema. Se vier de um recurso liberado pelo processo mas ainda não retornado ao sistema a memória fica como estava. Então você tem que se precaver de qualquer modo. Na verdade nem sei onde esse mecanismo estaria descrito. Nunca me preocupei porque não é 100% confiável. E só se aplica à alocação inicial.

 

Não vai reaproveitar ciclos de memória porque você não pode contar com isso 100%. E não vai aproveitar ciclos de execução pelo mesmo motivo. E ainda vai gastar mais ciclos porque esse serviço não pode ser desabilitado de jeito nenhum. E em muitos casos essa segurança é menos interessante.

 

Ironicamente o caso mais clássico é o do seu exemplo, o de sistemas linux embarcados, onde a oportunidade de injeção de código praticamente não existe e os ciclos gastos com esse serviço podem ser importantes, como em sistemas de "tempo real". Pense nisso. 

 

Sistemas embarcados Windows não tem esse problema porque usam uma tecnologia diferente --- DEP data execution protection --- que muitas vezes é assistida por hardware. E é controlável. 

 

Não sou especialista nessas coisas, claro. E está um pouco fora do tópico :D 

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...