[C Anfänger] Pointer

Hey,

ich wollte eine Funktion schreiben, die zwei Zeichenketten miteinander tauscht. Dabei dachte ich mir: Tausche doch einfach die Pointer. Mein Code:

Code:
#include <stdio.h>

void tausche(char *text1, char *text2)
{	
	printf("%u %u\n", *text1,*text2);
	int temp = *text1;
	*text1 = *text2;
	*text2 = temp;
	printf("%u %u\n", *text1,*text2);
}

int main()
{
	char text1[] = "Hallo1", text2[] = "Hallo2";
	
	// printf("text1: %s\ntext2: %s\n", text1, text2);
	tausche(text1, text2);
	// printf("text1: %s\ntext2: %s\n", text1, text2);
	return 0;
}

sollte theoretisch funktionieren, tut er auch, aber es gilt von vornherein "*text1 = 72 = *text2" was ich nicht ganz nachvollziehen kann. Offenbar klappt das wechseln der Pointer nicht?
 
Zuletzt bearbeitet:
sollte theoretisch funktionieren, tut er auch, aber es gilt von vornherein "*text1 = 72 = *text2" was ich nicht ganz nachvollziehen kann.
ändere mal
Code:
char text1[] = "Hallo1", text2[] = "Hallo2";
zu 
char text1[] = "Xallo1", text2[] = "Hallo2";
und lass es noch mal laufen ;)
Offenbar klappt das wechseln der Pointer nicht?
Du vertauscht in Deinem Code nicht die Pointer, sondern schon Werte im Speicher ("Hello"), auf die diese Pointer zeigen.

Code:
#include <stdio.h>

void tausche(char **text1, char **text2) /*nimmt einen Pointer auf Pointer entgegen, 
                                                            d.h text1,text2 sind lokale Variable, die auf einen Pointer zeigen */
{	
	char *temp = *text1;
	*text1 = *text2;
	*text2 = temp;
}

int main()
{
	char *text1 = "Hallo1", *text2 = "Hallo2";
	printf("text1: %s\ntext2: %s\n", text1, text2);
	tausche(&text1, &text2);  /* um Pointer vertauschen zu können, braucht die Funktion logischerweise deren Adressen und nicht den Inhalt ;) */
	printf("text1: %s\ntext2: %s\n", text1, text2);
	return 0;
}
 
Das man auch **text1 schreiben kann, wusste ich gar nicht :D Mit deiner Variante kann ich jetzt erstmal weiter verstehen, warum das so funktioniert.

Mal eine Frage zum internen: Es gibt ja auch eine Funktion strcpy (oder so), mit der man eben auch die Strings tauschen kann. Arbeitet diese Funktion ähnlich wie das hier? Oder ist sie effizienter?

Also nochmal
Code:
char s[]
erzeugt einen Array und

Code:
char *s
einen Pointer auf den Speicher.

Wenn ich im Netz nach den Unterschieden google ( string - What is the difference between char s[] and char *s in C? - Stack Overflow ) dann besteht nur darin der Unterschied, dass ich
Code:
s[0]='J';
belegen darf, was im Falle *s nicht ginge.

Wie würde ich denn die Funktion schreiben, wenn ich char s[] verwende? Geht das überhaupt ohne strcpy?
 
Zuletzt bearbeitet:
Das man auch **text1 schreiben kann, wusste ich gar nicht :D
Es geht auch mehr ***text1 (pointer-auf-pointer-auf-pointer) usw. ;)

Mal eine Frage zum internen: Es gibt ja auch eine Funktion strcpy (oder so), mit der man eben auch die Strings tauschen kann. Arbeitet diese Funktion ähnlich wie das hier? Oder ist sie effizienter?
Das sind verschiedene paar Schuhe.
str(n)cpy wird den Inhalt des char-arrays (aka "Speicherbereich") kopieren.
Und "tausche" vertauscht nur die Werte der Zeiger auf diese Arrays (Speicherbereiche), denn so hatte ich dein Anliegen verstanden ;)




Wie würde ich denn die Funktion schreiben, wenn ich char s[] verwende? Geht das überhaupt ohne strcpy?
Vom Datentyp ist ein Array doch etwas anderes als ein Pointer. Ein Arraybezeichner kann zudem (afaik) kein modifizierbares lvalue (L-value - Wikipedia) sein, daher sollte diese Trickserei mit dem (direkten) Vertauschen der Adressen nicht klappen. Allerdings ist diese nur bei Spielereien interessant ;), da man ja auch soetwas machen kann:
Code:
#include <stdio.h>

void tausche(char **text1, char **text2) /*nimmt einen Pointer auf Pointer entgegen, 
                                                                                                      d.h text1,text2 sind lokale Variable, die auf einen Pointer zeigen */
{
    char *temp = *text1;
    *text1 = *text2;
    *text2 = temp;
    }

int main()
  {
      char text1[] = "Hallo1 Welt1", text2[] = "Hallo2 Welt2";
      char *t1 = &text1[0], *t2 = &text2[0];
      printf("text1: %s\ntext2: %s\n", t1, t2);
      
      tausche(&t1, &t2);  /* um Pointer vertauschen zu können, braucht die Funktion logischerweise deren Adressen und nicht den Inhalt ;) */
      printf("text1: %s\ntext2: %s\n", t1, t2);
      return 0;
      }
 
Zuletzt bearbeitet:
Wie ich sehe, ist dieser Trick aber dann nicht für text1[] und 2 funktionierend. Gut. Ich muss in jedem Fall noch ein wenig über die Pointer nachdenken, aber ich denke, dass das Konzept gut nachvollziehbar ist. (Sollte ja logisch sein :D)

Dann erstmal vielen Dank für die Hilfestellungen und Erklärungen. Mein Vorhaben habe ich übrigens in nem Buch entdeckt. Deren Lösung ist das über strcpy zu lösen. Ich dachte aber, dass man, wie bei Integern, die Pointer umdefinieren kann. Sollte auch (logischer Weise) bei gleichlangen Arrays funktionieren, aber hier könnte der Aufbau eines Arrays widersprechen. Meine Idee ist eben gewesen, dass ein Objekt char s[10] (11 stelliger Character-Array?) so aufgebaut ist: Zeiger auf Speicherstart mit dem Wert s[0]. s[1] (und generall s[i++]) verweist dann auf &s[0]++. Als Lösung habe ich dann erdacht (zum Tauschen), dass man entsprechend &s[0] auf &t[0], für char t[10], umdefinieren kann.
Aber dann ist eben noch die Idee da, dass irgendwo hinterlegt sein muss, wie lang der Array ist, womit dann der Speicher nicht mehr von &s[0] bis &s[0]+11 reicht, sondern offenbar noch größer sein muss. (Wie viel Byte verbrauch ein Array insgesamt?)

Aber ich glaube, dass für mein Vorhaben das detaillierte Wissen darüber nicht unbedingt notwendig ist. Ich will insgesamt mit wirklich großen Zahlen arbeiten (>15120-Bit) bzw. Punkten auf Kurven, mit einer Basis-punkt-größe (Erzeuger) von ca. 256-521-Bit.
 
Meine Idee ist eben gewesen, dass ein Objekt char s[10] (11 stelliger Character-Array?) so aufgebaut ist: Zeiger auf Speicherstart mit dem Wert s[0]. s[1] (und generall s[i++]) verweist dann auf &s[0]++.
Ich bin nicht sicher ob ich dich richtig verstanden habe. Aber streng gesehen ist [] in dem Kontext ein Operator und s[0] (und generell s[n]) ist 'ne Array-subscription.
Ich hab' jetzt mal nachgeschlagen:
ISO/IEC 9899:TC3 hat gesagt.:
A postfix expression followed by an expression in square brackets [] is a subscripted
designation of an element of an array object. The definition of the subscript operator []
is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that
apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the
initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th
element of E1 (counting from zero).
Grob gesagt, array object ist ein (nicht modifizierbarer) Pointer auf das erste Element, Zugriff auf die anderen Elemente erfolgt über (Zeiger)arithmetik (pointer + X).

Aber dann ist eben noch die Idee da, dass irgendwo hinterlegt sein muss, wie lang der Array ist,
*hust* this is Sparta C. Die Größe des konstanten Arrays ist dem Compiler bekannt, aber (implizit) hinterlegt wird das nirgendwo - sonst gäbe es nicht so viele buffer-overflow Lücken ;)
 
Hey,
sowas in der Richtung habe ich gestern Abend auch kurz gelesen. Ein Array, sagen wir char s[3]="abc" ist so aufgebaut, dass
{
0 => 'a',
1 => 'b',
2 => 'c',
'\0'}
wobei die binäre 0 (d.h. '\0') das Ende des Arrays angibt. Also gibt es wohl ein "Break" Argument (sonst würde ja while(s[x]) für x++ eine Endlosschleife liefern) aber die exakte Größe ist nicht unbedingt bekannt.

Es gibt doch sicherlich auch Befehle, die die Länge des Arrays zurückgeben. Gibt es für solche Befehle auch Speedanalysen? (In cycles/op, Ausgaben in µs sind ja Hardware abhängig.) Oder allgemeiner: Gibt es irgendwo eine Seite, die solche Analysen sammeln?
 
Also gibt es wohl ein "Break" Argument (sonst würde ja while(s[x]) für x++ eine Endlosschleife liefern) aber die exakte Größe ist nicht unbedingt bekannt.
Ist eher eine Konvention bezüglich der Strings (denn man kann Arrays auch für andere Dinge nutzen ;) )
Und Endlosschleife wäre schön. Viel eher bekommt man aber eine SEGV/Access Violation nach dem verlassen des gültigen Speicherbereiches. Und falls in "while(s[x]) foo" noch der Inhalt modifiziert wurde - eine potentiell ausnutzbare Sicherheitslücke.

Es gibt doch sicherlich auch Befehle, die die Länge des Arrays zurückgeben.
Code:
sizeof(myarr)/sizeof(myarr[0])
Falls die Länge zur Compilierungszeit bekannt ist. Ansonten muss man entweder explizit speichern oder bei Strings "zählen" (strlen).

Gibt es für solche Befehle auch Speedanalysen? (In cycles/op, Ausgaben in µs sind ja Hardware abhängig.) Oder allgemeiner: Gibt es irgendwo eine Seite, die solche Analysen sammeln?
Die Performance ist Compiler und Bibliothekabhängig. Einfach mal zum Vergleich:
Comparison of C/POSIX standard library implementations for Linux
Code:
Performance comparison	musl	uClibc	dietlibc	glibc
Tiny allocation & free	0.005	0.004	0.013	0.002
Big allocation & free	0.027	0.018	0.023	0.016
Allocation contention, local	0.048	0.134	0.393	0.041
Allocation contention, shared	0.050	0.132	0.394	0.062
Zero-fill (memset)	0.023	0.048	0.055	0.012
String length (strlen)	0.081	0.098	0.161	0.048
Zum einen kann man einfach "timen": http://www.google.de/search?q=c+timing+bench&btnG=Suche
Ansonsten wäre das Stichwort "profiling" (z.B. gcc -pg)
Code:
% gcc -Wall -Wextra -pg  main.c -o habo
% gprof habo habo.gmon|more
...
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
  0.0       0.00     0.00       34     0.00     0.00  __vfprintf [646]
  0.0       0.00     0.00       29     0.00     0.00  arena_run_split [1]
  0.0       0.00     0.00       14     0.00     0.00  arena_avail_remove [2]
  0.0       0.00     0.00       13     0.00     0.00  memcpy [3]
  0.0       0.00     0.00       12     0.00     0.00  memchr [4]
  0.0       0.00     0.00       11     0.00     0.00  __malloc [647]
  0.0       0.00     0.00        8     0.00     0.00  __fflush [648]
  0.0       0.00     0.00        8     0.00     0.00  arena_avail_insert [5]
  0.0       0.00     0.00        6     0.00     0.00  __sfvwrite [649]
  0.0       0.00     0.00        4     0.00     0.00  __jemalloc_choose_arena_hard [650]
  0.0       0.00     0.00        4     0.00     0.00  __swrite [651]
  0.0       0.00     0.00        4     0.00     0.00  _once [652]
  0.0       0.00     0.00        4     0.00     0.00  _swrite [653]
  0.0       0.00     0.00        4     0.00     0.00  _write [654]
  0.0       0.00     0.00        4     0.00     0.00  localeconv_l [6]
  0.0       0.00     0.00        4     0.00     0.00  strlen [7]
  0.0       0.00     0.00        4     0.00     0.00  vfprintf [8]
  0.0       0.00     0.00        4     0.00     0.00  vfprintf_l [9]
  0.0       0.00     0.00        3     0.00     0.00  __jemalloc_arena_malloc_large [655]
  0.0       0.00     0.00        3     0.00     0.00  arena_run_alloc_helper [10]
  0.0       0.00     0.00        3     0.00     0.00  jemalloc_constructor [11]
  0.0       0.00     0.00        2     0.00     0.00  __smakebuf [656]
und/oder entsprechende Tools: Valgrind – Wikipedia die das messen.
4321d1492859750-c-anfaenger-pointer-2017-04-22-13-10-49.png

Allgmein zum Thema Optimierung fällt mir Software optimization resources. C++ and assembly. Windows, Linux, BSD, Mac OS X ein.
 
Etwas OT..

Vom Datentyp und Interna ist ein Array doch etwas anderes als ein Pointer.

"Damals" war es so, dass ein Verweis auf einen bestimmten Vektor, z.B. a[2] intern zum Zeiger wird, nämlich *(zeiger_auf_a+2).
Und zwar bei allen Adressierungen und unabhaengig vom Typ: Es wird (fuers in/dekrement) lediglich die Größe des Typs berücksichtigt, denn intern ist a[3] dann *(zeiger_auf_a+(3*Typegröße_von_a). Man kann (oder konnte?) auch statt a[2], genausogut *(a+2) schreiben was die Tatsache noch deutlicher hat (gilt sogar für &a[1]).

Unterschiede hatte man während des schreibens, denn ein Zeiger gilt als Variable auf den man alle möglichen Operatoren anwenden kann während ein Arrayname als Konstante gehandhabt wird.

Allerdings beziehe ich mich auf die Doku von Kernighan/Richtie selbst, also ist es möglich dass sich da was gaendert hat - ist zwar OT aber vielleicht kann da jemand was abschliessendes zu sagen?
 
"Damals" war es so, dass ein Verweis auf einen bestimmten Vektor, z.B. a[2] intern zum Zeiger wird, nämlich *(zeiger_auf_a+2).
Streiche "Interna" aus der Aussage (ich habe es jetzt wegeditiert) weil es falsch ist bzw. im Kontext der Syntax gemeint war. Man kann nämlich sowas machen:
Code:
#include <stdio.h>

void tausche(char **text1, char **text2) {
  char *temp = *text1;
  *text1 = *text2;
  *text2 = temp;
}

int main() {
  char text1[] = "Hallo1 Welt1", text2[] = "Hallo2 Welt2";
  char *t1 = text1, *t2 = text2;

  tausche(&t1, &t2);
  puts("tausche(&t1,&t2)");
  printf("t1: %s | t2: %s\n", t1, t2);
  printf("text1: %s | text2 %s\n", text1, text2);

  tausche(&text1, &text2);
  puts("tausche(&text1, &text2)");
  printf("t1: %s | t2: %s\n", t1, t2);
  printf("text1: %s, text2  %s\n", text1, text2);
  return 0;
}
und der Compiler schluckt das (wenn auch mit Warnungen). Das Ergebnis des zweiten Tauschs kann aber "überraschend" sein:
Code:
% gcc -O2 main.c -o habo && ./habo                                                                                 [138] 
tausche(&t1,&t2)
t1: Hallo2 Welt2 | t2: Hallo1 Welt1
text1: Hallo1 Welt1 | text2 Hallo2 Welt2

tausche(&text1, &text2)
t1: Hallo1 Welt2 | t2: Hallo2 Welt1
text1: Hallo2 Welt1, text2  Hallo1 Welt2
@Threadersteller: das schaut ein wenig nach Korintenkack^W Haarspalterei und Klugdefäkation aus, aber wenn auch C auf den ersten Blick einfach ausschaut, so gibt es viele kleine Teufelchen, die in den Details stecken - siehe z.B das Beispiel oben ;)
Ein weiters Beispiel und Stolperfalle wäre "int propagation":
Value Range Propagation | Dr Dobb's


denn intern ist a[3] dann *(zeiger_auf_a+(3*Typegröße_von_a). Man kann (oder konnte?) auch statt a[2], genausogut *(a+2) schreiben was die Tatsache noch deutlicher hat (gilt sogar für &a[1]).
Sofern man die Definition des Standards nimmt, scheint sich das nicht geändert zu gaben:
The definition of the subscript operator []
is that E1[E2] is identical to (*((E1)+(E2)))
 
Also ich verstehe mittlerweile den Zusammenhang zwischen Pointern (denke ich jedenfalls ^^)

Kurze Zwischenbemerkung: Das Buch, mit dem ich die Grundlagen erlese, notiert immer
Code:
main(){
    // Funktion
}
Wenn ich sowas ausführen wollen würde, bekäme ich einen Error.

Also zum Thema Pointer, laut dem Buch habe ich
Code:
void main(){
    int zahl = 1234, *ptr;
    ptr = &zahl; // speichert Adresse von zahl in ptr
    printf("%i\n",*ptr); // Zeigt den Wert, der in der Adresse "ptr" gespeichert ist. 
}

Soweit gut. Wie würde ich nun den Pointer auf einen Pointer, also **ptr , verarbeiten?
Nach obiger Logik wäre *ptr die Adresse und **ptr der Wert in *ptr. Demzufolge macht es recht selten Sinn ptr, *ptr und **ptr im selben Zuge zu verwenden?
 
Zurück
Oben