Ir ao conteúdo

Posts recomendados

Postado

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();
}

 

Postado

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;
}

 

Postado
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
Postado
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

 

 

Postado
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 

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...

LANÇAMENTO!

eletronica2025-popup.jpg


CLIQUE AQUI E BAIXE AGORA MESMO!