Conta un carattere di una stringa

di Vincenzo.79 il
17 risposte

Buongiorno a tutti, sto facendo un esercizio come da titolo, devo inserire una stringa e poi un carattere; contare quante volte il char è presente nella stringa...tutto deve essere fatto con un loop da quale esco solo quando l'utente inserisce uno spazio...

Il problema è il programma gira ma solo la prima volta che inserisco il carattere...già al secondo inserimento, ottengo sempre 0...

Grazie a chi risponde!

Posto il codice: 

# ESAME del 24 Giugno 2026
#
# Scrivere un programma assembly che allochi una stringa al più di 255 caratteri. Il main legge in input
# la stringa e, in loop, un carattere del quale conteggiare le occerrenze all'interno della stringa fornita.
# Il loop di input potrà essere terminato fornendo uno spazio (codice ASCII 32). Una funzione 
#	conta_occorrenze riceve il puntatore alla stringa ed il carattere da cercare come parametri e ritorna
# il suo numero di occorrenze nella stringa, che sarà opportunamente stampato nel main. 
# Maiuscole e minuscole devono essere considerate equivalenti
	
.global _main

.data
	prompt_string:		.asciz "\n Inserire una frase: "
	size:						.space 255
	prompt_char:		.asciz "\n Inserire il carattere da ricercare (spazio per uscire): "
	print_char:			.asciz "\n Numero di caratteri trovati: "
	
.text
_main:
	la a0, prompt_string
	li a7, 4
	ecall
	li a7, 8
	li a1, 255
	ecall
	mv t2, a0

loop:	
	la a0, prompt_char
	li a7, 4
	ecall
	li a7, 12
	ecall
	mv t0, a0					# Salva il carattere inserito nel registro t0 
	
	li t1, 32					# Carattere terminatore
	beq t0, t1, end
	jal ra, conta_occorrenze	# Richiamo della funzione per il conteggio
	
	la a0, print_char
	li a7, 4
	ecall
	mv a0, t4					# Viene spostato il conteggio del char nel registro a0
	li a7, 1
	ecall
	j loop

end:
# Fine programma
	li a7, 10
	ecall
	
# Funzione per il conteggio dei caratteri
conta_occorrenze:
	li t4, 0					# Inizializzare il contatore
	ciclo:	
		lb t3, 0(t2)			# Caricare il prossimo carattere della stringa in t2
		beqz t3, fine				
		bne t3, t0, jump		# Se i caratteri sono differenti salta a jump
		addi t4, t4, 1			# Incrementa il contatore, vuol dire che i due caratteri sono uguali
	jump:
		addi t2, t2, 1
		j ciclo
	fine:
#		mv a0, t4				# Commentanto perchè altrimenti non ho il conteggio
	ret
	

17 Risposte

  • Spiega passo passo cosa fa.

  • Questo è ciò che fa:

    il compito chiede:

    Maiuscole e minuscole devono essere considerate equivalenti

  • 17/07/2026 - sihsandrea ha scritto:

    Questo è ciò che fa:

    il compito chiede:

    Maiuscole e minuscole devono essere considerate equivalenti

    Fin qui ci siamo, solo che se provi ad inserire un altro carattere il risultato è sempre 0...

    PS: la richiesta maiuscole - minuscole ancora non l'ho gestita...pensavo di farlo con le funzioni "ori" e "andi", ma secondo te come potrei fare per considerarle equivalenti?

    Pensavo di convertire l'intera stringa tutta in minuscolo e poi effettuare il conteggio...

  • 18/07/2026 - Vincenzo.79 ha scritto:

    se provi ad inserire un altro carattere il risultato è sempre 0...

    Ma il registro t2, quando lo azzeri nella funzione?

    Io sposterei anche 

    li t1, 32

    prima del loop. Farlo ad ogni ciclo è uno spreco.

  • 20/07/2026 - Sgrubak ha scritto:

    Io sposterei anche 

    li t1, 32

    prima del loop. Farlo ad ogni ciclo è uno spreco.

    Grazie in effetti, fatto...

    20/07/2026 - Sgrubak ha scritto:

    Ma il registro t2, quando lo azzeri nella funzione?

    Ho provato ad azzerare anche il registro t2 ma cmq da sempre lo stesso errore, ma poi inserendo questo comando, tornerei all'inizio della frase...non ho capito bene a cosa servirebbe!

    cmq ho provato ad inserirlo nei diversi punti della funzione ma non cambia il risultato...all'inserimento del secondo carattere ho sempre il contatore a zero...

  • 20/07/2026 - Vincenzo.79 ha scritto:

    non ho capito bene a cosa servirebbe!

    Ma la funzione deve scansionare l'intera frase sempre, no? Per quello, così come azzeri t4 dovresti azzerare t2.

    Se stampi a video t2 dopo l'esecuzione della funzione, lo troverai ogni volta aumentato quando invece dovrebbe essere costante ed uguale alla lunghezza della frase. Se t2 aumenta indiscriminatamente, con la lb vai a leggere in zone di memoria che non c'entrano nulla con la stringa in esame.

  • 20/07/2026 - Sgrubak ha scritto:

    20/07/2026 - Vincenzo.79 ha scritto:

    non ho capito bene a cosa servirebbe!

    Ma la funzione deve scansionare l'intera frase sempre, no? Per quello, così come azzeri t4 dovresti azzerare t2.

    Se stampi a video t2 dopo l'esecuzione della funzione, lo troverai ogni volta aumentato quando invece dovrebbe essere costante ed uguale alla lunghezza della frase. Se t2 aumenta indiscriminatamente, con la lb vai a leggere in zone di memoria che non c'entrano nulla con la stringa in esame.

    Scusami se possa sembrare rompiscatole, vediamo se ho capito bene cosa stai dicendo:

    Dopo aver inserito la frase, digito il carattere da ricerca...ecco è presente 3 volte ed ottengo il risultato corretto; 

    a questo punto, quando inserisco la seconda lettere devo rileggere la frase dall'inizio, per questo ho bisogno di azzerare il registro t2, giusto???

    Se è così, dove e come devo re-inizializzare t2?

    PS: ho provato con li t2, 0 in diversi punti della funzione e fuori ma ho sempre lo stesso problema...

    Ti ringrazio anticipatamente

  • 20/07/2026 - Vincenzo.79 ha scritto:

    ho provato con li t2, 0 in diversi punti della funzione

    Subito vicino a

    li t4, 0
    li t2, 0

    20/07/2026 - Vincenzo.79 ha scritto:

    e fuori

    nel "fuori" di quel che capita nella funzione non deve interessare secondo me. Già che non ci sono le sw/lw per spostare sullo stack quel che avevi prima nei registri t non mi convince molto. Ora la situazione è semplice, ma se inizi a voler gestire upper case e lower case probabile che ti serve una funzione dedicata e, continuando ad annidare funzioni, ad un certo punto i registri li finisci. Meglio iniziare a gestirli da subito.

    Infine, credo sia un "errore" anche non spostare il risultato della funzione in a0 dentro al funzione stessa. Si va contro la convenzione. Non che non sia funzionale (in effetti ti funziona almeno la prima volta) ma se c'è una convenzione un motivo ci sarà. :-)

  • Ho provato come mi ha consigliato di inserire il comando per azzerare t2, ma non funzione in nessun modo...esce sempre questo messaggio di errore:

    Error in C:\Users\Vincenzo\Desktop\Esercizi RiscV\Esame 24-06-26.asm line 59: Runtime exception at 0x00400078: address out of range 0x00000000
    

    21/07/2026 - Sgrubak ha scritto:

    Infine, credo sia un "errore" anche non spostare il risultato della funzione in a0 dentro al funzione stessa. Si va contro la convenzione. Non che non sia funzionale (in effetti ti funziona almeno la prima volta) ma se c'è una convenzione un motivo ci sarà. :-)

    intendi questo comando: 

    fine:
    #		mv a0, t4				# Commentanto perchè altrimenti non ho il conteggio
    	ret

    Per quanto riguarda l'altra richiesta, upper case - lower case, stavo prima gestendoli a parte e poi vedere come inserirli nel programma;

    PS: Quindi in definitiva, i registri sw/lw (in effetti lo stack) li devo usare quando termino quelli temporanei e quando ho funzioni non foglia

  • 21/07/2026 - Vincenzo.79 ha scritto:

    esce sempre questo messaggio di errore:

    Giusto, perché t2 non è 0... Allora userei t5 come contatore per la frase ->

    # Funzione per il conteggio dei caratteri
    conta_occorrenze:
    	li t4, 0					# Inizializzare il contatore
    	mv t5, t2                   # Inizializzo t5 che userò per iterare la frase
    	ciclo:	
    		lb t3, 0(t5)			# Caricare il prossimo carattere della stringa in t5
    		beqz t3, fine				
    		bne t3, t0, jump		# Se i caratteri sono differenti salta a jump
    		addi t4, t4, 1			# Incrementa il contatore, vuol dire che i due caratteri sono uguali
    	jump:
    		addi t5, t5, 1          # Passo al carattere successivo della frase
    		j ciclo
    	fine:
    #		mv a0, t4				# Commentanto perchè altrimenti non ho il conteggio
    	ret
  • 21/07/2026 - Sgrubak ha scritto:

    21/07/2026 - Vincenzo.79 ha scritto:

    esce sempre questo messaggio di errore:

    Giusto, perché t2 non è 0... Allora userei t5 come contatore per la frase ->

    # Funzione per il conteggio dei caratteri
    conta_occorrenze:
    	li t4, 0					# Inizializzare il contatore
    	mv t5, t2                   # Inizializzo t5 che userò per iterare la frase
    	ciclo:	
    		lb t3, 0(t5)			# Caricare il prossimo carattere della stringa in t5
    		beqz t3, fine				
    		bne t3, t0, jump		# Se i caratteri sono differenti salta a jump
    		addi t4, t4, 1			# Incrementa il contatore, vuol dire che i due caratteri sono uguali
    	jump:
    		addi t5, t5, 1          # Passo al carattere successivo della frase
    		j ciclo
    	fine:
    #		mv a0, t4				# Commentanto perchè altrimenti non ho il conteggio
    	ret

    Ottimo, provato sembra funzionare tutto...

    una sola cosa...ma il codice "mv t5, t2", che è il comando addi t5, t2, 0, in effetti non prende la stringa ma solo il conteggio dei caratteri che compongono la stringa...giusto?

    PS: nel pomeriggio cerco di inserire la parte upper - lower case...speriamo bene

    Grazie mille

  • 21/07/2026 - Vincenzo.79 ha scritto:

    giusto?

    No, ripristina l'indirizzo di memoria in cui comincia la stringa. Quando tu scrivi il blocco

    	li a7, 8
    	li a1, 255
    	ecall
    	mv t2, a0

    con la mv, non stai spostando la stringa perché in a0 c'è solo l'indirizzo inziale. Poi tu sai che la stringa non occuperà più di 255 byte perché è quanto hai impostato in a1. Conoscendo lil punto d'inizio e la lunghezza, allora la stringa la si può ricostruire.

    In pratica adesso stiamo facendo in modo che venga copiato in t5 l'indirizzo iniziale che è stato impostato in t2 nel main. Poi viene incrementato questo t5, passando quindi al successivo, e ne leggiamo "l'offset 0". Ma non toccando più t2, sappiamo sempre da dove iniziare al giro successivo. L'indirizzo iniziale resterà memorizzato li e non verrà più toccato.

    Che è il motivo per cui non funzionava prima: alla prima invocazione della funzione t2 era a posto. Alla fine, t2 era incrementato e quindi al passaggio successivo leggevi un indirizzo che non c'entrava nulla. Ci fosse stata memorizzata un'altra stringa in quella porzione di memoria, avrebbe contato ed ti avrebbe restituito risultati apparentemente a casaccio.

  • 21/07/2026 - Sgrubak ha scritto:

    21/07/2026 - Vincenzo.79 ha scritto:

    giusto?

    No, ripristina l'indirizzo di memoria in cui comincia la stringa. Quando tu scrivi il blocco

    	li a7, 8
    	li a1, 255
    	ecall
    	mv t2, a0

    con la mv, non stai spostando la stringa perché in a0 c'è solo l'indirizzo inziale. Poi tu sai che la stringa non occuperà più di 255 byte perché è quanto hai impostato in a1. Conoscendo lil punto d'inizio e la lunghezza, allora la stringa la si può ricostruire.

    In pratica adesso stiamo facendo in modo che venga copiato in t5 l'indirizzo iniziale che è stato impostato in t2 nel main. Poi viene incrementato questo t5, passando quindi al successivo, e ne leggiamo "l'offset 0". Ma non toccando più t2, sappiamo sempre da dove iniziare al giro successivo. L'indirizzo iniziale resterà memorizzato li e non verrà più toccato.

    Che è il motivo per cui non funzionava prima: alla prima invocazione della funzione t2 era a posto. Alla fine, t2 era incrementato e quindi al passaggio successivo leggevi un indirizzo che non c'entrava nulla. Ci fosse stata memorizzata un'altra stringa in quella porzione di memoria, avrebbe contato ed ti avrebbe restituito risultati apparentemente a casaccio.

    Ok perfetto, molto meglio ora...passo al lower - upper case, vediamo che ne viene fuori

  • Non vorrei creare un nuovo post inutile, per cui posto qui la sezione di codice che ho implementato per l'upper e lower case...

    L'ho provato solo per un char ed ho cercato di svolgerlo utilizzando le funzioni

    .global _start
    .data
    	in_char:			.asciz "\n Inserisci un carattere (space to exit): "
    	size:					.space 2
    	out_char:			.asciz "\n Il carattere convertito: "
    	new_line:			.asciz "\n"
    .text 
    _start:
    loop:
    	la a0, in_char
    	li a7, 4
    	ecall
    	la a1, size
    	li a7, 12
    	ecall
    	mv t0, a0
    	
    	li t1, 32
    	beq t0, t1, end
    	
    	li t2, 65				# t2 = 'A'
    	li t3, 96				# t3 = 'Z'
    	
    	blt t0, t2, upper
    	bgt t0, t3, upper
    	
    	jal ra, char_lower
    	j print_char
    	
    upper:
    	jal ra, char_upper
    
    print_char:	
    	la a0, out_char
    	li a7, 4
    	ecall
    	mv a0, t0
    	li a7, 11
    	ecall
    	j loop
    
    end:
    la a0, new_line
    li a7, 4
    ecall
    # Fine del programma
    li a7, 10
    ecall
    
    # Funzione stampa minuscolo
    char_lower:
    	ori t0, t0, 32					# Conversione in minuscolo
    	ret
    	
    # Funzione stampa maiuscolo
    char_upper:
    	andi t0, t0, 223			# Conversione in maiuscolo
    	ret
    

    Il programma funziona, così sembra, ora devo provare a convertirlo per una stringa...

    Potrei migliorare qualcosa secondo voi?

Devi accedere o registrarti per scrivere nel forum
17 risposte