Δείκτες - γενικά

Συζητήσεις για την γλώσσα C και C++

Συντονιστές: WebDev Moderators, Super-Moderators

Απάντηση
Άβαταρ μέλους
vassilism
Δημοσιεύσεις: 1950
Εγγραφή: 17 Μαρ 2007 14:47
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από vassilism » 20 Οκτ 2008 23:05

Παιδιά καλησπέρα σας.

Μετά από αρκετά χρόνια ξεθάβω πάλι την θεωρία της C και μου δημιουργούνται ξανά απορίες τις οποίες δεν είχα κατανοήσει στο παρελθόν.

Η ερώτησή μου έχει να κάνει με δείκτες.

Με τους δείκτες μπορούμε να έχουμε άμεση προσπέλαση στη μνήμη.
Επίσης μπορούμε να δούμε ποια είναι η θέση μνήμης μιας μεταβλητής.
Ωραία μέχρι εδώ.

Αυτό που δεν έχω καταλάβει είναι σε ποιες περιπτώσεις τους χρησιμοποιούμε.
Μπορούν να κάνουν ένα πρόγραμμα γρηγορότερο?
Μπορεί να δώσει κάποιος ένα παράδειγμα με απλά λόγια μήπως και το καταλάβω?

Άβαταρ μέλους
bxenos
Δημοσιεύσεις: 53
Εγγραφή: 18 Αύγ 2008 19:56

Δείκτες - γενικά

Δημοσίευση από bxenos » 21 Οκτ 2008 12:36

Καταρχήν σε C++ έχουν απλοποιηθεί οι διαδικασίες με τις "αναφορές" και δεν είναι τόσο απαραίτητοι οι pointers.

π.χ.

Κώδικας: Επιλογή όλων

//ακολουθεί κωδικας C
struct {
          double x,y;
          char description[32];
} point;

void move(point *p,double dx,double dy){
        p->x += dx;
        p->y += dy;
}

//ακολουθεί κωδικας C++
void move(point& p,double dx,double dy){
        p.x += dx;
        p.y += dy;
}
για να περάσεις π.χ. ένα ολόκληρο struct σε μια συνάρτηση που θα το αλλάξει περνάς μόνο
τον pointer (ή την αναφορά του). Αλλιώς (αν δεν υπήρχαν) θα έπρεπε να περάσεις το struct ολόκληρο και να το επιστρέψεις πίσω αλλαγμένο (με return), δηλαδή δύο φορές αντιγραφή όλων των περιεχομένων του αντί για 4 bytes (ενός pointer) (βέβαια το 4 είναι ανάλογα με την υλοποίηση).

άλλο παράδειγμα η αριθμιτική με pointers
π.χ.

Κώδικας: Επιλογή όλων

      char s[256],*p,*d;
      int diff;
      strcpy(s,"he.........llo world");
      p = strchr(s,"he");
      d = strchr(s."lo");
      diff = d - p + 2;
      printf("ανάμεσα στο he και στο lo παρεμβάλονται %d χαρακτήρες\n",diff);
αυτά για ξεκίνημα.

Άβαταρ μέλους
vassilism
Δημοσιεύσεις: 1950
Εγγραφή: 17 Μαρ 2007 14:47
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από vassilism » 21 Οκτ 2008 13:43

Φίλε bxenos ωραίο παράδειγμα.
Βέβαια ακόμα δεν έχω κατανοήσει την φιλοσοφία.
Για να καταλάβω λίγο καλύτερα η μεγαλύτερη ευελιξία που μας δίνουν είναι όταν χρησιμοποιούμε συναρτήσεις και θέλουμε να διαχειριζόμαστε καλύτερα τις τιμές των μεταβλητών τους?

Άβαταρ μέλους
dva_dev
Script Master
Δημοσιεύσεις: 3790
Εγγραφή: 16 Σεπ 2005 01:32
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από dva_dev » 22 Οκτ 2008 03:00

Πέρα από τη (απλή) χρήση των pointers που αναφέρθηκε ήδη, ένα πολύ δυνατό σημείο τους είναι ότι μπορούν να δώσουν δυναμικά χαρακτηριστικά στο πρόγραμμά μας αφού μπορούν να δείξουν σε functions (και στη C++ ακόμα περισσότερο σε classes αλλά μην το μπλέξουμε ακόμα περισσότερο αφού μπαίνει και η ένοια του interface). Εδώ έρχονται να μας βοηθήσουν οι (function) pointers. Αντί να δείχνουν δηλαδή σε κάποια δεδομένα και όταν τους προσπελάσουμε να πάρουμε την συγκεκριμένη τιμή, δείχνουν σε κάποια συνάρτηση και μπορούμε να καλέσουμε αυτή τη συνάρτηση (με ότι παραμέτρους θα την καλούσαμε και αν το κάναμε στατικά). Ενα απλό παράδειγμα είναι το εξής (το typedef χρησιμοποιείται γιατί απλοποιεί αρκετά τη σύνταξη ώστε να είναι κατανοητή):

Κώδικας: Επιλογή όλων

#include <stdio.h>

int MyAdd&#40;int a, int b&#41;
&#123;
	return &#40;a + b&#41;;
&#125;
typedef int &#40;*PFUNC&#41;&#40;int, int&#41;;

int main&#40;void&#41;
&#123;
	PFUNC pF;

	printf&#40;"%d + %d = %d\n", 4, 5, MyAdd&#40;4,5&#41;&#41;;
	pF = MyAdd; //Set pointer to function MyAdd
	printf&#40;"%d + %d = %d\n", 4, 5, pF&#40;4,5&#41;&#41;;
	return 0;
&#125;
Για να το προχωρήσουμε λίγο ακόμα, στο bubblesort ο αλγόριθμος ταξινόμησης είναι απλός και συγκεκριμένος. Το μόνο που μπορεί να αλλάζει είναι το σημείο που γίνεται η σύγκριση ώστε να βάζει τα στοιχεία με αύξουσα με φθίνουσα, ή με οποιαδήποτε άλλη σειρά φανταστούμε.
Αντί να φτιάχνουμε λοιπόν 100 διαφορετικές υλοποιήσεις για τις 100 διαφορετικές μορφές ταξινόμησης που μπορούμε να φανταστούμε (αύξουσα/φθίνουσα/πρώτα οι ζυγοί σε αύξουσα/πρώτα οι σύνθετοι σε φθίνουσα/...), θα μπορούσαμε να φτιάξουμε απλώς κάποιες functions που να κάνουν τη σύγκριση για να αποφασίσουν αν τα στοιχεία είναι στη σειρά ή όχι, και να περάσουμε παραμετρικά όποια από αυτές τις functions θέλουμε στην μία και μοναδική function που υλοποιεί το bubble sort.
Ενα απλό (ελπίζω) παράδειγμα (με αρκετή σαβούρα για να δείχνει τη διαφορά στο αποτέλεσμα που βγαίνει) και περνάει παραμετρικά το αν θα γίνει αύξουσα ή φθίνουσα ταξινόμηση φαίνεται παρακάτω.

Κώδικας: Επιλογή όλων

#include <stdio.h>

//Επιστρέφουν 1 αν είναι στην σωστή σειρά αλλιώς -1
int Ascending &#40;int a, int b&#41; &#123; return &#40;a <= b&#41; ? -1 &#58; 1; &#125;
int Descending&#40;int a, int b&#41; &#123; return &#40;a >= b&#41; ? -1 &#58; 1; &#125;

//pointer σε function που επιστρέφει int και παίρνει δύο παραμέτρους &#40;int, int&#41;
//Και η Ascending και η Descending ακολουθούν αυτό το πρότυπο.
typedef int &#40;*PSORTFUNC&#41;&#40;int, int&#41;;

void BubbleSort&#40;PSORTFUNC pFunc, int* intArray, int arraySize&#41;
&#123;
	int i;
	while &#40;arraySize > 1&#41; &#123;
		for &#40;i=0; i<arraySize-1; ++i&#41; &#123;
			//Καλούμε την συνάρτηση &#40;Ascending ή Descending&#41; μέσω του pointer
			if &#40; pFunc&#40;intArray&#91;i&#93;, intArray&#91;i+1&#93;&#41; > 0 &#41; &#123;
				int tmp;
				tmp = intArray&#91;i&#93;;
				intArray&#91;i&#93; = intArray&#91;i+1&#93;;
				intArray&#91;i+1&#93; = tmp;
			&#125;
		&#125;
		--arraySize;
	&#125;
&#125;

void PrintArray&#40;int* intArray, int arraySize&#41;
&#123;
	int i;
	for &#40;i=0; i<arraySize; ++i&#41; printf&#40;"%d ", intArray&#91;i&#93;&#41;;
	puts&#40;""&#41;;
&#125;

void SimpleTestFunctions&#40;PSORTFUNC pFunc&#41;
&#123;
	int array1&#91;&#93; = &#123;3, 2, 4, 9, 8, 5, 1&#125;;

	PrintArray&#40;array1, 7&#41;;
	BubbleSort&#40;pFunc, array1, 7&#41;;
	PrintArray&#40;array1, 7&#41;;
&#125;

int main&#40;int argc, char** argv&#41;
&#123;
	puts&#40;"Test with Sort Ascending"&#41;;
	SimpleTestFunctions&#40; Ascending &#41;;
	puts&#40;"Test with Sort Descending"&#41;;
	SimpleTestFunctions&#40; Descending &#41;;

	return 0;
&#125;
Βέβαια για να μπορούμε να επιτύχουμε αυτό το αποτέλεσμα πρέπει να έχει δηλωθεί κατάλληλα τόσο ο pointer, όσο και οι functions στις οποίες θα δείχνει ο pointer να ακολουθούν το ίδιο πρότυπο στη δήλωση τους. Πρέπει όλα να έχουν την ίδια υπογραφή, για το λόγο αυτό χρησιμοποιούμε το typedef όπου φαίνεται καθαρά ποιά είναι η υπογραφή που θα έχει ο pointer (και φροντίζουμε να είναι ίδια με αυτή των functions που θα τον χρησιμοποιήσουμε), αλλά και επιπλέον διευκολύνει την σύνταξη αφού μετά μπορούμε να χρησιμοποιήσουμε το όνομα του τύπου που έχουμε δώσει μέσα στο typedef και όχι ολόκληρο το μακρυνάρι.

Μέχρι εδώ έχουμε δεί πως μπορεί να αξιοποιηθεί η δυναμική κλήση συναρτήσεων, αλλά θα πει κάποιος ότι μπορούμε με κάποιες άλλες τεχνικές να πετύχουμε το ίδιο αποτέλεσμα χωρίς να πάμε μέσω pointers και ίσως με πιο απλό/πιο δομημένο/πιο ... τρόπο. Η δύναμη όμως που κρύβει αυτή η τεχνική είναι ότι μπορούμε από το πρόγραμμα μας να καλέσουμε άλλες βιβλιοθήκες που έχουμε φτιάξει εμείς (σπάνια περίπτωση) ή έχει το λειτουργικό σύστημα ή έχει υλοποιήσει κάποιος άλλος. Ακόμα περισσότερο δίνει τη δυνατότητα να δημιουργήσουμε σημεία επέκτασης η παραμετροποίησης της εφαρμογής μας, ώστε να πάει κάποιος τρίτος και ακολουθώντας συγκεκριμένους κανόνες να δημιουργήσει δικά του modules/libraries/plug-ins που θα μπορούν να προστεθούν πάνω στην εφαρμογή μας και να την επεκτείνουν ή να αλλάξουν τη λειτουργικότητα της χωρίς να χρειαστεί να πειράξουμε τον source κώδικα ο ένας του άλλου. Για την ακρίβεια πέρα από τους κανόνες που πρέπει να τους γνωρίζουμε και εμείς και ο άλλος, δεν χρειάζεται να έχουμε ο ένας τον source κώδικα του άλλου, και όταν βάλουμε μαζί το εκτελέσιμο της εφαρμογής και την βιβλιοθήκη αυτά τα δύο θα μπορούν να συνεργάζονται. Βάζοντας μετά την βιβλιοθήκη κάποιου άλλου η εφαρμογή θα μπορεί να επεκταθεί ακόμα περισσότερο ή να τροποποιηθεί η συμπεριφορά της. Χαρακτηριστικό παράδειγμα σχετικό με το forum είναι ο apache και η php με τα δεκάδες modules και plugins που έχουν δημιουργήσει developers άσχετοι με αυτούς που ασχολούνται με τον apache και την php.

Αυτό προσπαθεί να επιδείξει η παρακάτω εφαρμογή (που παρότι έχει μπεί ο κώδικας που χρειάζεται το linux έχει δοκιμαστεί μόνο σε windows). Αυτό που κάνει είναι να φορτώσει ένα plugin δυναμικά σε run time και να καλέσει μια function που κάνει κάποιους πολύπλοκους υπολογισμούς με τον αριθμό που θα δώσει σαν παράμετρο και κατόπιν αφού το plugin κάνει όλη την επεξεργασία η εφαρμογή τυπώνει το αποτέλεσμα. Επιπλέον ζηταει η εφαρμογή το version string του plugin, τα credits των δημιουργών του και τα τυπώνει (όχι τίποτα βασικά αλλά να υπάρχει κάτι για επίδειξη).

Εχουν δημιουργηθεί δύο βιβλιοθήκες που καλούνται δυναμικά (γιατί μόνο ένα δεν δείχνει τη διαφορά) και υλοποιούν τις εξής συναρτήσεις τις οποίες θέλει η εφαρμογή να καλέσει:
Calculate (η function με τους υπολογισμούς)
Version (η function που μας δίνει το version string)
Credits (η function που μας δίνει τα ονόματα των δημιουργών του plugin)

Η εφαρμογή...
Sample.c

Κώδικας: Επιλογή όλων

#include <stdio.h>
#include "LibraryLoader.h"

//Signature for "Calculate"
typedef int &#40;*PFUNC&#41;&#40;int&#41;;
//Signature for "Version", "Credits"
typedef const char* &#40;*PSTRFUNC&#41;&#40;void&#41;;

int main&#40;int argc, char** argv&#41;
&#123;
	void* dll;
	PFUNC pFunction;
	PSTRFUNC pStringFunction;
	const char* versionStr;
	const char* author;

	if &#40;argc != 2&#41; &#123;
		printf&#40;"Syntax&#58;\n %s <dynamic library path>\n", argv&#91;0&#93;&#41;;
		return -1;
	&#125;

       //Φορτώνει την βιβλιοθήκη που περνάμε σαν παράμετρο
	dll = LibraryLoad&#40; argv&#91;1&#93; &#41;;

       //Παίρνει τη διεύθυνση της Calculate από τη βιβλιοθήκη
	pFunction = &#40;PFUNC&#41;LibraryFunction&#40;dll, "Calculate"&#41;;
	printf&#40;"Calculate at %s returned %d\n", argv&#91;1&#93;, pFunction&#40;7&#41;&#41;;

        //Παίρνει τη διεύθυνση της Version από τη βιβλιοθήκη
	pStringFunction = &#40;PSTRFUNC&#41;LibraryFunction&#40;dll, "Version"&#41;;
        //Καλεί την Version και κρατάει τον pointer &#40;το string&#41; που επιστρέφεται
	versionStr = pStringFunction&#40;&#41;;
	
        //Παίρνει τη διεύθυνση της Credits από τη βιβλιοθήκη
	pStringFunction = &#40;PSTRFUNC&#41;LibraryFunction&#40;dll, "Credits"&#41;;
	author = pStringFunction&#40;&#41;;
	
	printf&#40;"The version number is&#58; %s\n", versionStr&#41;;
	printf&#40;"Credits go to %s\n", author&#41;;

        //Κλείνει τη βιβλιοθήκη που είχε φορτώσει δυναμικά
	LibraryUnload&#40;dll&#41;;

	return 0;
&#125;
βοηθητικός κώδικας που φορτώνει δυναμικά μια βιβλιοθήκη (σε c++ θα μπορούσε να είναι μια πολύ ωραία class, αλλά η c δεν δίνει τέτοιες πολυτέλειες)
LibraryLoader.h

Κώδικας: Επιλογή όλων

#ifndef __LIBRARY_LOADER__
#define __LIBRARY_LOADER__

#ifdef WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

void*	LibraryLoad&#40;const char* libname&#41;;
void	LibraryUnload&#40;void* handle&#41;;
void*	LibraryFunction&#40;void* handle, const char* funcName&#41;;

#endif //__LIBRARY_LOADER__
LibraryLoader.c

Κώδικας: Επιλογή όλων

#include "LibraryLoader.h"

#ifdef WIN32 /* WINDOWS CODE */

void* LibraryLoad&#40;const char* libname&#41;
&#123;
	return LoadLibrary&#40;libname&#41;;
&#125;

void LibraryUnload&#40;void* handle&#41;
&#123;
	if &#40;handle&#41;
		FreeLibrary&#40;&#40;HINSTANCE&#41;handle&#41;;
&#125;

void* LibraryFunction&#40;void* handle, const char* funcName&#41;
&#123;
	return GetProcAddress&#40;&#40;HINSTANCE&#41;handle, funcName&#41;;
&#125;

#else /* LINUX CODE - UNTESTED */

void* LibraryLoad&#40;const char* libname&#41;
&#123;
	return dlopen&#40;libname, RTLD_LAZY&#41;;
&#125;

void LibraryUnload&#40;void* handle&#41;
&#123;
	if &#40;handle&#41;
		dlclose&#40;handle&#41;;
&#125;

void* LibraryFunction&#40;void* handle, const char* funcName&#41;
&#123;
	return dlsym&#40;handle, funcName&#41;;
&#125;

#endif
το πρώτο plugin: lib
lib.c

Κώδικας: Επιλογή όλων

const char* versionString = "version 2.3.0.0";
const char* creditsString = "Many, but none I know!";

int Calculate&#40;int i&#41;
&#123;
	return i; //Do not calculate anything, just return it.
&#125;

const char* Version&#40;&#41;
&#123;
	return versionString;
&#125;

const char* Credits&#40;&#41;
&#123;
	return creditsString;
&#125;
lib.def

Κώδικας: Επιλογή όλων

LIBRARY	lib
EXPORTS
	Calculate
	Version
	Credits
το δεύτερο plugin: lib2
lib2.c

Κώδικας: Επιλογή όλων

const char* versionString = "version 1.0.0.0";
const char* creditsString = "Various Authors...";

int Calculate&#40;int i&#41;
&#123;
	return i * i; //Calculate i^2
&#125;

const char* Version&#40;&#41;
&#123;
	return versionString;
&#125;

const char* Credits&#40;&#41;
&#123;
	return creditsString;
&#125;
lib2.def

Κώδικας: Επιλογή όλων

LIBRARY	lib
EXPORTS
	Calculate
	Version
	Credits
Παραδείγματα αντίστοιχα με το παραπάνω τουλάχιστον στο linux θα έλεγα ότι αποτελούν όλες οι εφαρμογές που έχουν κάποιο configuration αρχείο και περιέχουν γραμμές που λένε ποιά modules θα φορτωθούν και ποιά όχι. Στα windows είναι μάλλον λίγο πιο πολύπλοκα τα πράγματα αφού χρησιμοποιείται αρκετά συχνά και η registry πέρα από τα configuration αρχεία(π.χ. COM objects, ActiveX) αλλά ας μην απλωθούμε ως εκεί, να δούμε πρώτα (κάποια στιγμή) τι μπορεί επιπλέον να κάνει η c++.

Δεν ξέρω κατά πόσο είναι κατανοητά (γράφω και διορθώνω αρκετή/ές ώρες τώρα προσπαθώντας να βγαίνει νόημα), ούτως ή άλλως οι pointers και οι τεχνικές χρήσης τους είναι τεράστιο θέμα που θέλει πολλά βιβλία για να αναλυθεί. Θα έλεγα ότι ουσιαστικά η C είναι οι pointers.

Μια προσπάθεια πάντως την έκανα... κι όσοι δεν κάνουν χαρακίρι τα ξαναλέμε.

Άβαταρ μέλους
bxenos
Δημοσιεύσεις: 53
Εγγραφή: 18 Αύγ 2008 19:56

Δείκτες - γενικά

Δημοσίευση από bxenos » 22 Οκτ 2008 14:24

Σωστό αυτό που αναφέρθηκε από τον dva_dev με τους pointer σε συνάρτηση (που στη C++ καλύπτετε εν μέρη με τις virtual functions), οπότε μια που προχωρά το πράγμα, να συμπληρώσω και το πιό χαμηλού επιπέδου setjmp.h το οποίο είναι μετάβαση σε οποιοδήποτε κομάτι κώδικα και όχι απλή κλήση σε συνάρτηση (π.χ. για υλοποίηση exceptions, parrallel processing, co-routines, κλειδωμάτων κτλ)

Πάντως το πως θα ερμηνεύσεις μια θέση μνήμης είναι στο χέρι του προγραμματιστή με τη χρήση των pointers, π.χ. η printf η οποία αποφασίζει τι είναι το κάθε όρισμα ανάλογα με το format string που έχει περαστεί. Αυτό δημιουργεί και μια σχέση αγάπης/μίσους με τους διάφορους προγραμματιστές (ανάλογα με τον βαθμό κατανόησης που έχουν βεβαίως-βεβαίως).

Παλιότερα (msdos εποχές) δίνοντας

Κώδικας: Επιλογή όλων

typedef void &#40;* _far vfunc&#41;&#40;void&#41;;
vfunc reset = &#40;vfunc&#41;MK_FP&#40;0xf000,0x0100&#41;;
&#40;*reset&#41;&#40;&#41;;
//βέβαια αν μετά απο τόσα χρόνια θυμάμε καλά την sergment διευθυνση του bios.
κάναμε reset στον υπολογιστή.
Και με

Κώδικας: Επιλογή όλων

unsigned char *vtxt = &#40;unsigned char *&#41;MK_FP&#40;0xc800,0&#41;;
*vtxt = 'h';vtxt +=2;
*vtxt = 'e';vtxt +=2;
*vtxt = 'l';vtxt +=2;
*vtxt = 'l';vtxt +=2;
*vtxt = 'o';vtxt +=2;
γράφαμε στην οθόνη (color mode)

Τώρα δεν υπάρχει περίπτωση να λειτουργούν τα reset και οι εγγραφές σε οθόνη με την protected memory και το paging των λειτουργικών συστημάτων....

Άβαταρ μέλους
vassilism
Δημοσιεύσεις: 1950
Εγγραφή: 17 Μαρ 2007 14:47
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από vassilism » 22 Οκτ 2008 18:30

Καταρχήν παιδιά σας ευχαριστώ πολύ που μπήκατε στον κόπο να φτιάξετε τέτοια παραδείγματα και αφιερώσατε τόσο χρόνο.
Τι να πω είστε φοβεροί.

Πάντως να ξεκαθαρίσω ότι είμαι εντελώς αρχάριος και δεν έχω ιδέα από C, και αν δεν ήξερα λίγο php δεν θα καταλάβαινα τίποτα.
Όχι ότι τώρα κατάλαβα τους δείκτες αλλά μελετάω προσεκτικά τα όσα μου γράψατε και αρχίζω να μπαίνω στο νόημα.
bxenos έγραψε: Παλιότερα (msdos εποχές)
Εγώ αυτές τις DOS εποχές δεν τις πέτυχα, γιατί είμαι μικρός :D
Αλλά αυτό και αν είναι ενδιαφέρον!

Να ρωτήσω τώρα κάτι άσχετο (με τους δείκτες) που το είχα πάντα απορία :question:

Για να λειτουργήσει η printf που κατά συνέπεια μας επιτρέπει να τυπώνουμε μηνύματα στην οθόνη χρειάζεται να καλέσομε στο πρόγραμμα το αρχείο stdio.h.
Σωστά μέχρι εδώ?

Τώρα θέλω να σας πω πως έχω φανταστεί εγώ το όλο σκηνικό και θέλω να μου πείτε αν είναι σωστό ή λάθος.

Λοιπόν καταρχήν φαντάζομαι πως το αρχείο stdio.h περιέχει μέσα κώδικα (μάλλον σε C) το οποίο περιέχει πολλές functions.
Μια από αυτές είναι και η printf.
Αν τα παραπάνω είναι σωστά τότε μου δημιουργούνται οι εξής απορίες:
1 Η printf στην ουσία είναι function ή εντολή?
2 Είναι εφικτό να δώ στο αρχείο stdio.h το κομμάτι κώδικα που αναφέρετε στην printf?

Αυτά :roll:

Άβαταρ μέλους
bxenos
Δημοσιεύσεις: 53
Εγγραφή: 18 Αύγ 2008 19:56

Δείκτες - γενικά

Δημοσίευση από bxenos » 22 Οκτ 2008 20:18

Οτιδήποτε υπάρχει στα αρχεία *.h (π.χ. string.h,stdio.h,stdlib.h...) είναι γενικά declaration (αναφορές) σε συναρτήσεις που είναι συχνά χρησιμοποιούμενες (π.χ. printf,strcpy), σταθερές, μικρές inline (ψεύδο)συναρτήσεις (π.χ. getc,putc).

Δεν υπάρχει ο κώδικας της printf μέσα στο stdio.h. Υπάρχει μόνο μια αναφορά για τον compiler να ξέρει ότι υπάρχει μια συναρτηση με όνομα printf, να μάθει τι τιμή επιστρέφει και τι ορίσματα χρειάζεται (ώστε να μας βοηθήσει να γλυτώσουμε λάθη από αμέλεια μας).
Οι συναρτήσεις αυτές υπάρχουν μέσα στις βιβλιοθήκες της C σε object μορφή.
π.χ.

Κώδικας: Επιλογή όλων

void foo&#40;int a,double b&#41;; //αναφορά συνάρτησης &#40;declaration&#41;

void foo&#40;int a,double b&#41; &#123; //κώδικας/ορισμός συνάρτησης &#40;definition&#41;
   b = a;
&#125;
Παλιά (πρίν το ansi πρότυπο) δεν ήταν απαραίτητο να κάνεις include τίποτα (ευτυχώς αυτό πέρασε).

Αν θέλεις τον κώδικα κάποιας συνάρτησης ψάξτον στο google (π.χ. printf.c) για την συνάρτηση printf. Δεν θα σε συμβούλεβα να αρχίσεις από την printf, είναι βαριά. Δες την strlen,strcpy, strchr,strstr,atoi ... και υπάρχουν και πολλά tutorial για C/C++ κατατοπιστικά για ξεκίνημα.

Καλό διάβασμα!

Άβαταρ μέλους
vassilism
Δημοσιεύσεις: 1950
Εγγραφή: 17 Μαρ 2007 14:47
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από vassilism » 22 Οκτ 2008 20:49

Πολύ κατατοπιστικό.
Ευχαριστώ πολύ :D

chief
Δημοσιεύσεις: 49
Εγγραφή: 14 Οκτ 2008 13:37
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από chief » 24 Οκτ 2008 15:08

Και από εδώ αρπάζω την ευκαιρία για να πω τους προβληματισμούς μου, βασικά είμαι στην εκπόνηση μιας εργασίας για το Παν. Μου. Είναι σε C και γενικά ζορίζομαι .. και έχω χάσει και λίγο την μπάλα αλλά αναγκαστικά ακόμα προσπαθώ.
Η εργασία είναι να δημιουργήσω ένα πρόγραμμα fcompare, το οποίο θα συγκρίνει 2 αρχεία ASCII και θα έχει 3 παραμέτρους.

/nc : Αγνόησε διάκριση κεφαλαίων-πεζών στη σύγκριση
/s : Επίστρεψε μόνο την πρώτη και την τελευταία γραμμή από ένα σύνολο συνεχόμενων διαφορετικών γραμμών
/ln : Τύπωσε και τους αριθμούς των διαφορετικών γραμμών στην έξοδο.

Για αρχή κατανοώ - και μετά από ψάξιμο - ότι χρειάζομαι τους παρακάτω headers.

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

Στην συνέχεια για τα ορίσματα θα πρέπει να τα δηλώσω στην εντολή main. Ακολούθως έχουμε:

int main(int argc, char *argv[])

Μετά δηλώνω του pointers για τα αρχεία που θα ανοίγονται.

FILE *inFile1Ptr;
FILE *inFile2Ptr;

Επανέρχομαι στην εντολή argc &#8211; argv

Περιορίζω τις επιλογές των ορισμάων μαζί και την εντολή fcompare και έχω

if(argc<3 || argc >6 ).
printf("usage:fcompare [options] file1 file2, options: /nc /s /ln" );

else if ((inFile1Ptr=fopen()argv[1],"r")!=NULL){
}
else if
((inFile2Ptr=fopen()argv[2],"r")!=NULL)){

}
else if
((inFile2Ptr=fopen()argv[3],"r")!=NULL)){

}
else if
((inFile2Ptr=fopen()argv[4],"r")!=NULL)){

}
else if
((inFile2Ptr=fopen()argv[5],"r")!=NULL)){

}

Από εδώ και πέρα το ψάχνω, τίποτα συμβουλές και ιδέες;

Άβαταρ μέλους
bxenos
Δημοσιεύσεις: 53
Εγγραφή: 18 Αύγ 2008 19:56

Δείκτες - γενικά

Δημοσίευση από bxenos » 24 Οκτ 2008 16:23

chief έγραψε: if(argc<3 || argc >6 ).
α) δηλαδή το fcompare /s /s /s /s /s /s /s /s /s xxx1 xxx2
είναι λάθος;

chief έγραψε:else if ((inFile1Ptr=fopen()argv[1],"r")!=NULL){
β) εδώ δεν έχεις syntax error με τις παρενθέσεις;
γ) τι νομίζεις ότι περιέχουν τα argv[0],argv[1],argv[2],...argv[n];
δ) τι νομίζεις ότι κάνει η συνάρτηση fopen();
ε) πόσο καιρό κάνεις C;
στ) μέχρι εδώ τα σκέφτηκες μόνος σου;

chief
Δημοσιεύσεις: 49
Εγγραφή: 14 Οκτ 2008 13:37
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από chief » 26 Οκτ 2008 13:57

Καταρχάς ευχαριστώ για την απάντηση σου.
Στο πρώτο ερώτημά σου ναι είναι λάθος οι χρήση άσχετων παραμέτρων και η χρήση πάνω από 2 φορές της ίδιας παραμέτρου. οι παραπάνω παράμετροι ή οι ανύπαρκτοι θα πρέπει να αγνοούνται από το πρόγραμμα.
Στο δεύτερο σου ερώτημα φαντάζομαι ότι αναφέρεσαι στο fopen() εν γνώσει μου το άφησα κενό &#8230;ακόμα δεν ξέρω τη θα βάλω μέσα&#8230;&#8230;
rgv[0],argv[1],argv[2],...argv[n]; Περιέχουν (συμβολίζουν) τις παραμέτρους &#8230;. εάν δεν κάνω λάθος.
Η fopen ανοίγει αρχείο
Με την C ασχολούμαι περίπου 1 μήνα, κάτι λιγότερο από μήνα.
Από παραδείγματα έχω πάρει κομμάτια κώδικα και προσπαθώ να α προσαρμόσω στην άσκησή μου, ξέρω ότι καμιά φορά δεν είναι και πολύ αποδοτικό αλλά&#8230; δεν υπάρχει και πιο σύντομος τρόπος εν μέσω πίεσης χρό χρόνου

Άβαταρ μέλους
bxenos
Δημοσιεύσεις: 53
Εγγραφή: 18 Αύγ 2008 19:56

Δείκτες - γενικά

Δημοσίευση από bxenos » 27 Οκτ 2008 03:43

Οπότε, δουλειά...

ναι, τα argv[] περιέχουν παραμέτρους, αλλά ο τρόπος που τις δίνει ο χρήστης
του προγράμματος δεν είναι καθορισμένος. Πάρε μιά ιδέα από το πιό κάτω παράδειγμα

αν δοθεί η εντολή

Κώδικας: Επιλογή όλων

fcompare /s /e xxx1 xxx2 /t/y/u
το argv[0] περιέχει το "c:\...\fcompare.exe" δηλαδή την διαδρομή για το πρόγραμμα
το argv[1] το "/s"
το argv[2] το "/e"
το argv[3] το "xxx1"
το argv[4] το "xxx2"
το argv[5] το "/t/y/u"

άρα για τα argv[] δεν μπορείς εκ των προτέρω να ξέρεις ποιά είναι παράμετροι τύπου /x και ποιά ονόματα αρχείων, άρα θέλει κάποια δουλειά στην αρχή για να βρείς ποιά ξεκινάνε με '/' και πόσες παραμέτρους έχουν (π.χ. "/t/y/u" σε ένα argv[]).

Και μετά μπορείς να ανοίξεις τα αρχεία
fopen(όνομα_αρχείου,τύπος_τρόπος)
όπου τύπος_τρόπος είναι "r" για ανάγνωση, "w" για εγραφή, ..., "b" για binary file,...
δώσε google στο fopen και θα βρείς τις πιθανές παραμέτρους (πιθανά θέλεις "r" σκέτο").

Οπότε κάνε το επόμενο βήμα και ποστάρισε το να το δούμε
good luck

chief
Δημοσιεύσεις: 49
Εγγραφή: 14 Οκτ 2008 13:37
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από chief » 27 Οκτ 2008 13:10

Λοιπόν, καταρχάς καλή μέρα.
Μετά από πειράματα και προσπάθειες για κάτι πιο πολύπλοκο αποφάσισα να πηγαίνω γραμμή γραμμή για να καταλαβαίνω και τι κάνω.

Το πρόγραμμα θα έχει τη μορφή:
Σύνταξη: fcompare [options] file1 file2

Options:

/nc : Αγνόησε διάκριση κεφαλαίων-πεζών στη σύγκριση
/s : Επίστρεψε μόνο την πρώτη και την τελευταία γραμμή από ένα σύνολο συνεχόμενων διαφορετικών γραμμών
/ln : Τύπωσε και τους αριθμούς των διαφορετικών γραμμών στην έξοδο
Για αρχή έγραψα τον κώδικα που παραθέτω, που πιστεύω για αρχή είναι σωστά.
Υπάρχει κάποια διόρθωση στο παρόν τμήμα κώδικα ή παρατήρηση;

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

int main (int argc, char *argv[]){

int count;
if (argc >0 || argc<6)
{
for (count = 1; count < argc; argc++)
}


FILE *file_1;
FILE *file_2;
char c[120];


file_1=fopen("test11.txt","r");
file_2=fopen("test12.txt","r");


fclose(file_1);
fclose(file_2);
return 0;
}

Άβαταρ μέλους
bxenos
Δημοσιεύσεις: 53
Εγγραφή: 18 Αύγ 2008 19:56

Δείκτες - γενικά

Δημοσίευση από bxenos » 27 Οκτ 2008 13:46

chief έγραψε:

Κώδικας: Επιλογή όλων

	int count;
	if &#40;argc >0 || argc<7&#41;;
***εδώ έβαλες μια εντολή που δεν κάνει τιποτα.

	&#123;
		for &#40;count=1; count<7; argc++&#41;
	&#125;
***τι κανεις εδώ;
καλύτερα χρησιμοποίησε ένα βρόγχο while για να επεξεργαστεις όλα τα arguments 
που ξεκινάνε με '/'
Τα arguments που θα περισσέψουν θα είναι τα ονόματα αρχείων

τα υπόλοιπα δεν έχουν πρόβλημα

chief
Δημοσιεύσεις: 49
Εγγραφή: 14 Οκτ 2008 13:37
Επικοινωνία:

Δείκτες - γενικά

Δημοσίευση από chief » 27 Οκτ 2008 15:50

Το πλησιάζω καθόλου γιατί εδώ και 5 ώρες έχω βγάλει τα μάτια μου....
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char *argv[]){
FILE *file_1, *file_2;

char c[120];

//file_1=fopen("test11.txt","r");
//file_2=fopen("test12.txt","r");

if(argc<0 || argc>6){
printf("Usage: fcompare [options] file1 file2, options: /nc, /s, /ln.\n")
}
else if {
if ((file_1 = fopen(argv[1],"r"))!=NULL){
if((file_2 = fopen(argv[2], "r"))!=NULL)
}
}


fclose(file_1);
fclose(file_2);
return 0;

Απάντηση

Επιστροφή στο “C, C++”

Μέλη σε σύνδεση

Μέλη σε αυτήν τη Δ. Συζήτηση: Δεν υπάρχουν εγγεγραμμένα μέλη και 0 επισκέπτες