C Codeschnipsel

Dieser Text befindet sich in der neusten Version auf http://www.eggdrop.ch/texts/cschnipsel/.

13.05.06: Version 1.6.1

Inhalt

Einleitung

Hier ist eine kleine Sammlung meiner Codeschnipsel, die in C programmiert wurden. Diese Seite wurde in der Hoffnung erstellt, dass diese Schnipsel jemandem nützen werden. Wer auch ein Codeschnipsel hat, darf es mir natürlich mailen. Ich freue mich auf die Schnipsel ;-) Übrigens sind alle diese Schnipsel "public domain", so dass du sie ohne Angabe eines Copyrights in dein Programm integrieren kannst bzw. umändern kannst.

Doppelte Abstände und Tabulatoren aus einem String entfernen

Dieser Code entfernt doppelte Abstände, Tabulatoren und auch Newline-Zeichen (\n) und ersetzt sie durch einen einzigen Abstand. Der Code demonstriert auch sehr schön den Umgang mit Zeigern (auch Zeiger auf Zeiger). Die Funktion muss mit der Adresse des Strings aufgerufen werden (also in den meisten Fällen stripspaces(&string);), weil sie den String direkt verändert.

void stripspaces(char **str)
{
	char *tmp = (char *)malloc(strlen(*str)+1);
	char *p = tmp;
	int i=0, first = 1;
	
	strcpy(tmp, *str);

	while(1)
	{
		while (*p && (*p == ' ' || *p == '\n' || *p == '\t'))
			p++;
	
		if (!first)
			*(*str+(i++)) = ' ';
		else
			first = 0;
	
		if (!*p) break;
	
		while (*p && !(*p == ' ' || *p == '\n' || *p == '\t'))
			*(*str+(i++)) = *(p++);

		if (!*p) break;
	}
	
	if (i && *(*str+i-1) == ' ')
		i--;
	
	*(*str+i) = '\0';
	
	free(tmp);
}

Dynamische Stringeingabefunktion

Wolltest du nicht auch schon immer einen (fast) unendlich langen String einlesen können? Das geht mit dieser Funktion:

#define GETSTR_STEP 128

char *getstr(void)
{
    unsigned char *dst = NULL;
    int c;
    int len = 0;
    while((c = getchar()) != '\n')
    {
        if (!(len%GETSTR_STEP) &&
                !(dst=(unsigned char *)realloc(dst, (len/GETSTR_STEP+1)*GETSTR_STEP+1)))
            return NULL;

        dst[len++]=c;
    }
    dst[len]='\0';
    return dst;
}

Den GETSTR_STEP-Wert kann man bei riesigen Eingaben erhöhen. Hier gibt es noch eine ältere Version der Funktion, die bei jedem Zeichen realloc() und sprintf() aufruft.

Die Verwendung von getstr() ist ganz einfach. Die Funktion gibt die Adresse des String bei Erfolg zurück und 0, wenn malloc() oder realloc() fehlschlägt. Der Speicher des Strings muss nach der Verwendung wieder mit free() freigegeben werden. Das Proramm benötigt die Header-Dateien stdio.h und stdlib.h. Hier ein Beispiel zum Einlesen eines Strings:

char *str;
printf("Text eingeben: ");
fflush(stdout);

/* einlesen */
if ((str = getstr()) == NULL)
{
        printf("Fehler: Kein Speicher frei!\n"); exit(1);
}

/* verarbeiten */
printf("Du hast eingegeben: %s\n", str);

/* Speicher wieder freigeben */
free(str);

Hostnamen nicht-blockierend in IPs auflösen

Heutzutage sind viele Programme Netzwerkanwendungen. Fast alle von denen müssen einen Hostnamen auflösen können. Das geschieht mit der Funktion gethostbyname(). Das Problem dieser Funktion ist, dass sie blockierend ist, d.h. man kann nichts anderes mit dem Programm machen, während es den Hostnamen auflöst. Das ist beispielsweise bei grafischen GTK-Anwendungen sehr unschön, weil der Benutzer dann denkt sie wären abgestürzt, aber auch andere wichtige Anwendungen können so etwas brauchen. Die Lösung ist, man startet einen neuen Prozess bzw. Thread, welcher die IP auflöst und diese dann dem Hauptprogramm mitteilt.

Das folgende Programm läuft nur unter UNIX-Systemen. Ich werde später einmal vielleicht noch eine Windows-Version ziegen. Das Programm verwendet die pthreads-Library, was dem Compiler mit dem Parameter -lpthread mitgeteilt werden muss. Ich habe hier noch eine ältere Version, welche anstelle von pthreads die Funktion fork() verwendet, was aber relativ unschön ist. Dies ist der Code (wer es als einzelne Datei haben will, soll hier klicken):

#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

void *dnslookup(void *h);
void progress();

int state;
struct in_addr ip;

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

	if (argc != 2)
	{
		printf("Usage: %s <host>\n", argv[0]);
		exit(1);
	}

	state = 0;
	
	if (pthread_create(&t, NULL, dnslookup, (void *)argv[1]) != 0)
	{
		printf("Cannot create thread!\n");
		exit(1);
	}

	printf("Looking up %s... ", argv[1]);
	fflush(stdout);

	while (state == 0)
	{
		progress();
		usleep(50000);
	}

	if (state == -1)
		printf("\bERR: Unable to resolve.\n");
	else
		printf("\bOK: %s\n", inet_ntoa(ip));
	
	return 0;
}


void *dnslookup(void *h)
{
	struct hostent *addr;
	struct sockaddr_in in;

	addr = gethostbyname((char *)h);

	if (!addr)
	{
		state = -1;
		pthread_exit((void *) 0);
	}

	in.sin_addr = *(struct in_addr*)addr->h_addr;
	in.sin_family = AF_INET;
	ip = in.sin_addr;
	state = 1;
	pthread_exit((void *) 0);

	/* never reached */
	return (void *)NULL;
}


void progress()
{
	static unsigned char cur='/';
	switch(cur)
	{
		case '-':
			cur = '\\';
			break;
		case '\\':
			cur = '|';
			break;
		case '|':
			cur = '/';
			break;
		case '/':
			cur = '-';
			break;
	}
	printf("\b%c", cur);
	fflush(stdout);
}

Sortieren von Strings

Dieses Schnipsel zeigt, wie einfach es ist, Strings zu sortieren. Es benutzt die im C-Standard enthaltene Funktion qsort(), welche relativ einfach erlaubt, Strings zu sortieren. Die sortierenden Strings werden dabei als Argumente dem Programm übergeben.

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

int cmp(const void *p1, const void *p2)
{
        char **s1 = (char **)p1, **s2 = (char **)p2;
        return strcmp(*s1, *s2);
}

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

        qsort(&argv[1], argc-1, sizeof *argv, cmp);
        
        for (i=1;i<argc;i++)
        {
                printf("%s\n", argv[i]);
        }
}

Daten wie in einem Hexeditor darstellen

Für Debugging-Zwecke ist es oft hilfreich, wenn man sich Daten wie in einem Hexeditor anzeigen kann: Links der Hexcode der Bytes und rechts das entsprechende ASCII-Zeichen. Dafür hab ich folgenden Code geschrieben:

#define HEXVIEW_COLUMNS 80
#define HEXVIEW_CHARS ((HEXVIEW_COLUMNS-1)/4)

void hexview(char *buf, int size)
{
  int i;
  unsigned char c;

  while (size>0)
  {
    for (i=0;i<HEXVIEW_CHARS;i++)
    {
      if (size-i>0)
        printf("%02x ", (unsigned char)*(buf+i));
      else
        printf("   ");
    }
    printf(" ");
    for (i=0;i<HEXVIEW_CHARS&&size-i>0;i++)
    {
      c = (unsigned char)*(buf+i);
      printf("%c", c>=32&&c<127 ? c : '.');
    }
    buf+=HEXVIEW_CHARS;
    size-=HEXVIEW_CHARS;
    printf("\n");
  }
}

int main(int argc, char *argv[])
{
  char buf[256];
  strcpy(buf, "some data");
  hexview(buf, sizeof(buf));
}

HEXVIEW_COLUMNS ist auf die Breite des Terminals einzustellen, normalerweise ist 80 okay. Hier noch ein Beispielausschnitt:

73 6f 6d 65 20 64 61 74 61 00 00 00 f4 e1 ff bf 34 20 53  some data.......4 S
4d 50 20 53 00 6c 31 03 40 30 e2 ff bf 4c 7a 00 40 98 90  MP S.l1.@0...Lz.@..
03 40 45 82 04 08 53 54 20 32 30 30 34 00 0c 82 04 08 20  .@E...ST 2004..... 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4e 91 03 40  ...............N..@
fc 3e 03 40 2c b4 02 40 f0 7b 01 40 03 00 00 00 90 7e 01  .>.@,..@.{.@.....~.
40 c0 7e 01 40 00 6c 01 40 d4 6f 01 40 33 82 04 08 c8 e2  @.~.@.l.@.o.@3.....
ff bf cf 83 00 40 33 82 04 08 8e ff 77 01 88 81 04 08 84  .....@3.....w......
e2 ff bf 88 6f 01 40 01 00 00 00 c0 7e 01 40 00 00 00 00  ....o.@.....~.@....
01 00 00 00 84 e2 ff bf 00 00 00 00 a8 8c 15 40 71 03 00  ...............@q..
00 00 00 00 00 8e ff 77 01 00 e3 ff bf 38 6e 01 40 54 e3  .......w.....8n.@T.
ff bf b4 e2 ff bf 6c 31 03 40 f0 7b 01 40 70 9f 15 40 00  ......l1.@.{.@p..@.
00 00 00 0c 97 04 08 a8 e2 ff bf b5 82 04 08 50 8c 15 40  ...............P..@
b4 e2 ff bf c8 e2 ff bf 0b 85 04 08 5c e3 ff bf 60 8c 15  ............\...`..
40 00 00 00 00 60 8c 15 40                                @....`..@

String anhand eines Trennzeichens in ein Array konvertieren

Die folgende Funktion konvertiert den String anhand eines Trennzeichns in ein Array. Zu beachten ist noch, dass der string anschliessend nicht mehr brauchbar ist. Alle Elemente des zurückgegebenen Arrays müssen nach Benutzung mit free() freigegeben werden, das Array selber auch.

char **stringtoarray(char *string, char delimiter, int *size)
{ 
  char **array = NULL;
  char *ptr, *oldptr;
  int flag = 1;
  int count;
    
  *size = 0;
  ptr = string;

  for(count=0 ; flag ; count++) 
  {
    for (oldptr=ptr;*ptr&&*ptr!=delimiter;*ptr++)
      ;
    if (!*ptr) flag = 0;
    *ptr++ = '\0';
    (*size)++;                                                       

    array = realloc(array, (count+1)*sizeof(char *));
    array[count] = strdup(oldptr);
  }
  return array;
}

Hier ein Beispielprogramm:

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

int main(int argc, char *argv[])
{
  int num;
  int i;
  char **array;

  if (argc != 2) { return 1; }

  array = stringtoarray(argv[1], '.', &num);

  for (i=0 ; i< num ; i++)
  {
    printf("%d. %s\n", i+1, array[i]);
    free(array[i]);
  }

  free(array);

  return 0;
}

Beispielausgabe des Programms:

% ./stringtoarray Lorem.ipsum..dolor.sit.amet.
1. Lorem
2. ipsum
3. 
4. dolor
5. sit
6. amet
7. 

Zurück zu www.eggdrop.ch | Back to www.eggdrop.ch

Letzte Änderung: 13.05.06

Creative Commons License

Dieses Werk gehört zu http://www.eggdrop.ch/ und ist unter einer Creative Commons Lizenz lizensiert.