C - Fragen

C

Crymes

Guest
Hallo,
Ich beschäftige mich gerade mit der Programmiersprache C , deshalb probier ich einen Vector und eine Liste in C zu schreiben (was praktisches um die dynamische Speicherverwaltung zu üben :ugly: ) und habe ein paar Fragen.
Am Besen erstmal mein Code:

Vector.h:

Code:
/*
 * vector.h
 *
 *  Created on: 23.06.2014
 */

#ifndef VECTOR_H_
#define VECTOR_H_

#include <stdlib.h>

//Struktur des Vektors
struct Vector
{
	int numelements;
	int capacity;
	size_t element_size;
	void *data;
};

//Funktionen
struct Vector* Vector_new(const size_t, unsigned int);
void Vector_delete(struct Vector*);
size_t Vector_getSize(struct Vector*);
int Vector_getNumElements(struct Vector*);
int Vector_getCapacity(struct Vector*);
int Vector_resize(struct Vector*, const unsigned int);
int Vector_appendElement(struct Vector*, void*);
void *Vector_getElement(struct Vector*, const unsigned int);

#endif /* VECTOR_H_ */

Vector.c:

Code:
/*
 * vector.c
 *
 *  Created on: 23.06.2014
 */

#include "vector.h"

struct Vector* Vector_new(const size_t element_size, unsigned int capacity)
{
	//Vectorstruktur erstellen
	struct Vector *V = NULL;
	V = (struct Vector*) malloc(sizeof(struct Vector));
	if(V == NULL)
	{
		return(V);
	}
	V->element_size = element_size;
	V->numelements = 0;

	//Kapazitaet initialisieren
	if(capacity == 0)
	{
		V->data = NULL;
		V->capacity = 0;
	}
	else
	{
		V->data = calloc(capacity, element_size);
		V->capacity = capacity;

		//Fehlerbehandlung wenn Kapazität nicht zur Verfügung steht
		if(V->data == NULL)
		{
			//Vektor ohne Kapazitaet erstellen
			V->capacity = 0;
		}
	}
	return(V);
}
void Vector_delete(struct Vector *V)
{
	free(V->data);
	free(V);
}
int Vector_appendElement(struct Vector *V, void *element)
{
	//ueberprüfen ob das Element die gleiche größe wie element_size hat
	if(sizeof(element) != V->element_size)
	{
		return(0);
	}

	//Vector groß genug für neues Element?
	if(V->capacity <= V->numelements)
	{
		//Vektor um 1 vergroessern
		int error = Vector_resize(V, V->numelements+1);
		if(error == 0)
		{
			return(0);
		}
		V->capacity++;
	}

	//Neues Element am Ende des Datenblocks anhängen
	memcpy(V->data + V->numelements*V->element_size, element, V->element_size);
	V->numelements++;
	return(1);
}
void *Vector_getElement(struct Vector *V, const unsigned int position)
{
	if(position > V->numelements-1)
	{
		return(NULL);
	}
	return(V->data + position*V->element_size);
}
int Vector_resize(struct Vector *V, const unsigned int capacity)
{
	if(capacity == 0)
	{
		V->capacity = 0;
		V->numelements = 0;
		free(V->data);
		return(1);
	}

	//Pointer des aktuellen Datenblocks sichern, falls realloc() scheitert
	void *p = NULL;
	p = V->data;
	V->data = realloc(V->data, V->element_size*capacity);
	if(V->data == NULL)
	{
		V->data = p;
		return(0);
	}
	V->capacity = capacity;
	if(capacity < V->numelements)
	{
		V->numelements = capacity;
	}
	return(1);
}
size_t Vector_getSize(struct Vector *V)
{
	return(sizeof(V->data));
}
int Vector_getNumElements(struct Vector *V)
{
	return(V->numelements);
}
int Vector_getCapacity(struct Vector *V)
{
	return(V->capacity);
}

Und das Testprogramm:

Code:
/*
 ============================================================================
 Name        : test.c
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

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

int main(void)
{
	//Vektor erstellen
	struct Vector *V = Vector_new(sizeof(struct Vector), 50);
	int a,b;
	Vector_appendElement(V, &a);
	Vector_appendElement(V, &b);
	printf("%d", Vector_getNumElements(V));
	printf("%d", Vector_getCapacity(V));
	printf("%d", (int) Vector_getSize(V));
	return(0);
}

1. Was würdet ihr bis jetzt zum grundsätzlichen Aufbau sagen?
2. Es wird kein Element angefügt, wahrscheinlich stimmt in der Vector_appendElement Funktion am Anfang die Größenabfrage des void-Pointers nicht. Wie regelt man das sonst, dass der Vektor nur einen Datentyp enthält?

3. Wenn ich in eienr dynamisch erstellten Struktur auf den Wert hinter einer Pointervariable zugreifen will, schreibe ich das dann folgendermaßen?

Code:
struct a
	{
		int x;
		int *y;
	};
	
	struct a *s = (struct a*) malloc(sizeof(struct a));
	int i = 5;
	s->y = &i;
	*s->y++;	// ist das selbe wie i++, also 6 ?

Es geht um die letzte auskommentierte Zeile.
 
Zuletzt bearbeitet:
Habs mir jetzt nicht ganz durchgelesen aber meiner meinung nach hast du mit punkt 2 recht, es ist nicht möglich mit sizeof die grösse des elementes festzustellen. Sizeof sollte die grösse eines pointers, also 4 byte zurückgeben. Zum dritten punkt: da weis ich jetzt nicht genau wie die priorität ist. Ich würde klammern: *(s->y)++;
 
Ohne mir jetzt die Vector.c komplett durchgelesen zu haben: Ich denke, einiges ist da Geschmackssache. Man kann es sicherlich so machen, C ist nicht wirklich der Meister der Objektorientierung und so läuft es eigentlich immer darauf hinaus, das ganze mit Funktionen mit this-Pointern zu simulieren, so wie du es schon tust - ein Grund, warum ich der Programmiersprache möglichst aus dem Weg gehe und bestenfalls kleine, systemnahe Programme damit schreibe (Posix-API und so).

Ich habe auch wenig Ahnung von den Naming-Conventions, die in C als ungeschriebenes Gesetz irgendwie so gelten. Aber solange man sich da selbst einigermaßen treu bleibt, geht der Stil denke ich in Ordnung.

Paar Anmerkungen:
Code:
struct Vector
{
  [...]
};
Aus sowas mache ich eigentlich immer
Code:
typedef struct {
  [...]
} Vector;
Warum? Weil man sich so das ständige "struct" bei Benutzung des Typen spart.
Wie gesagt, ist Geschmackssache und irgendjemand wird sicher daherkommen und sagen, das wäre unsauber.

Code:
struct a *s = (struct a*) malloc(sizeof(struct a));
Der explizite Typecast ist da nicht nötig, anders als C++ castet C void-pointer implizit.

3. Wenn ich in eienr dynamisch erstellten Struktur auf den Wert hinter einer Pointervariable zugreifen will, schreibe ich das dann folgendermaßen?
Code:
*s->y++;
Edit: Nein, funktioniert so nicht, da muss in der Tat geklammert werden. GCC spuckt mit -Wall da übrigens auch eine Warnung aus. Richtig wäre:
Code:
(*s->y)++;


Edit:
Icephoen1x schrieb:
Zum dritten punkt: da weis ich jetzt nicht genau wie die priorität ist. Ich würde klammern: *(s->y)++;
Und genau so passt es nicht, weil du jetzt afaik den Pointer in s->y inkrementierst und dann dessen alten Wert dereferenzierst ;)
Bin ich aber auch erst drauf reingefallen, musste erstmal ausprobiert werden.

grösse eines pointers, also 4 byte
In der heutigen Zeit wahrscheinlich eher 8 als 4. Aber ansonsten stimmt es natürlich, sizeof kann die Größe eines Objekts nicht bestimmen, weil es ein Operator ist, der bereits beim Compilieren evaluiert wird.
 
Zuletzt bearbeitet:
OK danke, jetzt weiß ich auch warum mir bei der größe des Elements immer eine 8 angezeigt wird.
Werd mal im Internet googeln wie da die Vectoren gelöst sind.

Ich find C halt eine schöne Sprache weil man da alles versteht bzw. es nur einfache Sprachelemente gibt.
In Java hat man z.B. Interfaces und so Zeugs wo man sich dann ein Callback bastelt, wenn man sich dann fragt warum das funktioniert stößt man auf eine Java- definition, aber nicht auf eine "Anfangsadresse einer Funktion" wie in C.
 
In Java hat man z.B. Interfaces und so Zeugs wo man sich dann ein Callback bastelt, wenn man sich dann fragt warum das funktioniert stößt man auf eine Java- definition, aber nicht auf eine "Anfangsadresse einer Funktion" wie in C.
Joa, stimmt schon, mit so einer Low Level-Sprache lernt man viel eher, wie sowas kurz vor der Hardware umgesetzt ist - für produktives Entwickeln hält sich meine Begeisterung aber in Grenzen, da ist für mich C++ immer noch das Mittel der Wahl. Man hat Zugriff auf den ganzen Low Level-Blödsinn, wenn man ihn mal braucht, aber man hat die ganzen Konzepte wie virtuelle Funktionen eben schon fertig umgesetzt, und man hat Templates.

Wenn man so richtig auf Low Level steht... lerne man Assembler und optimiere seine Funktionen auf Performance. :ugly:
 
Also bindet der postinkrement also sogar stärker als der dereferenzierungsoperator, ich dachte das problem läge bei dem ->.
Ich programmier meistens auf microcontrollern deshalb die 4 byte.
 
Ich habe jetzt mal den ganzen Vektor umgestellt, sodass er als Datenelement einen Doppelpointer besitzt (void **data). Dadurch muss man sich nicht mehr drum kümmern, ob die Daten im Vektor alle die gleiche Größe haben. Funktionieren tut das ganze allerdings immer noch nicht.
Hier der aktuelle Code:

Vector.h
Code:
/*
 * vector.h
 *
 *  Created on: 23.06.2014
 */

#ifndef VECTOR_H_
#define VECTOR_H_

#include <stdlib.h>

//Struktur des Vektors
struct Vector
{
	int numelements;
	int capacity;
	size_t size;
	void **data;
};

//Funktionen
struct Vector* Vector_new(unsigned int);
void Vector_delete(struct Vector*);
size_t Vector_getSize(struct Vector*);
int Vector_getNumElements(struct Vector*);
int Vector_getCapacity(struct Vector*);
int Vector_resize(struct Vector*, const unsigned int);
int Vector_append(struct Vector*, void*, size_t);
void *Vector_get(struct Vector*, const unsigned int);

#endif /* VECTOR_H_ */

Vector.c
Code:
/*
 * vector.c
 *
 *  Created on: 23.06.2014
 */

#include "vector.h"

struct Vector* Vector_new(unsigned int capacity)
{
	//Vectorstruktur erstellen
	struct Vector *V = NULL;
	V = (struct Vector*) malloc(sizeof(struct Vector));
	if(V == NULL)
	{
		return(V);
	}
	V->numelements = 0;

	//Kapazitaet initialisieren
	if(capacity == 0)
	{
		V->data = NULL;
		V->capacity = 0;
	}
	else
	{
		V->data = calloc(capacity, sizeof(void*));
		V->capacity = capacity;

		//Fehlerbehandlung wenn Kapazität nicht zur Verfügung steht
		if(V->data == NULL)
		{
			//Vektor ohne Kapazitaet erstellen
			V->capacity = 0;
		}
	}
	return(V);
}
void Vector_delete(struct Vector *V)
{
	for(unsigned int i=0; i<V->numelements; i++)
	{
		free(V->data[i]);	//= V->data+sizeof(void*)*i
	}
	free(V->data);
	free(V);
}
int Vector_append(struct Vector *V, void *element, size_t size)
{
	//Vector groß genug für neues Element?
	if(V->capacity == V->numelements)
	{
		//Vektor um 1 vergroessern
		int error = Vector_resize(V, V->numelements+1);
		if(error == 0)
		{
			return(0);
		}
		V->capacity++;
	}

	//Neues Element am Ende des Datenblocks anhängen
	//Erst Speicherpöatz für das neue Element reservieren
	V->data[V->numelements + 1] = malloc(size);
	if(V->data[V->numelements + 1] == NULL)
	{
		return(0);
	}
	memcpy(V->data[V->numelements + 1], element, size);
	V->numelements++;
	return(1);
}
void *Vector_get(struct Vector *V, const unsigned int position)
{
	if(position > V->numelements-1)
	{
		return(NULL);
	}
	return(V->data[position]);
}
int Vector_resize(struct Vector *V, const unsigned int capacity)
{
	if(capacity > V->numelements)
	{
		//Pointer des aktuellen Datenblocks sichern, falls realloc() scheitert
		void *p = NULL;
		p = V->data;
		V->data = realloc(V->data, sizeof(void*)*capacity);
		if(V->data == NULL)
		{
			V->data = p;
			return(0);
		}
		V->capacity = capacity;
		return(1);
	}
	if(capacity < V->numelements)
	{
		for(unsigned int i=V->numelements-1; i>=capacity; i--)
		{
			free(V->data[i]);	//= V->data+sizeof(void*)*i
		}
		realloc(V->data, sizeof(void*)*capacity);
		V->capacity = capacity;
		V->numelements = capacity;
		return(1);
	}
	return(1);
}
size_t Vector_getSize(struct Vector *V)
{
	return(sizeof(V->data));
}
int Vector_getNumElements(struct Vector *V)
{
	return(V->numelements);
}
int Vector_getCapacity(struct Vector *V)
{
	return(V->capacity);
}

test.c
Code:
/*
 ============================================================================
 Name        : test.c
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

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

int main(void)
{
	//Vektor erstellen
	struct Vector *V = Vector_new(50);
	int a = 7;
	int b = 8;
	Vector_append(V, &a, sizeof(a));
	Vector_append(V, &b, sizeof(b));
	printf("%d", (int) Vector_get(V, 0));
	printf("%d", (int) Vector_get(V, 1));
	printf("%d", Vector_getNumElements(V));
	return(0);
}

1. Mir wird in der Funktion Vector_append bei der memcpy-Anweisung folgendes angezeigt: incompatible implicit declaration of built-in function ‘memcpy’
Ich glaube das liegt daran, dass das erste Argumentgebilde kein Pointer darstellt, aber wie schreibe ich das dann ?

2. Die Vector_get Funktion scheint soweit zu stimmen, oder muss ich da auch noch irgendwo ein Sternchen hinstreuen :( ?
 
Wegen den Headern:

- Ich schreibe alle Includes die ich brauche in die .c Datei, oder ?
- Wenn meine Funktion jetzt ein size_t Argument bekommt, schreibe ich die stddef in die .h Datei wegen dem Prototyp oder ist das auch ohne "Standardkonform" ?
 
- Ich schreibe alle Includes die ich brauche in die .c Datei, oder?
Das kommt drauf an, wo man die Inhalte der jeweiligen Includes braucht - falls du einen Typen oder ein Macro aus einer anderen Datei in einer öffentlichen Funktion benutzt, solltest du logischerweise schon im Header includen. Damit klärt sich dann auch die zweite Frage von allein.
 
So, ich habe mir jetzt einen Vector und eine Liste geschrieben, die toArray Funktion der Liste will aber einfach nicht funktionieren :hail:

Folgender Code funktioniert, es werden die Zahlen 0-9 ausgegeben.
Code:
int main(void)
{
	int *a = malloc(10 * 4);
	for(unsigned int i=0; i<10; i++)
	{
		a[i] = i;
	}
	for(int u=0; u<10; u++)
	{
		printf("%i", *(a++));
	}
	return(0);
}

Folgender Code funktioniert auch noch, es werden ebenfalls die Zahlen 0-9 ausgegeben:
Code:
void *List_toArray(struct List *L)
{
	int *a = malloc(10 * 4);
	for(unsigned int i=0; i<10; i++)
	{
		a[i] = i;
	}
	for(int u=0; u<10; u++)
	{
		printf("%i", *(a++));
	}
	return(a);
}
in der main-funktion wird einfach nur toArray aufgerufen.

Wenn ich aber in der main folgendes hab:
Code:
int main(void)
{
	struct List *L = List_newList(sizeof(int));
	for(int i=0; i<10; i++)
	{
		List_append(L, &i);
	}
	printf("%d", L->numelements);
	puts("");
	printf("%d", L->sizeofelement);
	puts("");

	int *array = List_toArray(L);
	for(int u=0; u<10; u++)
	{
		printf("%i", *(array++));
	}
	puts("fertig");
        return(0);
werden mir erst die Zahlen 0-9 ausgegeben (von der Funktion toArray) und dann irgendwelche komischen Zahlenkombination was nach Speicherpointern aussieht.
Nehme ich aber anstatt die toArray Funktion von meienm Vector, funktioniert alles:
Code:
void *Vector_toArray(struct Vector *V)
{
	void *a = malloc(V->numelements * V->sizeofelement);
	if(a == NULL)
	{
		return(NULL);
	}
	memcpy(a, V->data, V->numelements * V->sizeofelement);
	return(a);
}

Könnt ihr euch das erklären?
 
Du veränderst doch "a" in der toArray funktion.
Code:
printf("%i", *(a++));
Und dann gibst du den Pointer zurück, den du schon einmal durchlaufen bist. Ersetz die Zeile mal durch
Code:
printf("%i", a[u]);

Grüße
 
Du solltest den mit malloc() allozierten Speicher auch mal wieder frei geben, sonst gibt es Speicherlücken. Dafür sind Konstrukte der Art *(a++) übrigens ganz schlecht geeignet, da du den von malloc zurückgegebenen Pointer modifzierst. Wenn du die Änderungen nicht 100% rückgängig machst, so dass a wieder auf die von malloc gelieferte Startadresse zeigt, wird das beim free() ein böses Ende nehmen. Deswegen entweder ganz auf solche Geschichten verzichten (a oder *(a + u) verwenden), oder sich einen zweiten Pointer setzen, den du dann modifizieren kannst (free aber mit a).
 
ok danke, jetzt funktionierts.
Habe mir jetzt noch eine FiFo Schlange geschrieben die - wenn die Kapazität nicht verändert werden muss - ohne malloc und free auskommt.
Wie würdet ihr eigentlich die pop() Funktion implementieren (Also das erste Element zurückgeben und gleichzeitig löschen) ?
Ich habe mir 2 Möglichkeiten überlegt:

- Die Daten bleiben so lange im Element bis es überschrieben wird (also mindestens bis zum nächsten push() Aufruf), so habe ich es aktuell gemacht
- Die Daten werden in einen bereits angelegten Speicher verschoben, bleiben also bis zum nächsten pop() Aufruf erhalten

Gibt es vll. noch eine bessere Möglichkeit?
 
Wie würdet ihr eigentlich die pop() Funktion implementieren (Also das erste Element zurückgeben und gleichzeitig löschen)?
Wenn ich das richtig sehe, liefert deine "pop"-Methode einen Pointer auf den Speicher, den die Queue selbst verwaltet. Also kann sich der Inhalt des gepoppten Elements Wert verändern, während du es benutzt - das ist nicht gut. Das kann man zwar erlauben, wenn der Caller wirklich weiß, was er tut, aber als Standardverhalten würde ich tatsächlich die Kopie vorziehen. Und dann auch so, dass man es folgendermaßen benutzen kann:

Code:
struct Queue* queue = Queue_newQueue(sizeof(SomeType), ..., ...);
[...]
struct SomeType element;

if (Queue_pop(queue, &element)) {
  // 'element' enthält jetzt das Element aus der Queue
  ...
}

Die Queue_pop-Funktion liefert dann wahlweise den Pointer auf das gepoppte Element (oder NULL im Falle, dass die Queue leer ist) oder einen Boolean/Integer zurück. Der Pointer wäre dann logischerweise derselbe, den man auch an die Funktion übergibt. Hätte den Vorteil, dass man den Speicher für die Kopie nicht dynamisch allokieren müsste.


Das wäre so die C-Entsprechung von dem, was ich in C++ machen würde:
Code:
template<typename ElementType>
class Queue {
  ...
  bool fetch(ElementType& dst) {
    ...
  }
}
 
Ich habe das mal direkt noch als popIn implementiert, auf die Idee mit dem Speicher als Parameter bin ich gar nicht gekommen :daumen:

Jetzt habe ich noch 2 Probleme die für mich nicht weiter zu lösen sind :what:

- Wenn die Queue mehr als 8 Elemente beinhaltet schlägt das free(Temp->data); in Queue_deleteQueue fehl, ohne dieses free geht es
- In der Funktion Queue_toArray() stürzt das Programm immer bei der memcpy() Anweisung ab, bei der Liste und dem Vector geht alles ohne Probleme

Vll. seht ihr auf die schnelle woran das alls liegt, ich gebe jetzt mal spontan die Schuld an MinGW :ugly:

Edit: Ich habe den Fehler jetzt gefunden! Ich hatte die Daten in Q->lastelement anstatt in Q->lastelement->data kopiert, da data in der Struktur zuerst kam hat das auch funktioniert. Ich hätte das daran merken müssen, dass printf("%i", z); anstatt printf("%i", *z); funktioniert.
Jetzt sollte aber alles klappen.
 
Zuletzt bearbeitet:
So, mein nächstes kleines Projekt ist die Implementierung von AES.
Ich bin gerade biem testen und habe ein Problem dabei die Zeichenketten vom NIST zu verarbeiten. :ugly:

Der Testkey ist: 000102030405060708090a0b0c0d0e0f
Die Testdaten sind: 00112233445566778899aabbccddeeff

Mein Programmcode sieht gerade so aus:
Code:
int main(void)
{
	// the expanded key
	int expandedKeySize = 176;
	unsigned char expandedKey[expandedKeySize];

	// the cipher key
	unsigned char key[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};

	//the data to encrypt
	unsigned char data[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};

	//expandKey(expandedKey, expandedKeySize, key, size);
	//Aes_generateKeys(&key, 256, &expandedKey);

	puts("Expanded Key:");
	for (int i = 0; i < expandedKeySize; i++)
	{
	    printf("%2.2x%c", expandedKey[i], (i % 16) ? ' ' : puts(""));
	}

	//Daten verswchlüsseln
	//Aes_encryptBlockK(expandedKey, 128, data, 16);

	//Daten anzeigen
	for (int i = 0; i < 16; i++)
	{
		printf("%2.2x%c", data);
	}

	puts("!!!Hello World!!!"); /* prints !!!Hello World!!! */
	return EXIT_SUCCESS;
}

Stimmt das so wie ich die char-Arrays initialisiert habe?
Wie muss die Printf-Anweisung lauten, damit die Zeichenkette wieder wie bei der Eingabe angezeigt wird?
 
So, ich habe das Problem jetzt gelöst und die Aes Ver- und Entschlüsselung funktioniert auch.
Mein Problem ist, dass ich AES auf alle beliebigen Eingangsdaten und deren Länge anwenden will, also kann ich Padding vergessen (Cipher Block Stealing funktioniert auch erst ab 16 Byte).
Also muss ich mir einen IV venerieren.
Ich will aber nur den Key und den Ciphertext weitergeben.
Was meint ihr zu folgender Methode für die IV Generation:
Der Input Text und der Key werden jeweils mit SHA2 gehasht, die 2 Hashes werden mit xor zu meinem IV verrechnet.
Ist diese Metbode sicher ?
 
Zurück