Buon libro C

kobeilprofeta
Ciao, cerco un buon libro sulla programmazione C con molti esempi ed esercizi con soluzioni. Sarebbe bello se trattasse in maniera approfondita il discorso sui puntatori e come utilizzarli nelle funzioni.
Grazie.

Risposte
Cronovirus
Di che livello? Sai già programmare?

kobeilprofeta
so già programmare, non bene ma sì.
Non so farti capire a che livello.

kobeilprofeta
PS: riesco a compilare ed eseguire ad esempio usando devc++. Vorrei farlo anche da [esegui--cmd], ma non so come fare. Ho provato gcc ma mi dice che gcc non è conosciuto.

Cronovirus
Questo è un buon libro di base che consigliano in molte università: programmazione in C di king.

Ma ce ne sono davvero tanti...

kobeilprofeta
"Cronovirus":
Questo è un buon libro di base che consigliano in molte università: programmazione in C di king.

Ma ce ne sono davvero tanti...



grazie. lo so che ce ne sono molti... per questo dicevo che ne cercavo uno che spiegasse meglio la questione di funzioni con array o puntatori come parametri

vict85
Il King non lo conoscevo. Penso che sia comunque aggiornato al penultimo standard. Che è comunque meglio di insegnare che tutte le variabili vanno inserite all'inizio del ciclo (che tra l'altro tutti i principianti leggono come all'inizio della funzione).

Di libri sufficientemente recenti ne conosco due
Kochan - Programming in C
Prata - C Primer Plus
Il secondo penso che sia il più completo in termini di nuovo standard. Il Kochan fa pochi accenni al C11, quindi è simile al King sotto questo aspetto. Molte delle nuove cose non le utilizzeresti, ma alcune hanno una loro utilità.

Non saprei dare consigli specifici perché io ho imparato il C come subset del C++ e poi affinato successivamente la mia conoscenza da varie fonti. Non ho quindi mai usato un libro specifico per impararlo.

Sull'aspetto del nuovo C, consiglierei 21st Century C di Klemens . Contiene tutta una serie di consigli su come usare il C in maniera meno K&R e copre aspetti spesso non trattati nei libri. Ovviamente vanno viste come opinioni, puoi decidere di comportarti in modo differente. In particolare è più adatto per l'utilizzo in ambiente linux o comunque Posix.

Esistono comunque libri specifici per alcuni aspetti. Per esempio se ti interessano i puntatori potresti essere interessato a Understanding and Using C Pointers di Reese o a libri più vecchi sullo stesso argomento.

Riguardo ad esempi già fatti esistono vari libri sugli algoritmi che presentano codici completi o quasi. D'altra parte è probabilmente più utile implementare cose a mano.

Detto questo disinstalla Dev-C++ e non riinstallarlo più. Su windows ti consiglierei di usare Code::Blocks+MinGW (ti basta installarti code::blocks) ed eventualmente installarti LLVM, ma farlo non è sempre semplicissimo. Un'alternativa non tanto conosciuta è usare Pelles C. È un compilatore solo C ed è abbastanza serio sul rispetto dello standard.

apatriarca
Partiamo dal discorso sull'esecuzione da linea di comando. Quando scriviamo qualcosa come "gcc" da linea di comando, cmd va a cercare un programma con quel nome nei percorsi contenuti nella variabile d'ambiente PATH e se la trova esegue il programma con gli argomenti che abbiamo inserito dopo il nome del comando. Nello stesso modo potrai eseguire anche i tuoi programmi. In linux gcc è spesso già installato, ma questo non è certamente il caso di Windows. Devi installarlo a parte o andare ad inserire il gcc incluso in Dev-C++ nel PATH in modo da poterlo chiamare come desideri. Sinceramente trovo che Dev-C++ faccia comunque pena come IDE per cui userei altro ma fai come vuoi.

Il libro ti andrebbe bene anche in inglese o solo in italiano? Molti libri tendono ad essere un po' datati sul C (i nuovi standard non sono piaciuti più di tanto e per un po' è stato abbastanza ignorato in ambiente mainstream). Ovviamente se ti interessa concentrarti principalmente sui puntatori qualcosa come Understanding and Using C Pointers può essere interessante. Immagino che per le basi possa andare bene quasi qualsiasi manuale (anche se su molte cose gira molta disinformazione - soprattutto tra le dispense dei professori).

kobeilprofeta
scusate, ne approfitto per chiedere una cosa:

mi serviva tenere dati in una matrice da passare poi ad una funzione... ho usato un puntatore a puntatore... è corretto come li ho inizializati e il controllo che ho fatto dopo l'allocazione?

grazie.



double **x=(double**)malloc(sizeof(double*)*p); //input dati
if (*x==NULL) {printf ("Error"); return -1;}
for (i=0;i<p;i++)
{   
x[i]=(double*)malloc(sizeof(double)*c);
if (x==NULL) {printf ("Error"); return -1;}
}
for (i=0;i<p;i++)
{   
for (j=0;j<c;j++)
{
    printf ("\nPersona %d, caratteristica %d: ",i,j);
    scanf ("%lf",&x[i][j]);
}
}

vict85
Il primo controllo è sbagliato, manca però la deallocazione. Non sono un fan dell'uso dei puntatori doppi per le matrici. Questo codice mostra il tuo codice un po' modificato e due alternative (simili). Il numero 2 è il più utilizzato nella pratica, ma il numero 3 ha vari vantaggi. Questo codice non compila in C89, né è compatibile con il C++. Nota che l'incompatibilità con il C++ sta nel numero 3 e nel particolare modo in cui ho scritto i malloc. Per i malloc le modifiche le puoi immaginare, per il numero 3 sei costretto a passare al numero 2.

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

int main(void)
{
	int nRows, nCols;

	do
	{
		printf("Inserire il numero di righe e colonne separati da un spazio: ");		
	} while( scanf("%i %i",&nRows, &nCols) != 2 );

	printf("M sara' una matrice %dx%d\n", nRows, nCols);

	double** M1 = (double**) malloc(sizeof(double*[nRows]));
	if( !M1 )
	{
		free(M1);
		perror("Allocazione matrice tipo 1 fallita");
		exit(EXIT_FAILURE);
	}
	for (int i=0; i != nRows; ++i)
	{   
		M1[i] = (double*) malloc(sizeof(double[nCols]));
		if( !M1[i] )
		{
			for(int j=0; j != i; ++j)
			{
				free(M1[j]);
			}
			free(M1);
			perror("Allocazione matrice tipo 1 fallita");
			exit(EXIT_FAILURE);
		}
	}

	double* M2 = (double*) malloc(sizeof(double[nRows][nCols]));
	if( !M2 )
	{
		for(int j=0; j != nRows; ++j)
		{
			free(M1[j]);
		}
		free(M1);
		free(M2);
		perror("Allocazione matrice tipo 2 fallita");
		exit(EXIT_FAILURE);
	}
	double (*M3)[nCols] = (double (*)[nCols]) malloc(sizeof(double[nRows][nCols])); // M3 è un puntatore a un array di nCols elementi.
	if( !M3 )
	{
		for(int j=0; j != nRows; ++j)
		{
			free(M1[j]);
		}
		free(M1);
		free(M2);
		free(M3);
		perror("Allocazione matrice tipo 3 fallita");
		exit(EXIT_FAILURE);
	}

	for (int i=0; i != nRows; ++i)
	{   
		for (int j=0; j != nCols; ++j)
		{
			double t;
    		printf ("Persona %d, caratteristica %d: ", i, j);
    		scanf ("%lf",&t);
			M1[i][j] = t;
			M2[i*nCols + j] = t;
			M3[i][j] = t;
		}
	}

	printf("\n");
	printf("Matrice Tipo 1\n");
	for (int i=0; i != nRows; ++i)
	{   
		for (int j=0; j != nCols; ++j)
		{
			printf("%.2lf\t", M1[i][j]);
		}
		printf("\n");
	}
	for(int j=0; j != nRows; ++j)
	{
		free(M1[j]);
	}
	free(M1);

	printf("\n");
	printf("Matrice Tipo 2\n");
	for (int i=0; i != nRows; ++i)
	{   
		for (int j=0; j != nCols; ++j)
		{
			printf("%.2lf\t", M2[i*nCols + j]);
		}
		printf("\n");
	}
	free(M2);

	printf("\n");
	printf("Matrice Tipo 1\n");
	for (int i=0; i != nRows; ++i)
	{   
		for (int j=0; j != nCols; ++j)
		{
			printf("%.2lf\t", M3[i][j]);
		}
		printf("\n");
	}

}

kobeilprofeta
"apatriarca":
Partiamo dal discorso sull'esecuzione da linea di comando. Quando scriviamo qualcosa come "gcc" da linea di comando, cmd va a cercare un programma con quel nome nei percorsi contenuti nella variabile d'ambiente PATH e se la trova esegue il programma con gli argomenti che abbiamo inserito dopo il nome del comando. Nello stesso modo potrai eseguire anche i tuoi programmi. In linux gcc è spesso già installato, ma questo non è certamente il caso di Windows. Devi installarlo a parte o andare ad inserire il gcc incluso in Dev-C++ nel PATH in modo da poterlo chiamare come desideri. Sinceramente trovo che Dev-C++ faccia comunque pena come IDE per cui userei altro ma fai come vuoi.

Il libro ti andrebbe bene anche in inglese o solo in italiano? Molti libri tendono ad essere un po' datati sul C (i nuovi standard non sono piaciuti più di tanto e per un po' è stato abbastanza ignorato in ambiente mainstream). Ovviamente se ti interessa concentrarti principalmente sui puntatori qualcosa come Understanding and Using C Pointers può essere interessante. Immagino che per le basi possa andare bene quasi qualsiasi manuale (anche se su molte cose gira molta disinformazione - soprattutto tra le dispense dei professori).


parto da te.
Ho installato dev perchè era l'unico che sono riuscito ad installare :oops:
...mi hanno già detto che non è buono.
Ho capito la storia del gcc che non è installato ma non ho capito come inserirlo. (mi hai detto nel PATH, ma sono ignorante, scusa :oops:)
Per il libro potrei provare anche in inglese (nonostante non sia ferrato in materia). Non mi interessano solo i puntatori, ma al momento mi sembrano l'argomento che mi lascia con più dubbi... specie il passaggio di puntatori o array(multidimensionali a volte) a funzioni.
Ora guardo il tuo link.
Grazie.

kobeilprofeta
"vict85":
Il primo controllo è sbagliato, manca però la deallocazione. Non sono un fan dell'uso dei puntatori doppi per le matrici. Questo codice mostra il tuo codice un po' modificato e due alternative (simili). Il numero 2 è il più utilizzato nella pratica, ma il numero 3 ha vari vantaggi. Questo codice non compila in C89, né è compatibile con il C++. Nota che l'incompatibilità con il C++ sta nel numero 3 e nel particolare modo in cui ho scritto i malloc. Per i malloc le modifiche le puoi immaginare, per il numero 3 sei costretto a passare al numero 2.

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

int main(void)
{
	int nRows, nCols;

	do
	{
		printf("Inserire il numero di righe e colonne separati da un spazio: ");		
	} while( scanf("%i %i",&nRows, &nCols) != 2 );

	printf("M sara' una matrice %dx%d\n", nRows, nCols);

	double** M1 = (double**) malloc(sizeof(double*[nRows]));
	if( !M1 )
	{
		free(M1);
		perror("Allocazione matrice tipo 1 fallita");
		exit(EXIT_FAILURE);
	}
	for (int i=0; i != nRows; ++i)
	{   
		M1[i] = (double*) malloc(sizeof(double[nCols]));
		if( !M1[i] )
		{
			for(int j=0; j != i; ++j)
			{
				free(M1[j]);
			}
			free(M1);
			perror("Allocazione matrice tipo 1 fallita");
			exit(EXIT_FAILURE);
		}
	}

	double* M2 = (double*) malloc(sizeof(double[nRows][nCols]));
	if( !M2 )
	{
		for(int j=0; j != nRows; ++j)
		{
			free(M1[j]);
		}
		free(M1);
		free(M2);
		perror("Allocazione matrice tipo 2 fallita");
		exit(EXIT_FAILURE);
	}
	double (*M3)[nCols] = (double (*)[nCols]) malloc(sizeof(double[nRows][nCols])); // M3 è un puntatore a un array di nCols elementi.
	if( !M3 )
	{
		for(int j=0; j != nRows; ++j)
		{
			free(M1[j]);
		}
		free(M1);
		free(M2);
		free(M3);
		perror("Allocazione matrice tipo 3 fallita");
		exit(EXIT_FAILURE);
	}

	for (int i=0; i != nRows; ++i)
	{   
		for (int j=0; j != nCols; ++j)
		{
			double t;
    		printf ("Persona %d, caratteristica %d: ", i, j);
    		scanf ("%lf",&t);
			M1[i][j] = t;
			M2[i*nCols + j] = t;
			M3[i][j] = t;
		}
	}

	printf("\n");
	printf("Matrice Tipo 1\n");
	for (int i=0; i != nRows; ++i)
	{   
		for (int j=0; j != nCols; ++j)
		{
			printf("%.2lf\t", M1[i][j]);
		}
		printf("\n");
	}
	for(int j=0; j != nRows; ++j)
	{
		free(M1[j]);
	}
	free(M1);

	printf("\n");
	printf("Matrice Tipo 2\n");
	for (int i=0; i != nRows; ++i)
	{   
		for (int j=0; j != nCols; ++j)
		{
			printf("%.2lf\t", M2[i*nCols + j]);
		}
		printf("\n");
	}
	free(M2);

	printf("\n");
	printf("Matrice Tipo 1\n");
	for (int i=0; i != nRows; ++i)
	{   
		for (int j=0; j != nCols; ++j)
		{
			printf("%.2lf\t", M3[i][j]);
		}
		printf("\n");
	}

}


ok...
ci sono un po' di cose che non ho capito, ad esempio:
fai: double* M2 = (double*) malloc(sizeof(double[nRows][nCols]));
Ma a me sembra che così M2 abbia una dimensione in meno del previsto... in M1 tu facevi prima: double** M1 = (double**) malloc(sizeof(double*[nRows])); e poi M1 = (double*) malloc(sizeof(double[nCols]));
...non so come spiegarlo, ma a me sembra che manchi un passaggio (per dirla facile: quello con i due asterischi)

poi un'altra cosa: neanch'io sono un fan delle matrici con puntatori doppi :-D, solo che non riuscivo con gli array bidimensionali, allora ho provato l'altra strada....

ps: a dire il vero non sono fan neanche degli array bidimensionali :-D

vict85
È quello dei due asterischi che ha un passaggio in più. Un array multidimensionale normale/statico è la versione statica del metodo 3. Quando lo passi ad una funzione, per esempio scrivendo f(int ar[][3]), stai di fatto definendo ar come un puntatore ad un array di dimensione 3 ovvero come int (* ar)[3]. Mentre l'uso dei doppi puntatori è solo all'apparenza simile al metodo normale. In memoria il tutto è molto diverso.

Non puoi per esempio usare una funzione scritta per un array puntatori con un array doppio statico, mentre puoi tranquillamente farlo se usi il metodo 2.

vict85
Ho avuto un attimo di tempo e ho implementato questo per farti vedere quello che intendo:

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

/* Calcola R = a*AB + c*C
 *      R - matrice m x n in cui verrà memorizzato il risultato 
 *      A - matrice m x p che viene moltiplicata a B e per lo scalare alpha
 *      B - matrice p x n che viene moltiplicata a A e per lo scalare alpha
 *      C - matrice m x n che viene sommata al prodotto
 *         a - coefficiente associato al prodotto AB
 *      c - coefficiente associato alla matrice C
 *      m - numero di righe di R, A e C
 *      n - numero di colonne di R, B e C
 *      p - numero di colonne di A e righe di B
 */
void multiply_and_add(double R[], double const A[], double const B[], double const C[], double const a, double const c, int const m, int const n, int const p);

void print_matrix(double const[], int const, int const, char const[]);

int main(void)
{
	double const *A, *B, *C;
	double *R;

	A = (double[6]) 	{ 0., 2., 4., 1., 1., 5.};
	B = (double[6]) 	{ 1., 0., 1., -2., -1., -1.};
	C = (double[4]) 	{ 1., -1., -1., 1.};
	R = (double *)malloc(4 * sizeof(double));

	for (int i = 0; i != 4; ++i)
	{
		R[i] = 0.;
	}

	print_matrix(A, 2, 3, "A");
	printf("\n");
	print_matrix(B, 3, 2, "B");
	printf("\n");
	print_matrix(B, 2, 3, "B sbagliata");
	printf("\n");
	print_matrix(C, 2, 2, "C");
	printf("\n");
	print_matrix(R, 2, 2, "R");
	printf("\n \n");

	double const As[2][3] = { {0., 2., 4.}, {1., 1., 5.} };
	double const Bs[3][2] = { {1., 0.}, {1., -2.}, {-1., -1.} };
	double const Cs[2][2] = { {1., -1.}, {-1., 1.} };
	double Rs[2][2] = { 0. };

	print_matrix(As[0], 2, 3, "As");
	printf("\n");
	print_matrix(Bs[0], 3, 2, "Bs");
	printf("\n");
	print_matrix(Cs[0], 2, 2, "Cs");
	printf("\n");
	print_matrix(Rs[0], 2, 2, "Rs");
	printf("\n \n");

	double const a = 7.3;
	double const c = 0.5;

	printf("Inizio calcoli dinamici...\n");
	multiply_and_add(R, A, B, C, a, c, 2, 2, 3);
	print_matrix(R, 2, 2, "R");
	printf("\n \n");

	free(R);

	printf("Inizio calcoli statici...\n");
	multiply_and_add(Rs[0], As[0], Bs[0], Cs[0], a, c, 2, 2, 3);
	print_matrix(Rs[0], 2, 2, "Rs");
	printf("\n \n");
}

void multiply_and_add(double R[], double const A[], double const B[], double const C[], double const a, double const c, int const m, int const n, int const p)
{
	for (int i = 0; i != m * n; ++i)
		R[i] = c * C[i];

	if (a != 0)
	{
		for (int i = 0; i != m; ++i)
		{
			for (int k = 0; k != p; ++k)
			{
				double const A_ik = A[i * p + k];
				for (int j = 0; j != n; ++j)
				{
					double const B_kj = B[k * n + j];
					R[i * n + j] += a * A_ik * B_kj;
				}
			}
		}
	}
}

void print_matrix(double const *Mat, int const m, int const n, char const nome_var[])
{
	printf("%s = [%i, %i]\n", nome_var, m, n);
	for (int i = 0; i != m - 1; ++i)
	{
		printf("[%i][*] = { ", i);
		for (int j = 0; j != n; ++j)
		{
			printf("%.2lf, ", Mat[i * n + j]);
		}
		printf("},\n");
	}
	printf("[%i][*] = { ", m);
	for (int j = 0; j != n; ++j)
	{
		printf("%.2lf, ", Mat[(m - 1) * n + j]);
	}
	printf("} }\n");
}


Il codice calcola il prodotto \(\displaystyle R = \alpha AB +\beta C \). E mostra come il codice fatto per le matrici implementate nel secondo modo funziona anche per della matrici multidimensionali statiche. In realtà c'è una sola matrice definita dinamicamente.

Non è compatibile con il C++ per via del modo in cui ho inizializzato A, B e C. Immagino che alcuni compilatori lo permettano anche nel C++.

kobeilprofeta
ciao, grazie again per le risposte.
ora non ho a portata il PC, appena lo ho eseguo il codice.
L'ho comunque guardato cercando di capirlo.

A,B,C,R sono matrici... perchè le dichiari come puntatori singoli? poi ad esempio non ho capito perchè dichiari As[2][3] e poi usi As[0]... intendi la prima riga, cioè {0,2,4}?

vict85
As[0] è un array di 3 elementi. Passare un array ad una funzione equivale a passare un puntatore al primo elemento dell'array. Quindi è un po' come se avessi scritto &As[0][0] . La dimensione dell'array viene ignorata dal C. Il problema è che As è di tipo double (*)[3] e non di tipo double[] .

Riguardo A, B e C sono puntatori a dei compound literal ovvero a delle variabili statiche anonime. Non soffermartici troppo. È un po' come se avessi puntato A ad As. R invece è un array dinamico a tutti gli effetti. Quando comincerai a studiarti un po' di sintassi lo incontrerai, seppur probabilmente non subito.

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.