Ir ao conteúdo

Posts recomendados

Postado

Faça um programa que armazene n palavras em uma matriz. Em seguida, o
programa deverá exibir:
a) a quantidade de caracteres em cada palavra.
b) cada palavra invertida.

 

MEU CÓDIGO:

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
  char nomes[100][50];
  int i, l, n, tam[100];
  printf("Com quantos nomes você deseja trabalhar? ");
  scanf("%d", &n);
  for(i=0; i<n; i++){
    printf("Digite o %d nome: ", i);
    scanf("%*c");
    fgets(nomes[i], 50, stdin);
    tam[i]=strlen(nomes[i])-1;
  }
  system("clear");
  for(i=0; i<n; i++){
    printf("O total de caracteres do nome %s equivale a: %d\n", nomes[i], tam[i]);
  }

  return 0;
}

 

ainda sou nova na linguagem c, e já fiz até a parte de mostrar quantos caracteres tem, mas não sei como inverter as strings, alguém pode me ajudar??

  • Obrigado 1
Postado
50 minutos atrás, Ana987546 disse:
int main(void)
{
  char nomes[100][50];
  int i, l, n, tam[100];

 

 

Use nomes mais expressivos para as variáveis. tam seria o que? Podia ser
 

    char invertida[50];

 

Sua matriz é uma grade de char, um tabuleiro com 100 ocorrências de um vetor de 50 char. Entenda assim:

  • nomes é char[100][50]
  • então nomes[0] até nomes[99] são vetores de 50 char
  • nomes[99][0] é um char, a primeira letra da última palavra que cabe na sua matriz
  • a primeira letra de todas é nome[0][0] e a última é nome[99][49]
  • nome[x] é um vetor de char, com 50 deles
  • nome[x][y] é um char

Como inverter uma palavra?

 

Se entendeu o que está acima, sabe que tem 100 possíveis palavras, e cada uma tem até 50 char

 

Em C ao ler com fgets() vai receber um char*

 

    fgets(nomes[i], 50, stdin);

 

E sua chamada então vai colocar em nomes[i] a palavra. Ao final das letras vai estar um 0 indicando o fim da string, então sua palavra terá até 49 letras.

 

A tal string vai terminar quando o cara digitar um enter, o tal '\n', newline. E se sobrar espaço a função fgets() vai deixar isso no resultado, no seu caso em nomes[i][algo]. Claro, será a última coisa antes do zero porque foi o tal newline que fez fgets() retornar.

 

Então o zero por certo vai estar lá, ao final. E o '\n' vai estar imediatamente antes, a menos que o zero esteja no fim :) a conhecida posição 49.

 

Para inverter você pode usar uma outra string, mas acho que tam[100] não é um bom nome. E claro que 100 é muito. Não pode ter mais de 50 afinal.

 

E como fazer? Em um loop de 0 a 50 veja onde está o primeiro zero. Ele marca o final da string. Antes dele pode ter um '\n'. E antes dele estará a última letra da palavra. De posse desse número um use um simples loop dessa posição até zero copiando as letrinhas dessa palavra para o vetor onde vai estar a string invertida. Ao final acrescente um 0 para fechar a conta. Nada mais.

 

A pegadinha: 
 

    char *fgets(char *str, int n, FILE *stream);

 

Não sei porque não ensinam isso :(  Sempre use o retorno de fgets(). Nesse caso:

 

    fgets(nomes[i], 50, stdin);

 

fgets() retorna o endereço de nomes[i]. Só que se fgets() não ler nada, porque deu erro ou fim de arquivo, fgets() retorna NULL e nomes[i] permanece inalterado, o que é bem razoável já que não leu nada.

 

Mas se você escreve como escreveu nunca vai saber e seu programa vai ficar em loop. 

  • Curtir 1
Postado

@Ana987546    é uma boa prática identar o código para facilitar a leitura e detectar possíveis erros , e para a postagem ter uma melhor apresentação clique nesse botão  <>   que está na barra de ferramentas da janela de postar códigos , e no linux não sei como funciona , não ,pois uso Windows  ,  e aqui no codeblock , seu código funcionou bem assim :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    char nomes[100][50];
    int i, l, n, tam[100],j;
    printf("Com quantos nomes você deseja trabalhar? ");
    scanf("%d", & n);
    for(i=0; i<n; i++)
    {
        printf("Digite o %d nome: ", i);
        ///scanf("%*c");               // aqui não funciomou bem , não
        fflush(stdin);                 // esse limpa o buffer do teclado
        setbuf(stdin,NULL);            // esse também
        fgets(nomes[i], 50, stdin);    // pegar tudo digitado e mais o newline
        tam[i] = strlen(nomes[i]) - 1; // pega o tamanho da string
        nomes[i][tam[i] ] = '\0';      // remover o newLine
    }
    system("clear || cls");            // no Windows é cls
    for(i=0; i<n; i++)                 // percorrer o vetor de strimg's
    {
        printf("O total de caracteres do nome %20s equivale a: %3d\n",
               nomes[i], tam[i]                                  );
    }
    for(i=0; i<n; i++)
    {
        printf("O Nome %10s invertido eh -> ",nomes[i]);
        for(j=strlen(nomes[i])-1; j>=0; j--)// loop do fim para o inicio
            printf("%c",nomes[i][j]);       // escreve cada letras da string
        printf("\n");                       // e pula a linha para baixo
    }
    printf("\n\n\n");
    return 0;
}

 

Postado

eu fiz de um jeito a função que inverte uma string:
 

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

char *inverte(char *str)
{
	int tm = strlen(str); 
  	char*  new = malloc( sizeof(char) * tm );
	for(int i=0; i<tm; i++)
	{
		new[i] = str[tm-i-1];
	}
	return new;
}

int main(void)
{
	char *nome = "abcdefghijklm";
	nome = inverte(nome);
	printf("%s\n", nome);
	return 0;
} 


Agora é contigo pra saber como usar a função...

Eu tentei fazer um swap nos caracteres da string, mas não consegui.
porque isso aqui abaixo não funciona e como é o equivalente disso em c?
str[0] = str[tm-1]

Pelo que li, deveria existir uma função strrev(), mas aqui não funciona.

talvez pra quem usa Windows, ela funcione...

  • Amei 1
Postado

 

1 hora atrás, codigo rápido disse:

Eu tentei fazer um swap nos caracteres da string, mas não consegui.
porque isso aqui abaixo não funciona e como é o equivalente disso em c?
str[0] = str[tm-1]


consegui fazer, mas achei feio. Uma voltona e tive que fazer um malloc do mesmo jeito... Acho que não compensa fazer isso:

 

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

char *inverte(char *str)
{
	int tm = strlen(str);
	char strArr[tm]; 

	// tem que copiar o char* para um array
	strcpy(strArr, str);

	// agora dá pra fazer swap
	char temp;
	for(int i=0; i<tm/2; i++)
	{
		temp = strArr[i];
		strArr[i] = strArr[tm-i-1];
		strArr[tm-i-1] = temp;
	}

	char *new = malloc(sizeof(strArr)+1);
	strcpy(new, strArr); 
	return new;
}

int main(void)
{
	char *nome = "abcdefghijklm";
	nome = inverte(nome);
	printf("%s\n", nome);
	return 0;
}

 

  • Amei 1
Postado

@codigo rápido Eu acho que seria melhor usar o mesmo espaço que foi passado para voltar a string.

Dessa maneira que você fez você deixou um vazamento de memoria (mem leak) passar na hora que você reservou a memoria e não a liberou.

 

Uma alternativa melhor seria

#include <stdio.h>

#define T_FRASE 60

char *inverteString(char *str);
/*
 * Em caso de sucesso devolve a string invertida, ou NULL em caso de falha.
 */

int main(void)
{
    char frase[T_FRASE];
    puts("Digite uma frase para ser invertida");
    fgets(frase, T_FRASE, stdin);
    /* Retira o newline */
    {
        int contador;
        for (contador = 0; frase[contador]; contador++);
        frase[contador-1] = '\0';
    }

    if (inverteString(frase) == NULL) {
        puts("Erro ao inverter a frase!");
    } else {
        puts("Frase invertida");
        puts(frase);
    }
    puts("Pressione enter para continuar...");
    getchar();
    return(0);
}

char *inverteString(char *str)
{
    int contador1, contador2, tamanho_string = 0;

    /* Verifica o tamanho da string */
    if (str != NULL) {
        for (contador1 = 0; str[contador1]; contador1++);

        tamanho_string = contador1-1;
        if (tamanho_string > 0) {
            char temporario[tamanho_string+1];
            contador2 = tamanho_string;
            /* Inverte a string */
            for (contador1 = 0; contador1 < tamanho_string+1; contador1++) {
                temporario[contador1] = str[contador2];
                contador2--;
            }
            temporario[contador1] = '\0';

            /* Copia a string de volta */
            for (contador1 = 0; temporario[contador1]; contador1++) {
                str[contador1] = temporario[contador1];
            }

            /* retorna a string */
            return(str);
        }
    }
    /* Erro */
    return(NULL);
}

 

  • Obrigado 2
Postado
48 minutos atrás, kgin disse:

@codigo rápido Eu acho que seria melhor usar o mesmo espaço que foi passado para voltar a string.

Dessa maneira que você fez você deixou um vazamento de memoria (mem leak) passar na hora que você reservou a memoria e não a liberou.


Verdade. Obrigado! Não vi isso. Esquci. Acho que é porque eu to muito mal acostumado com o Java. :D

57 minutos atrás, kgin disse:
char temporario[tamanho_string+1];


Eu percebi que você fez um array.
A entrada do parametro da função é char* str.
No java podem ter parametros de entrada para funções como char[] str.
As vezes isso me faz me perder um pouco porque se eu tentar fazer em algo que foi declarado como
char *str
não consigo fazer algo como
str[n]='c
Mas vendo seu codigo percebo que parece que é melhor utilizar arrays.
quando eu devo dar preferência por usar

char *str no lugar de char str[n]?

  • Curtir 1
Postado
1 hora atrás, codigo rápido disse:

quando eu devo dar preferência por usar

char *str no lugar de char str[n]?

Eu não posso afirmar com certeza (mesmo porque eu nunca estudei C), mas quando você declara

	char *str = "Uma string!";

você só está criando um ponteiro chamado *str que aponta para um local do código (no formato elf provavelmente vai ser na secessão .text ou .data) que não pode ser modificada.

Mas quando você declara

	char str[] = "Uma string!";

você está pedindo para o compilador reservar memoria para o vetor na parte baixa da memoria e logo em seguida copiar o conteúdo do vetor para a memoria.

 

Isso vai depender do compilador e tipo de formato do executável, mas no geral é sempre assim.

Resumindo.

Assim você só pode modificar o ponteiro

	char *str = "Uma string!";
	char outra_str[] = "Outra string!";
	str = outra_str;
	str[3] = 'a';

 

É assim você só pode modificar o conteúdo.

	char str[] = "Uma string!";
	str[0] = ' ';	/* OK */
	str = NULL; 	/* Vai dar erro! */

 

Pense na primeira como sendo uma constante que você não pode modificar.

  • Curtir 2
Postado

Outra forma sem tamanho pré definido para a quantidade de nomes (só a quantidade de letras de 50 para cada um),

 

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

char *inverte(char *nome){
    size_t T = strlen(nome);
    if(nome[T - 1] == '\n'){
        T -= 1;
        nome[T] = 0;
    }
    for(size_t i = 0; i < T/2; i += 1){
        char aux = nome[T - i - 1];
        nome[T - i - 1] = nome[i];
        nome[i] = aux;
    }
    return nome;
}

int main(void){
    int n;
    printf("Quantidade: ");
    scanf("%d%*c", &n);
    char nomes[n][50];
    
    for(int i = 0; i < n; i += 1){
        printf("Nome %d: ", i + 1);
        fgets(nomes[i], 50, stdin);
    }
    for(int i = 0; i < n; i += 1){
        char *N = inverte(nomes[i]);
        printf("%s, letras = %lu\n", N, strlen(N));
    }
    return 0;
}

 

  • Curtir 2
Postado
4 horas atrás, kgin disse:

Eu acho que seria melhor usar o mesmo espaço que foi passado para voltar a string

 

É melhor não fazer isso. A função fica menos útil porque nem sempre se quer inverter e zoar a original...

 

E dependendo de como ela foi chamada pode claro abortar o programa. Imagine

 

	inverte("Clube do Hardware);

 

:) e já era. 

 

 

14 horas atrás, codigo rápido disse:

Eu tentei fazer um swap nos caracteres da string, mas não consegui.
porque isso aqui abaixo não funciona e como é o equivalente disso em c?
str[0] = str[tm-1]

 

funciona assim mesmo.

 

16 horas atrás, devair1010 disse:
        fflush(stdin);                 // esse limpa o buffer do teclado
        setbuf(stdin,NULL);            // esse também
        fgets(nomes[i], 50, stdin);    // pegar tudo digitado e mais o newline

 

Considerem o que eu falei sobre fgets() e SEMPRE use o ponteiro. Nunca o vetor. Se fgets() não ler nada o nomes[i] fica inalterado e o programa nunca vai ver...

 

2 horas atrás, Midori disse:
char *inverte(char *nome){
    size_t T = strlen(nome);
    if(nome[T - 1] == '\n'){
        T -= 1;
        nome[T] = 0;
    }

 

Se chamar inverte() com uma string constante vai cancelar o programa em nome[T] = 0;. É um programa para iniciantes então o mais seguro provavelmente é usar
 

    inverte(const char* entrada, int n, char* saida)


e passar a responsabilidade para o programa que chamou inverte: inverte entrada com no máximo n bytes e coloca o resultado em saída, como em 

 

    char	invertida[50];
    inverte("Clube do Hardware", 50, invertida);

 

15 horas atrás, codigo rápido disse:
int main(void)
{
	char *nome = "abcdefghijklm";
	nome = inverte(nome);
	printf("%s\n", nome);
	return 0;
}

 

Aqui, como @kgindisse, tem um vazamento de memória, porque inverte() aloca memória e não é liberada em lugar algum. Mas tem dois leaks na verdade, já que o ponteiro nome é o único acesso à string original. Assim que escreve nome = inverte() se perde o único acesso à string "abcdefghijklm". É bom considerar.

 

4 horas atrás, codigo rápido disse:

Eu percebi que você fez um array.
A entrada do parametro da função é char* str.
No java podem ter parametros de entrada para funções como char[] str.
As vezes isso me faz me perder um pouco porque se eu tentar fazer em algo que foi declarado como
char *str
não consigo fazer algo como
str[n]='c
Mas vendo seu codigo percebo que parece que é melhor utilizar arrays.
quando eu devo dar preferência por usar

char *str no lugar de char str[n]?

 

Em geral tanto faz. E se pode usar os índices e colchetes do mesmo modo.

 

char[] é char*, mas char* nem sempre é char[]. Isso é um trem chamado array pointer decay e é meio confuso mesmo.

 

Exemplo

 

#include <stdio.h>
char        val_a(int,char[]);
char        val_p(int,char*);

int main(void)
{
    char vetor[] = "01234";
    printf( "Usando char[], V[4] = %c\n", val_a(4,vetor) );
    printf( "Usando char*, V[4] = %c\n", val_p(4,vetor) );
    return 0;
}

char        val_a(int pos, char V[]) { return V[pos]; }
char        val_p(int pos, char* V)  { return V[pos]; }

 

Esse programa mostra o esperado
 

Usando char[], V[4] = 4
Usando char*, V[4] = 4

 

2 horas atrás, kgin disse:

Eu não posso afirmar com certeza (mesmo porque eu nunca estudei C), mas quando você declara

	char *str = "Uma string!";

você só está criando um ponteiro chamado *str que aponta para um local do código (no formato elf provavelmente vai ser na secessão .text ou .data) que não pode ser modificada.

Mas quando você declara

	char str[] = "Uma string!";

você está pedindo para o compilador reservar memoria para o vetor na parte baixa da memoria e logo em seguida copiar o conteúdo do vetor para a memoria.

 

Essas declarações são praticamente a mesma.

 

considere esse "programa" t1.c

 


char*    outro0 = "42 char* ";
char*    outro1 = "43 char* ";
const char* fim = "fim";

 

E esse "programa" t2.c

 


char        outro0[] = "42 char[]";
char        outro1[] = "43 char[]";
const char* fim = "fim";

 

Na maquina que estou usando agora, em gcc 9.3 veja o código gerado para os dois, lado a lado
 

image.thumb.png.d8f615768afe961d95cd2d88b59e61e6.png

 

E é na prática a mesma coisa.

 

char[] é char * (decay). char* não é char[].

 

3 horas atrás, kgin disse:

Assim você só pode modificar o ponteiro

	char *str = "Uma string!";
	char outra_str[] = "Outra string!";
	str = outra_str;
	str[3] = 'a';

 

É assim você só pode modificar o conteúdo.

	char str[] = "Uma string!";
	str[0] = ' ';	/* OK */
	str = NULL; 	/* Vai dar erro! */

 

Pense na primeira como sendo uma constante que você não pode modificar

 

str é const char*. E importante declarar assim, em especial em listas de argumentos para funções. porque não se sabe como ela vai ser chamada. Veja o exemplo que mostrei acima em inverte("Clube do Hardware);

 

Você pode modificar o ponteiro porque não foi declarado const, que seria isso:
 

    char * const    str = "Uma string!";

 

que na verdade é isso:

 

    char const * const    str = "Uma string!";

 

Onde você não pode mudar a string porque ela é constante, mas não pode mudar o ponteiro porque ele é também costante....

 

 

Em geral nesses programas para iniciantes também não se pode usar nada de string.h, por exemplo nada de strlen() já que o objetivo é mostrar a manipulação dos bytes.

  • Curtir 1
  • Obrigado 1
Postado
6 horas atrás, kgin disse:
	char str[] = "Uma string!";

você está pedindo para o compilador reservar memoria para o vetor na parte baixa da memoria e logo em seguida copiar o conteúdo do vetor para a memoria.

Então eu suponho que essa forma é a de melhor performance, poide deve ser bem mais rápida.

6 horas atrás, kgin disse:

Assim você só pode modificar o ponteiro

	char *str = "Uma string!";
	char outra_str[] = "Outra string!";
	str = outra_str;
	str[3] = 'a';

eu to tentando fazer isso, mas acho que estou fazendo isso errado...
Olha só:
 

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

char *inverte(char *str)
{
	int tm = strlen(str);
	// agora dá pra fazer swap?
	char temp;
	for(int i=tm/2; i>=0; i--)
	{
		temp = str[i];
		str[i] = str[tm-i-1];
		str[tm-i-1] = temp;
	}
	// Pô! Num deu!	
	return str;
}

int main(void)
{
	char *nome = "abcdefghijklm";
	nome = inverte(nome);
	printf("%s\n", nome);
	return 0;
}


Aqui pra mim aparece falha na seguimentação. Imagem do núcleo gravada.

Não sei em que uma mensagem assim ajuda...

6 horas atrás, Midori disse:
    size_t T = strlen(nome);
    if(nome[T - 1] == '\n'){
        T -= 1;
        nome[T] = 0;
    }
    for(size_t i = 0; i < T/2; i += 1){
        char aux = nome[T - i - 1];
        nome[T - i - 1] = nome[i];
        nome[i] = aux;
    }
    return nome;

 

Bem legal! Mas não consigo fazer algo equivalente funcionar aqui quando o parametro de entrada é char*.
Eu acho difícil ser uma questão de diferença de compilador. Eu acho que to errando mesmo.

  • Curtir 1
Postado

 

Imagino que não leu nada do que eu expliquei. Revendo o tópico anterior
 

5 horas atrás, arfneto disse:

É melhor não fazer isso. A função fica menos útil porque nem sempre se quer inverter e zoar a original...

 

E dependendo de como ela foi chamada pode claro abortar o programa. Imagine

 

	inverte("Clube do Hardware);

 

:) e já era. 

 

E vejo que seu programa continua errado. O tal "já era" que eu escrevi é no seu caso o sigsegv, core dump. ABEND para quem trabalhava com mainframes 30 anos atrás.

 

Talvez deva ler o tópico acima, o #10, onde tem até exemplos completos em C e uma explicação de porque esses programas cancelam. E mais aquela coisa sobre array pointer decay...

 

Use
 

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

char *inverte(char *str)
{
    int tm = (int) strlen(str);
    // agora dá pra fazer swap?
    char temp;
    for (int i = tm / 2; i >= 0; i--)
    {
        temp            = str[i];
        str[i]          = str[tm - i - 1];
        str[tm - i - 1] = temp;
    }
    // Pô! Num deu!
    return str;
}

int main(void)
{
    char *nome = "abcdefghijklm";

    char reserva[50];
    char *p = reserva;
    p = inverte(nome);
    printf("%s\n", p);
    return 0;
}

 

E vai ver que funciona. E veja a razão explicada no tópico #10.

 

Como também está explicado lá, inverter na mesma string é uma ideia ruim, e provavelmente foi a que matou strrev() nos anos 80

 

Muitas vezes você não quer ou não pode zoar a string original.

 

E se alguém chamar
 

        inverte("abcdefghijklm"); 


uma coisa razoável --- e basicamente o mesmo que você fez acima ---  o programa vai cancelar com a mesma mensagem que você achou inútil.

 

De volta ao título, este é um exercício para iniciantes e essas são as atribuições:
 

Em 15/08/2021 às 22:54, Ana987546 disse:

Faça um programa que armazene n palavras em uma matriz. Em seguida, o
programa deverá exibir:
a) a quantidade de caracteres em cada palavra.
b) cada palavra invertida.

 

Então:
 

  • é pouco provável que a questão a) possa ser uma simples chamada a strlen(). string.h não deve estar entre as ferramentas possíveis. ;)
     
  • alocação dinâmica, ponteiros e coisas assim são desnecessárias aqui e talvez desconhecidas por alunos nessa fase
     
  • nem mesmo uma função está citada no enunciado e talvez seja ainda algo novo para esses alunos
     

Escrevendo em torno dos dados

 

Esse programa tem 3 fases:
 

  1. É preciso determinar com quantos nomes de até 50 letrinhas o programa vai trabalhar. São possíveis até 100 palavras. E elas devem ser armazenadas em uma matriz. É o que está no enunciado como publicado
     
  2. O programa deve tentar ler esse determinado número de palavras
     
  3. Para cada palavra o programa deve mostrar o comprimento da string e mostrar a palavra invertida

 

Em meio a esses exemplos todos, vou mostrar um em torno desses 3 passos

 

Exemplo, nesses 3 passos
 

#include <stdio.h>
int main(void)
{
    unsigned char n = 0;
    char    linha[10];
    char    nome[100][50];
    char    invertida[100][50];
    int     tam[100];   // sao 100 palavras de ate 50 bytes
    int     lidas = 0;

    // quantos vai ler
    printf("Total de nomes:  ");
    int res = scanf("%hhu", &n);  // sao 100 no maximo e sem sinal
    if (res != 1) return -1;  // não leu nada
    fgets(linha, 10, stdin); // tanto faz. so pra ler o enter
    if (n > 100) n = 100; // vai que...
    printf("Vai ler %hhu nomes:\n", n);

    // tenta ler
    for (lidas = 0; lidas < n; lidas++)
    {
        printf("\nDigite o nome %hhu de %hhu: ", 1+lidas, n);
        char* res = fgets(nome[lidas], 50, stdin);
        if (res == NULL) break;
    };

    printf(
        "\n\
Lidos %hhu de %hhu nomes possiveis\n\
Agora calcula os tamanhos e inverte os nomes\n\
Tecle ENTER para seguir\n",
        lidas, n);
    fgets(linha, 10, stdin);  // tanto faz. so pra ler o enter

    // processa o que leu
    for (int i = 0; i < lidas; i++)
    {
        // vai cuidar de nome[i]
        int len = 0;
        for (len = 0; len < 50; len += 1)
        {  // 49 letras no maximo, mais o 0 no fim
            if (nome[i][len] == '\n')
            {
                nome[i][len] = 0;
                tam[i]       = len;
                break;
            };  // if()
            // podem ser ate 49 letras
            if (nome[i][len] == 0)
            {
                tam[i] = len;
                break;
            }
        };  // for(len)

        // ja sabe o tamanho, inverte agora para aproveitar o loop
        // palavra = nome[i] e tamanho = tam[i]
        for (int j = tam[i]-1; j >= 0; j -= 1)
            invertida[i][tam[i]-j-1] = nome[i][j]; 
        invertida[i][tam[i]] = 0;
        printf(
            "\
    palavra #%d = \"%s\" tem %d letras. Invertida = \"%s\"\n",
            1 + i, nome[i], tam[i], invertida[i]);
    }
    return 0;
}

 

Que mostra por exemplo

 

Total de nomes:  225
Vai ler 100 nomes:

Digite o nome 1 de 100: Clube do Hardware

Digite o nome 2 de 100: escreva em torno dos dados

Digite o nome 3 de 100: ^Z

Lidos 2 de 100 nomes possiveis
Agora calcula os tamanhos e inverte os nomes
Tecle ENTER para seguir

    palavra #1 = "Clube do Hardware" tem 17 letras.\
    Invertida = "erawdraH od ebulC"
    palavra #2 = "escreva em torno dos dados" tem 26 letras.\
    Invertida = "sodad sod onrot me avercse"

 

Reparem no uso de fgets() como expliquei no início. SEMPRE testem o retorno de fgets() ou o programa pode entrar em loop. Nem sei quantas vezes me trouxeram problemas assim, e em muitos casos eram profissionais. Está no manual: fgets() não altera o valor do primeiro parâmetro se não ler nada ou se o arquivo terminar.

 

Fica claro com o mesmo valor de antes e o programa não tem como saber...

 

De uma referência comum: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm

 

image.png.689e54a3542f825a16bc2df1750843d1.png

 

 

No Unix / Linux / MacOS um

    man fgets 3

 

mostra desde os anos 80
 

image.png.31a212b8e560fb16a4f185de20ec313c.png

 

 

 

Postado
Citação

E vejo que seu programa continua errado.


É sim, por isso gosto de foruns como esse.

 

Citação

O tal 'já era' que eu escrevi é no seu caso o sigsegv, core dump. ABEND para quem trabalhava com mainframes 30 anos atrás.


Entendi foi PN de nada.

 

Citação

Talvez deva ler o tópico acima, o #10, onde tem até exemplos completos em C e uma explicação de porque esses programas cancelam. E mais aquela coisa sobre array pointer decay...


Obrigado! vou ler agora mesmo. Não vi que tinha coisa pra ler já aqui. Que legal, não é?
 

Citação
    char *nome = "abcdefghijklm";

    char reserva[50];
    char *p = reserva;

 

Realmente isso é um dos macetes da linguagem. Obrigado. Like pra você.
 

Legal!
funcionou:

 

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

char *inverte(char *str)
{
	int tm = strlen(str);
	char temp;
	for(int i=tm/2; i>=0; i--)
	{
		temp = str[i];
		str[i] = str[tm-i-1];
		str[tm-i-1] = temp;
	}
	return str;
}

int main(void)
{
	char nome[] = "abcdefghijklm";
	char *n = nome;
	n = inverte(n);
	printf("%s\n", n);
	return 0;
}

 

Citação

Como também está explicado lá, inverter na mesma string é uma ideia ruim, e provavelmente foi a que matou strrev() nos anos 80: 

 

Muitas vezes você não quer ou não pode zoar a string original.


Parece ser mais uma particularidade da linguagem...

Quem pensa de forma algoritmica, (pra qualquer linguagem ou o portugol), é quem deveria se sentir responsavel por tomar essa decisão de dizer se quer ou não alterar a string.
de qualquer forma, obrigado por dipor conhecimento.

Se a strrev morreu por isso, a culpa não é dos usuários?

Postado

 

Com o parâmetro da saída é só atribuir as posições com base no índice das duas entradas,

 

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

char *inverte(const char *fonte, char *saida){
    int T = (int)strlen(fonte) - 1;
    int i = 0;
    T -= fonte[T] == '\n';
    while(T >= 0){
        saida[i] = fonte[T];
        i += 1;
        T -= 1;
    }
    saida[i] = 0;
    return saida;
}

int main(void){
    char str[50];
    puts(inverte("Invertendo String", str));
    return 0;
}

 

Postado
3 horas atrás, codigo rápido disse:

Se a strrev morreu por isso, a culpa não é dos usuários?

 

É possível. Nunca pensei nisso.  Mas é como vender veneno ou água. A chance de alguém se dar mal aumenta para o caso do veneno. E alguém acaba se afogando com a água. A ideia é diminuir os riscos.

 

O que pensei e expliquei é que:    

  1. Usando a própria string a função fica muito menos útil, porque sempre destrói a origem, É como strtok(), outra que já devia ter sido extinta.
     
  2. Se a string de entrada for só de leitura vai cancelar o programa, como acontecia com o seu. Se você escreve a rotina e o usuário chamar por exemplo
     
        inverte( "Clube do Hardware");
  3. Se o programa dele cancelar você pode claro dizer que foi culpa dele, do usuário.
     
  4. Se a função aloca memória e retorna um endereço da string invertida é claro que é lícito. Esse lance de owning pointers é comum no mundo profissional e nos sistemas operacionais: devolve um ponteiro para uma coisa e o cara que recebeu se torna o pai dela. C foi escrita para isso. Mas em funções de uso amplo é melhor não usar. Ninguém garante que o programa chamador vai usar free() quando a string não estiver mais em uso. Você por exemplo não usou na primeira versão de seu programa, como apontou @kgin num tópico acima. valgrind existe por isso.
     
4 horas atrás, codigo rápido disse:

Parece ser mais uma particularidade da linguagem...

Quem pensa de forma algoritmica, (pra qualquer linguagem ou o portugol), é quem deveria se sentir responsavel por tomar essa decisão de dizer se quer ou não alterar a string

 

Não é uma particularidade. Qualquer linguagem que opere com o próprio parâmetro de entrada para o retorno vai ter o mesmo inconveniente. Imutabilidade é um conceito importante hoje em dia. 

 

Em Python, a linguagem da moda, strings são imutáveis, por exemplo. 

 

Por outro lado, pode ser um pouco exagerado dizer como quem vai usar uma função que a gente escreve vai usá-la ou como deva pensar. Em especial se a gente quer vende-la. ;) Ou se for o patrão.

 

As safe versions
 

A Microsoft tentou por décadas emplacar umas versões de scanf() e printf() chamadas de safe versions, mas nunca foram para o padrão. Coisas como scanf_s() que muitas vezes aparecem aqui no forum porque o usuário não lê o manual e não entende que precisa de um parâmetro a mais.

 

Uma versão segura de inverte

 

Uma versão segura de inverte:

  • não deve alocar memória, porque deixaria uma ponta solta.
  • tem que receber o tamanho da string, sem sinal, porque um programa com erro pode dar overflow e passar um número negativo.
  • tem que receber a string de entrada como const para que o sujeito possa chamar direto com a string
  • tem que receber o endereço de uma área de memória para colocar a string invertida, e checar para que o chamador não use a mesma como entrada e saída porque se ela for const vai cancelar o programa

É claro que pensar assim pode complicar as coisas para quem escreve. Mas assim é. Por isso programa de produção passa mais tempo prevendo o que pode sair errado do que resolvendo as coisas, muitas vezes. Basta ler o código de um sistema desses, tipo a libc ou uma biblioteca comum.

 

Um exemplo

 

    int inverte_s(const char *origem, char *destino, size_t tam);

 

Retornando o tamanho da string em caso de sucesso ou um número negativo em caso de erro, que pode ser causado por ponteiros nulos ou tentativa de usar o mesmo endereço como entrada e saída.

 

Um teste
 

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

int inverte_s(const char *origem, char *destino, size_t tam);

int main(void)
{
    char reserva[50];
    int res = 0;
    for (unsigned c = 1; c <= strlen("Clube do Hardware"); c += 1)
    {
        res = inverte_s("Clube do Hardware", reserva, c);
        printf(
            "inverte(\"%s\",%d) retornou %d e \"%s\"\n",
            "Clube do Hardware", c, res, reserva);
    };
    return 0;
}

 

Não testei exceto por esse programa. É só um exemplo grátis afinal. Mas acho que dá para entender a ideia.

 

O que importa é poder chamar com uma string direto na chamada, porque assim é mais útil.

 

 

O programa do tópico não precisa disso, e acho que não pode usar strlen() nem qualquer coisa de string.h, como já disse. É o comum para os exercícios desse tipo. Eu mostrei um exemplo assim.

 

A saída do tal programa
 

inverte("Clube do Hardware",1) retornou 2 e "C"
inverte("Clube do Hardware",2) retornou 3 e "lC"
inverte("Clube do Hardware",3) retornou 4 e "ulC"
inverte("Clube do Hardware",4) retornou 5 e "bulC"
inverte("Clube do Hardware",5) retornou 6 e "ebulC"
inverte("Clube do Hardware",6) retornou 7 e " ebulC"
inverte("Clube do Hardware",7) retornou 8 e "d ebulC"
inverte("Clube do Hardware",8) retornou 9 e "od ebulC"
inverte("Clube do Hardware",9) retornou 10 e " od ebulC"
inverte("Clube do Hardware",10) retornou 11 e "H od ebulC"
inverte("Clube do Hardware",11) retornou 12 e "aH od ebulC"
inverte("Clube do Hardware",12) retornou 13 e "raH od ebulC"
inverte("Clube do Hardware",13) retornou 14 e "draH od ebulC"
inverte("Clube do Hardware",14) retornou 15 e "wdraH od ebulC"
inverte("Clube do Hardware",15) retornou 16 e "awdraH od ebulC"
inverte("Clube do Hardware",16) retornou 17 e "rawdraH od ebulC"
inverte("Clube do Hardware",17) retornou 18 e "erawdraH od ebulC"

 

E o código de inverte_s
 

#include <stdio.h>

int inverte_s(const char *origem, char *destino, size_t tam)
{
    if (origem == NULL) return -1;
    if (destino == NULL) return -2;
    if (destino == origem) return -3;  // vai que...
    const char *p   = origem;          // uma nome curtinho = melhor
    size_t      len = 0;               // o indice da entrada
    for (len = 0; len < tam; len += 1)
        if ((*(p + len) == '\n') || (*(p + len) == 0)) break;
    len -= 1;  // desconta o 0 final
    // copia invertendo
    for (int i = (int)len; i >= 0; i -= 1)
        *(destino + len - i) = *(origem + i);
    *(destino + len + 1) = 0;  // termina a string
    return (int)len + 2;
}
// fim de inverte.c

 

@Midori veja que basta uma variável para o loop em

 

1 hora atrás, Midori disse:
    while(T >= 0){
        saida[i] = fonte[T];
        i += 1;
        T -= 1;
    }
    saida[i] = 0;

 

 

Compare com 
 

    // copia invertendo
    for (int i = (int)len; i >= 0; i -= 1)
        *(destino + len - i) = *(origem + i);
    *(destino + len + 1) = 0;  // termina a string

 

 

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