Jump to content

Archived

This topic is now archived and is closed to further replies.

GuilhermeLeobas

Estrutura de repetição e strcpy em C

Recommended Posts

Opa pessoal, estou apanhando um pouquinho do C.

 

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

char matriz[9][1];
int cont;

int main (){


for (cont=1; cont<=9; cont++){
	strcpy( matriz[cont], "-");
}



for (cont=1; cont<=9; cont++){
	printf ("%s \n", matriz[cont]);
}


return 0;
}

 

A ideia do strcpy é copiar o caractere "-" para a string matriz[cont] e que o resultado seja esse:

-
-
-
-
-
-
-
-
-

 

Mas o que o strcpy está fazendo é copiar 9x o caractere "-" para a matriz[1], 8x para a matriz[2], ...., e o resultado que eu tenho é esse:

---------
--------
-------
------
-----
----
---
--
-

 

Poderiam me dar uma ajuda?

 

Edit: Removi o for do algoritmo e fiz a atribuição do "-" na mão e deu a mesma coisa, o problema ocorre quando eu uso mais de uma vez o strcpy

Share this post


Link to post
Share on other sites

Tente alterar, na declaração da matriz, o comprimento das strings de 1 para 2, e veja se funciona.

Uma string sempre é terminada com o caractere nulo '\0'. Se você declarar uma string com o comprimento [1], ocorre umas coisas estranhas como estas...

 

A própria função 'strcpy' se encarrega de adicionar o caractere nulo no final da string copiada.

Share this post


Link to post
Share on other sites

Olá!

 

Veja como a matriz abaixo é armazenado na memória:

 

int m[4][2];

 

m[0][0] | m[0][1] | m[1][0] | m[1][1] | m[2][0] | m[2][1] | m[3][0] | m[3][1]

 

Isso te ajudará a entender a explicação abaixo.

 

Lembre-se também que strings são conjuntos de caracteres terminados com um caracter nulo, o famoso '\0', que marca o tamanho da string.

 

 

 

Bom, observe que você está utilizando strings ao usar strcpy(). Por isso, o que é armazenado em sua array não é '-', mas '-' + '\0'. Assim, na memória, a cada instância, você está pegando o valor '-' da instância em que você está da array e colocando um \0 na próxima.

 

 

Então, na 1ª instância, temos isso armazenado:

 

m[0][0] = ?
m[1][0] = '-';
m[2][0] = '\0';
m[3][0] = ?
m[4][0] = ?
m[5][0] = ?
m[6][0] = ?
m[7][0] = ?
m[8][0] = ?

 

Na segunda instância do for, vamos ir para o seguinte, pois você substituiu o '\0' que estava no lugar com o strcpy() e clocou o valor '-' no lugar, e pegou o próximo valor na matriz e colocou o '\0':

 

 

m[0][0] = ?
m[1][0] = '-';
m[2][0] = '-';
m[3][0] = '\0';
m[4][0] = ?
m[5][0] = ?
m[6][0] = ?
m[7][0] = ?
m[8][0] = ?

 

 

Na 3ª instância, ficou assim:

 

m[0][0] = ?
m[1][0] = '-';
m[2][0] = '-';
m[3][0] = '-';
m[4][0] = '\0';
m[5][0] = ?
m[6][0] = ?
m[7][0] = ?
m[8][0] = ?

 

Até chegar na 7ª instãncia, seu código estará correto:

 

m[0][0] = ?
m[1][0] = '-';
m[2][0] = '-';
m[3][0] = '-';
m[4][0] = '-';
m[5][0] = '-';
m[6][0] = '-';
m[7][0] = '-';
m[8][0] = '\0';

 

Mas, ao chegar na 8ª instância, o '\0' do strcpy() vai sobrepor o valor depois da matriz na memória, tendo o famoso buffer overflow.

 

m[0][0] = ?
m[1][0] = '-';
m[2][0] = '-';
m[3][0] = '-';
m[4][0] = '-';
m[5][0] = '-';
m[6][0] = '-';
m[7][0] = '-';
m[8][0] = '-';
local_da_memoria_apos_o_m[8][0] = '\0'; // :o

 

Esse overflow faz com que você modifique um local na memória que não faz parte da array e adicione o valor 0. Assim, você pode estar modificando variáveis até do seu próprio programa, causando um erro na runtime que pode variar desde valores sem sentido nas variáveis até um crash (se o lugar que tentou acessar e que não fazia parte da matriz não fazia parte do seu programa).

 

E as coisas ainda pioram quando você parte para a 9ª iteração:

 

m[0][0] = ?
m[1][0] = '-';
m[2][0] = '-';
m[3][0] = '-';
m[4][0] = '-';
m[5][0] = '-';
m[6][0] = '-';
m[7][0] = '-';
m[8][0] = '\0';
local_da_memoria_apos_o_m[8][0] = '-'; // :o
local_da_memoria_apos_o_local_da_memoria_apos_o_m[8][0] = '\0'; // :OO

 

Você acessou 2 lugares fora da matriz, errando seriamente no código.

 

 

Após isso, ao printar o valor m[1], você estaria printando a string que vai desde m[1][0] até o local_da_memoria_apos_o_local_da_memoria_apos_o_m[8][0], e não o valor m[1][0], que é um char. Isso ocorre por propriedades de ponteiros que não convém explicar agora.

 

Ao printar o valor de m[1], você printaria a string que vai desde m[2][0] até o local_da_memoria_apos_o_local_da_memoria_apos_o_m[8][0], e não m[2][0], e assim por diante.

 

 

--------------------------------------------

 

Mas quais foram os seus erros?

 

1º: NUNCA ESQUEÇA de que o 0 é um número tão perfeito quanto os outros. Por que a discriminação? Em programação, todas as contagens começam do 0, e não do 1. Por exemplo, um vetor de ints declarado assim:

 

int vetor[9];

 

Tem seus valores armazenados desde vetor[0] até vetor[8], mas NUNCA vetor[9], pois está fora da array. Conte: 0, 1, 2, 3, 4, 5, 6, 7, 8 - 9 números. Se você adicionar o 9, ficam 10, o que está fora do que você quis.

 

Então, a primeira correção para seu código é:

 

for (cont = 0; cont < 9; ++cont){
               strcpy( matriz[cont], "-");
       }

 

Você não quer que chegue ao valor 9, pois é fora do vetor, então você coloca < 9, pois aí só chegará até o 8. Ah, e detalhe, gera-se menos código ao utilizar ++cont, ao invés de cont++, por isso é um pouco mais rápido. Há uma diferença entre os 2, mas para o que você provavelmente desejará utilizar, faz até mais sentido usar ++cont.

 

 

 

2º: Você declarou sua matriz assim:

 

char matriz[9][1];

 

Por que então, ao acessá-la, você está esquecendo do 2º colchete? Utilizando somente o 1º, por causa de propriedades das matrizes e ponteiros, você acessa o que pode ser considerado uma string se tiver algum '\0' mais para frente. Se quer acessar o caracter, acesse por matriz[x][0].

 

Mas, se você só quer armazenar caracteres, não strings, não faria mais sentido armazenar assim?

 

char matriz[9];

 

Você estará armazenando um vetor de caracteres, e não uma string. Uma string é um vetor de caracteres com um '\0' no final. Se ele não existir, não será uma string. Além disso, você pode acessar os caracteres assim:

 

for (cont = 0; cont < 9; ++cont){
               matriz[cont] = '-';
       }

 

Lembre-se da diferença entre "-", que é uma string ( '-' + '\0' ) e '-', que é um caracter sozinho.

 

Mas, se quer utilizar strings, você tem que adicionar espaço para o 2º caracter, o '\0' que marca que é uma string. Então, você precisa colocar a declaração da matriz assim:

 

char matriz[9][2];

 

Isso fará 9 strings de 1 caracter. Aí o código abaixo se torna válido:

 

for (cont = 0; cont < 9; ++cont){
               strcpy( matriz[cont], "-");
       }

 

 

Agora, no final, se você quer printar a string, você utiliza %s. Se quer printar o caracter sozinho, você utiliza %c. Ao utilizar %s no printf, você forçou ele a printar tudo até achar o primeiro '\0', e por isso deu aquele resultado. Então, terminamos com as 2 versões do programa, uma utilizando strings, e a outra caracteres.

 

 

VERSÃO COM CARACTERES:

#include <stdio.h>

char vetor[9];
int cont;

int main(void) {


       for (cont = 0; cont < 9; ++cont) {
               vetor[cont] = '-';
       }

       for (cont = 0; cont < 9; ++cont) {
               printf ("%c\n", vetor[cont]);
       }

       return 0;
}

 

PS: é de melhor prática utilizar int main(void) ao invés de int main()...

 

 

VERSÃO COM STRINGS:

#include <stdio.h>
#include <string.h>

char matriz[9][2];
int cont;

int main(void) {


       for (cont = 0; cont < 9; ++cont) {
               strcpy( matriz[cont], "-");
       }

       for (cont = 1; con < 9; ++cont) {
               printf ("%s\n", matriz[cont]);
       }

       return 0;
}

 

Perceba que além de ser mais rápido, utilizar caracteres para essa aplicação gasta menos memória. Mas strings são mais úteis, como você provalvelmente vai descobrir mais para frente. Você acabará tendo que aprender a utilizá-las, mais cedo ou mais tarde.

 

 

Você pode notar que caracteres e strings são confusas e uma parte difícil de masterizar na C. Elas se misturam com ponteiros e causam muita dor de cabeça para quem não os entende. Entendendo ponteiros, você entenderá como as strings funcionam, e também como evitar problemas com elas.

 

 

Abraços :D

Share this post


Link to post
Share on other sites

Gostaria de fazer algumas observações:

 

Quando strcpy recebe m[0] como argumento, tenta armazenar 2 bytes ali. Isto em si gera undefined behavior, ainda que saibamos que m[1][0] está em posição consecutiva a m[0][0]. Este é o primeiro erro do código, e se repete a cada iteração e chamada para strcpy.

O outro erro é, como comentado, iniciar a contagem em 1. Ele é tão sério quanto o primeiro.

O último é passar qualquer coisa que não contenha '\0' como argumento relativo a %s. Pode parecer que o comportamento seria bem-definido caso o '\0' estivesse em algum lugar antes do fim da array de arrays, mas isto não é verdade. Assim que printf recebe m[0] como argumento, continua e acessa posições fora da array cujo primeiro elemento é apontado por m[0]. Novamente digo: não importa que saibamos que os valores consecutivos são internos a m - isto gera undefined behavior.

 

foo++ ou ++foo só têm, necessariamente, diferenças semânticas. Quanto código, e com que eficiência, cada implementação gera para cada um não é especificado.

 

Dado char m[9][1];, m tem tipo 'array de 9 arrays de 1 char', e valor de tipo 'ponteiro para array de 1 char'. m[0] tem tipo 'array de 1 char', e valor de tipo 'ponteiro para char'. A regra a entender aqui é que o valor de qualquer array é um ponteiro para seu primeiro elemento. Em chamadas de funções, o que é passado são os valores dos argumentos fornecidos, e a regra acima se aplica de forma bonita e lógica.

Share this post


Link to post
Share on other sites

×

Important Information

Ao usar o fórum, você concorda com nossos Terms of Use.