Newsgroup: it.comp.lang.c
Titolo: Strano problema con struct, stringhe e puntatori

Ciao a tutti,
ho il seguente problema: per motivi di studio sto scrivendo un programma e
ho necessità di convertire i campi (di tipo char[]) di una struct in
un'unica stringa di lunghezza prefissta, e poi viceversa splittando la
stringa e rimettendo le varie parti in una struct uguale alla prima.
L'ho già scritto, ma come potrete vedere c'è qualcosina che non funziona
e non riesco proprio a venirne a capo.
Eccolo qui:
---8<---------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define		NAME	16
#define		SURN	20
#define		CITY	16
#define		TLEN	(NAME+SURN+CITY)
#define		MLEN	(TLEN+1)
struct foo_customer
{
	char name[NAME];
	char surn[SURN];
	char city[CITY];
};
typedef struct foo_customer customer;
char *struct_to_string(customer *pstruct);
customer *string_to_struct(char *string);
void strcatwpad(char *dest, char *src, size_t len);
char *substring(char *src, int base, size_t len);
char *substring(char *src, int base, size_t len)
{
	int i;
	char *tmp = malloc(sizeof(char)*len);
	for(i=0; i<len; i++)
	{
		*(tmp+i) = *(src+base+i);
	}
	*(tmp+i) = '\0';
	return tmp;
}
/*
	questa funzione si occupa di concatenare tutti i campi stringa di una
	struttura (passata per riferimento) restituendo un puntatore alla stringa
	che li raffigura. essa, facendo uso della funzione "strcatwpad()", puo'
	essere considerata analoga ai metodi "toString()" del linguaggio Java.
*/
char *struct_to_string(customer *pstruct)
{
	char *ret = malloc(sizeof(char)*MLEN);
	strcatwpad(ret, pstruct->name, NAME);
	strcatwpad(ret, pstruct->surn, SURN);
	strcatwpad(ret, pstruct->city, CITY);
	ret[TLEN] = '\0'; /* stringa terminata dal carattere nullo */
	return ret;
}
/*
	questa funzione si occupa di splittare una stringa per poi posizionare le
	varie parti nei rispettivi campi della struttura.
*/
customer *string_to_struct(char *string)
{
	customer *ret = malloc(sizeof(customer));
	printf("#%s#\n", substring(string, 0, NAME)); /* printf di prova, sembra ok */
	printf("#%s#\n", substring(string, NAME, SURN)); /* printf di prova, sembra ok */
	printf("#%s#\n", substring(string, NAME+SURN, CITY)); /* printf di prova, sembra ok */
	strncpy(ret->name, substring(string, 0, NAME), NAME); /* POSSIBILE ERRORE QUI (?) */
	strncpy(ret->surn, substring(string, NAME, SURN), SURN); /* POSSIBILE ERRORE QUI (?) */
	strncpy(ret->city, substring(string, NAME+SURN, CITY), CITY); /* POSSIBILE ERRORE QUI (?) */
	return ret;
}
/*
	questa funzione si occupa di concatenare la stringa puntata da src alla
	stringa puntata da dest. len e' la lunghezza massima consentita e, in caso
	la lunghezza della stringa di origine fosse minore del valore di len, la
	stringa di destinazione dest viene riempita (padding) con un carattere
	determinato dall'utente fino a raggiungere la lunghezza len.
*/
void strcatwpad(char *dest, char *src, size_t len)
{
	int srclen = strlen(src);
	char pad[] = "-"; /* carattere per il padding */
	if (srclen<len)
	{
		int i;
		int rng = len - srclen;
		strcat(dest, src);
		for (i=0; i<rng; i++)
			strcat(dest, pad);
	}
	else
		strncat(dest, src, len);
}
int main()
{
	customer uno;
	customer due;
	customer *puno;
	customer *pdue;
	puno = &uno;
	pdue = &due;
	strncpy(
uno.name
, "Mickey", NAME);
	strncpy(uno.surn, "Mouse", SURN);
	strncpy(uno.city, "Topolinia", CITY);
	printf("> NOME: _%s_ (%d)\n", 
uno.name
, strlen(
uno.name
));
	printf("> COGN: _%s_ (%d)\n", uno.surn, strlen(uno.surn));
	printf("> CITT: _%s_ (%d)\n", uno.city, strlen(uno.city));
	printf("STRINGA_FINALE: _%s_ (%d)\n", struct_to_string(puno), strlen(struct_to_string(puno)));
	pdue = string_to_struct(struct_to_string(puno));
	printf("< NOME: _%s_ (%d)\n", pdue->name, strlen(pdue->name)); /* QUI QUALCOSA NON VA!!! */
	printf("< COGN: _%s_ (%d)\n", pdue->surn, strlen(pdue->surn)); /* QUI QUALCOSA NON VA!!! */
	printf("< CITT: _%s_ (%d)\n", pdue->city, strlen(pdue->city)); /* QUI QUALCOSA NON VA!!! */
	return 0;
}
---8<---------------------------------------------------------------------
Secondo voi dov'è che sbaglio?
Uso con GNU/Linux e il compilatore è GCC 3.4.5.
Grazie mille a tutti, ciao!
-- 
      ______________
(*)--[::::::::::::::]--(*)
(*)--[>::d1s4st3r::<]--(*)
(*)--[::::::::::::::]--(*)
(*)--[::wonder'ing::]--(*)
(*)--[::what's::::::]--(*)
(*)--[::^happening::]--(*)
(*)--[::|^::::::::::]--(*)
(*)--[__||__________]--(*)

 strncpy(uno.city, "Topolinia", CITY);
copi piu' byte delle stringhe in questione... questa non e' cosa buona
dato che esci fuori
dalla memoria allocata.
usa strcpy
Valerio



In realtà, da "man strncpy":
"In the case where the length of src is less than that of n, the remainder
of dest will be padded with nulls."
Ho scelto di usare "strncpy()" perchè al posto di "Mickey", "Mouse" e
"Topolinia" dovro' usare delle stringhe che non conosco a priori, e voglio
quindi far sì che "not more than n bytes of src are copied.".
I valori specificati dai "#define" sono le lunghezze massime che non
devono essere superate.
Comunque non riesco ancora a capacitarmi del perchè gli ultimi 3 printf
diano come output:
< NOME: _Mickey----------Mouse---------------Topolinia-------_ (52)
< COGN: _Mouse---------------Topolinia-------_ (36)
< CITT: _Topolinia-------_ (16)
anzichè:
< NOME: _Mickey----------_ (52)
< COGN: _Mouse---------------_ (36)
< CITT: _Topolinia-------_ (16)
visto che la funzione "substring()" parrebbe funzionare!
Boh! :-O
-- 
      ______________
(*)--[::::::::::::::]--(*)
(*)--[>::d1s4st3r::<]--(*)
(*)--[::::::::::::::]--(*)
(*)--[::wonder'ing::]--(*)
(*)--[::what's::::::]--(*)
(*)--[::^happening::]--(*)
(*)--[::|^::::::::::]--(*)
(*)--[__||__________]--(*)
 |......................|
http://xoomer.virgilio.it/
 +------> mental_insomnia/
(*) <-------------------+

 }
prima copi len caratteri dentro tmp e poi ci aggiungi lo \0:
temp e' allocato solo per len caratteri...
Valerio

< CITT: _Topolinia-------_ (16)
il problema e' che mancano le terminazioni!!
In C le stringhe sono 'simulate' da sequenze di caratteri terminate da
\0; praticamente tutte le funzioni della lib standard si basano su
questa informazione.
Infatti la printf ti stampa la sequenza di caratteri che inizia sul
puntatore
pdue->name finche' non trova il terminatore \0 che pero' sta solo alla
fine di pdue->city.
Questo succede perche' hai allocato il tutto senza considerare lo
spazio per i
terminatori e perche' i campi di una struttura in memoria sono
adiacenti.
Quindi succede che se anche tu il terminatore lo metti alla fine del
nome,
in realta' lo stai scrivendo sul primo byte del cognome.. e cosi via..
Valerio
Ps
Sbaglio o non ho visto neanche una free :/ ?

 Sbaglio o non ho visto neanche una free :/ ?
No, non sbagli :-)
Me le ero lasciate per ultime, adesso le metto!
Grazie ancora!
