1

Sonntag, 2. August 2009, 00:41

[C] Frage zur Main-Funktion

In Java sieht die Main-Methode ja folgend aus:

Quellcode

1
2
3
4
public static void main(String[] arguments)
{
    //irgendwas
}


Das C-Pendant dazu sieht aber so aus:

Quellcode

1
2
3
4
int main(int argc, char *argv[])
{
    /* irgendwas */
}


argv ist hierbei also ein Array von Char-Pointern.
Da man in C ja keine Strings hat, benutzt man ja hier Char-Arrays. Soweit, so gut.

Warum aber übergibt man hier einen Array von Char-Pointern? Jeder Char-Pointer zeigt ja auf einen Buchstaben, oder?
Außerdem, wenn man dann auf ein Argument zugreifen möchte, schreibt man Folgendes:

Quellcode

1
2
printf("Name des Programms: %s\n", argv[0]);
printf("Argument 1: %s", argv[1]);

Wenn ich das richtig verstehe, ist das intern dasselbe wie

Quellcode

1
2
printf("Name des Programms: %s\n", &argv[0][0]);
printf("Argument 1: %s", &argv[1][0]);


Damit wird doch jetzt aber auch nur auf einen Buchstaben gezeigt! = /

Wäre nett, wenn mir das nochmal jemand genauer erklären und etwas Durchblick schaffen könnte! (:

Evrey

Oberschurke im Ruhestand

Motto: "Satzzeichen sind keine Rudeltiere." - Chesra

  • Nachricht senden

2

Sonntag, 2. August 2009, 00:59

Der argv[] sammelt Strings aus der Konsole, die durch "\s" oder "\n" oder "\t" getrennt sind. Jeder substring wandert dann in eine Zelle des argv[]. Da Strings streng genommen ohne Klasse ein Array aus chars sind, kannste das Array argv[] als zweidimensionales Array sehen. In der zweiten Dimension schaltest du hierbei durch die einzelnen Zeichen.
Beispiel, in der Console (z.B. CMD) tippste:

Quellcode

1
MeinProg hallo 33


argv[] sieht dann so aus:

Quellcode

1
argv = {{'M','e','i','n','P','r','o','g'},{'h','a','l','l','o'},{'3','3'}};


PS: Gilt auch für C++.
args gibt nur die Größe der ersten Dimension an. Die Größe der Strings erfährste durch:

Quellcode

1
unsigned short int length = sizeof(argv[i]);
  • :medal: Werbung

    Bild

    Cpp Quellcode

    1
    
    #define TRUE FALSE //Happy debugging suckers
    (Einfach nur wundervoll.)
  • :palette: 1plus3 :cake:

    Bild
  • :fires: Nuuuhminaaah

    Bild
  • :medal: compétences

    mes compétences
    :heart_full: :heart_full: :heart_full: :heart_full: :heart_full: max.
    :ruler-triangle: Maps machen :heart_full: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :media-player: Musik machen :heart_full: :heart-half: :heart-empty: :heart-empty: :heart-empty:
    :cup: Scripts machen :heart_full: :heart_full: :heart_full: :heart_full: :heart-break:
    :paper: Story ausdenken :heart_full: :heart_full: :heart_full: :heart-empty: :heart-empty:
    :cut: Pixeln und so :heart-empty: :heart-empty: :heart-empty: :heart-empty: :heart-empty:
    :game: Events proggen :heart_full: :heart_full: :heart_full: :heart_full: :heart_full:
    (Dieser Tab ist rein satirisch.)
  • :folder-open: mes projets

    • :addressbook: Silentium
      :book: Name: Silentium
      :rmxp: Maker: Eigenbau (C++, x86-SSE/AVX-Assembly, Ruby/Lua)

      :paper: Story
      :game: NPCs
      :cup: Scripts
      :drill: Ressis
      :ruler-triangle: Maps
      :compile: Gesamt
      (3+4)% 42 69% 0815 -17.438 103.38% ± 6.3mm²

      (Die Tabelle erfüllt lediglich satirische Zwecke.)
    • :compile: Onyx
      Eine in C++ implementierte, modulare, plattformunabhängige, virtuelle Maschine. Die Test-Version ist bereits halb fertig. Ab dann gibt es vielleicht mehr Infos. Sie soll die auf dem ersten Blick LISP-artige und eigens dafür konstruierte Sprache Obsidian ausführen können. Experimentell wird auch ein Lua-Compiler für Onyx gebaut. Ziel ist eine leistungsfähige, virtuelle Maschine für beliebige Scriptsprachen. Theoretisch gesehen müsste man bloß noch einen kompatiblen Compiler schreiben, der Quellcode jener Sprache in Onyx-Assembly, oder direkt in Onyx-Bytecode übersetzt. Ob die jemand nutzen wird, ist eine andere Frage und nur ein sekundäres... nein, eher tertiäres Ziel dieser VM. Primär dient es mir lediglich dazu, mein Verständnis von Hardware, ISA, und Assembly zu vertiefen, sowie eigene Grenzen auszutesten.

      :exclamation: Warnung!
      Das Entwickeln einer virtuellen Maschine oder Programmiersprache (im wahnsinnigsten Fall beides) ist eine höchst komplizierte Tätigkeit, aus der viel Frust und Hirnmatsche hervor gehen. Sollte sich dennoch ein ähnlich wahnsinniger finden, der sowas zusammen schustern will, so lege ich ihm/ihr die folgenden Bücher ans Herz:
      • Compiler - Das Drachenbuch [978-3-8273-7097-6]
        Dieses Buch schlachtet ausführlich und leicht verständlich die Grundlagen bis hoch zu den Experten-Techniken des Compilerbaus aus. Es fängt mit der Automaten-Theorie und formalen Sprachen an, arbeitet sich durch Analysetechniken vor, und landet schließlich bei Techniken wie Optimierung und Register-Zuweisung. Das Buch wiegt 3Kg oder 4Kg. Hab's mal gewogen. Ist also nicht gerade die Lektüre für unterwegs.

      • Computerarchitektur [3-8273-7016-7]
        Hier werden leicht verständlich die wichtigsten Entwicklungen der Rechnerarchitekturen erklärt (Gut, das Buch ist in die Jahre gekommen, aber der Weg zu heute ist ein winziger Schritt, den man sich nach diesem Buch selbst erdenken kann). Hauptbestandteil des Buchs ist eine relativ umfassende Betrachtung der Funktionsweise dreier gänzlich unterschiedlicher, aber dominierender Prozessor-Typen am Beispiel des Pentium II, UltraSPARC II, sowie picoJava. Die meisten Elemente dieses Buchs sind zwar für die Konstruktion einer virtuellen Maschine irrelevant, oder aufgrund der Tatsache, dass die VM Software ist und z.B. Byte-Grenzen hat, sogar zu Leistungseinbußen führen kann, doch ist ein hinreichendes Verständnis dieser Maschinen, mit denen wir arbeiten, äußerst hilfreich für die Überlegungen, wie die VM arbeiten soll.

      Es kann sehr hilfreich und inspirierend sein, den Code quelloffener, virtueller Maschinen anderer Sprachen zu überfliegen. Meine Lieblings-Quelle war und ist stets die VM von Lua. Sie ist schlank, verständlich, in C implementiert, und basiert im Gegensatz zu vielen anderen Scriptsprachen-VMs auf einer Register-Maschine statt einer Stapelmaschine. Es wäre natürlich vorteilhaft, die entsprechende Sprache zu verstehen, in der man auch die eigene VM implementieren will. Weiterhin ist es äußerst vorteilhaft, eine leistungsstarke und bequeme Sprache wie C++ zu beherrschen, um die VM zu implementieren. Und bevor irgendwer auf die Idee kommt: Assembly ist NICHT als dominierende Sprache für den Bau einer VM geeignet. Wer die Frage des "Warum?" nicht beantworten kann, sollte zunächst die gewählte Sprache und Assembly hinreichend verstehen lernen, und es dann erneut mit der Frage versuchen. Es lohnt sich dennoch, Assembly zu lernen. Allein schon, um erneut das Verständnis zu vertiefen, zumal ihr mehr oder weniger gezwungen seid, auch für eure VM eine Assembler-Sprache zu entwickeln (Außer natürlich ihr schreibt eure Test-Programme Bit für Bit ;3).
  • :locale: enfin

    Je ne peux pas parler français.
    C'est tout ce que Goodle et les restes de cours de français.
Signaturstand: 24.07.2013

3

Sonntag, 2. August 2009, 01:07

Pointer und Array sind so ziemlich dasselbe. Ein Pointer zeigt immer auf eine Speicheradresse und hat einen spezifischen Typ, der wiederum eine spezifische Größe hat.
*pointer löst den Pointer auf und gibt dir den Wert auf der Speicheradresse. *pointer ist aber dasselbe wie pointer[0]. Beide Schreibweisen sind äquivalent. pointer[1] besorgt dir den Wert auf der speicheradresse pointer + sizeof(pointertyp). Oder in C-Code:

Quellcode

1
2
3
4
5
6
7
8
9
char *pointer = malloc(5); // reserviert dir 5 Byte Speicher
sizeof(char); //=> Typ char ist 1 Byte groß
pointer; //=> ist irgendeine Speicheradresse, z.B. 100
*pointer = 'c';
*pointer; // => ist 'c'
pointer[0]; //=> ist 'c'
pointer[1] = 'd';
pointer[1]; // ist 'd'
pointer[5] == *(pointer + sizeof(char) * 5); // wahre Aussage


Ein Array unterscheidet sich von einem Pointer also nur insofern, dass er
- auf dem Stack gespeichert werden kann, falls du ihn als normale Variable in einer Funktion deklarierst
- er für dich den Speicher vorreserviert, so dass du nicht extra malloc verwenden musst.

Ein Pointer vom Typ char* kann also ein Zeiger auf ein einzelnes Zeichen sein. Es kann aber auch ein Zeiger auf eine ganze Reihe von Zeichen sein. Damit du weißt wie viele Elemente in so einem Zeiger stecken (also die Länge des Arrays) hast du zwei Möglichkeiten:
- Du gibst die Länge als Parameter an
- Du setzt ans Ende des Feldes ein reservierten Wert. Wenn man das Feld abgeht und auf den reservierten Wert stößt, weiß man, dass das Feld hier zuende ist. Auf die Weise kann man die Länge selbst berechnen, in dem man einfach alle Felder bis zum Endfeld zählt

Bei Strings wird letztere Möglichkeit verwendet. Jeder String endet mit dem Zeichen \0, so dass klar ist wie lang ein String ist. Bei anderen Datentypen kannst du das zwar auch so machen, es ist aber üblicher die Länge des Arrays per Parameter mitzugeben. Genau das macht auch die main-Funktion.
argv ist ein Array. Die Länge des Arrays bekommst du über den zweiten Parameter, argc.
argv zeigt auf *char Pointer. Dies können einzelne Zeichen sein, es können aber auch Felder von Zeichen sein. Bei der Main-Methode darfst du von letzterem ausgehen, also das argv ein String-Array ist. Die Länge jedes einzelnen Strings wird von dem Nullzeichen festgelegt.

Edit: Ah, da hat schon wer vor mir gepostet. Envrey hat recht, aber ich bin mir unsicher was sein sizeof Beispiel angeht. IMO ist sizeof eine Funktion/Macro/Wasauchimmer, die dir die Größe eines Datentyps ausgibt. Du weißt von einem Pointer aber nicht, wie groß das Feld ist, auf das er zeigt. sizeof(argv[1]) gibt dir nur die Länge eines Pointer-Typs zurück. Für String-Längen musst du die Zeichen bis zum Endzeichen zählen (ist also auch nicht ganz unaufwendig!). Dafür gibt es die strlen Funktion.
Ich hoffe das stimmt so *es selbst nicht ausprobiert hat*
Bild
RMXP Grundkurs
1 2 3
Ruby/RGSS-Kurs

4

Sonntag, 2. August 2009, 02:10

Ah, danke ihr beiden für die ausführlichen Antworten =) Also basiert ein Array eigentlich nur auf angewandter Pointerarithmetik?

Nebenbei nochmal eine Frage:
Wenn ich zum Beipspiel Folgendes schreibe (verzichten wir mal auf die %u201EPräprozessordirekt)

Quellcode

1
2
char *string[5];
strcpy("Mehr als 5 Zeichen", string);

, dann überschreibt C ja andere Speicherstellen, die nicht mehr für string reserviert sind. Heißt das also, dass dadurch irgendwelche andere Variablen, vielleicht sogar aus anderen Programmen zerstört bzw. überschrieben werden? Und warum ist das nach x Jahren C denn immer noch so?

5

Sonntag, 2. August 2009, 03:41

um das für sizeof zu ergänzen:
Das gilt nur, wenn es sich wirklich um ein Array handelt. Arrays sind in C ein eigenständiger Typ und verhält sich eben bei Dingen wie sizeof anders (und in C++ gibt es bei typeid auch was anderes aus).
sizeof liefert bei Pointern die Größe des Speichers, den der Pointer belegt, bei Arrays die Größe, die das gesamt Array im Speicher belegt. Die Sache ist nur die, dass du Arrays nicht als Parameter in Funktionen haben kannst! Wann immer ein Array als Parameter deklariert ist, handelt es sich in Wahrheit um einen Pointer und beim Aufruf wird das Array implizit in einen Pointer gecastet, um das zu verdeutlichen folgender Code:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
void foo(char arr[])
{
	printf("%d", sizeof(arr)); // 4
}
 
int main()
{
	char arr[] = {2 ,3};
	printf("%d", sizeof(arr)); // 2
	foo(arr);
 
}


Auch für Stringlängen verhält sich das ganze ähnlich, für ein echtes char [] liefert dir sizeof auch die Größe des Strings(+ 1), aber es handelt sich hier in Wirklichkeit um char*. strlen liefert dir jedenfalls immer die Größe des Strings.

@Kai:
Das dein Code funktioniert ist zufall;) Das geht so nur mit chars, denn wenn du 1 zu einem Pointer addierst, wird nicht 1 zur Addresse addiert, sondern sizeof(T)
pointer[5] == *(pointer + sizeof(char) * 5); // wahre Aussage
ist nur also nur eine wahre Aussage, weil sizeof(char) 1 ist, richtig wäre
pointer[5] == *(pointer + 5);

Zitat

Heißt das also, dass dadurch irgendwelche andere Variablen, vielleicht sogar aus anderen Programmen zerstört bzw. überschrieben werden? Und warum ist das nach x Jahren C denn immer noch so?

Kann passieren, muss aber nicht. Keine Ahnung wie genau sich da C oder C++ Standard unterscheiden, in C++ ist afaik undefiniertes Verhalten, es kann passieren was will, es kann ein Fehler kommen, oder auch sonst was passieren.
-
edit:
richtig, in einem modernen Betriebssystem hat jedes Programm seinen eigenen Adressraum, weswegen du kaum in der Lage sein wirst andere Programme durch so etwas zum abstürzen zu bringen oder gar ihr Verhalten zu modifizieren.
-
Der Grund dafür ist, dass C eine Low-Levelsprache ist, sie ist besonders schnell und nah am Prozessor, runtimechecks auf Boundaries sind es nicht, es würde die resultierende Binary unnötig aufblähen und verlangsamen. Es hat auch nichts damit zu tun, wie alt C ist, es war damals genau so möglich wie heute, ist passt nur nicht zur Zielsetzung der Sprache.
C++ benutzt die selbe Low-Levelbasis, wrappt das ganze in der Standardbibliothek aber in Klassen, sodass man Boundchecks haben kann, wenn man will.. Eine sicherere Variante wäre evtl. immer strncpy zu verwenden, wenn wir jetzt speziell von strcpy reden.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Ankou« (4. August 2009, 00:39)


6

Montag, 3. August 2009, 20:16

vielleicht sogar aus anderen Programmen zerstört bzw. überschrieben werden?

Nö. Da passt dein Betriebsystem drauf auf, dass ein Programm keinen Zugriff auf Speicher hat der ihm nicht gehört. Bei Linux gibts dann den allseits beliebten "Segmentation Fault". Windows gibt dir großatige Möglichkeit einen Bericht an Microsoft zu schicken (die können dir aber dann auch nicht helfen).
Es kann allerdings passieren, das andere Variablen im gleichen Programm übeschrieben werden.


Und warum ist das nach x Jahren C denn immer noch so?

Tjoa weil wie Ankou schon sagt es Zeit kosten würde, wenn bei jedem Zugriff geprüft würde ob du keinen Quatsch programmiert hast. Wenn du willst die auf die Finger geschaut wird, musst du entweder ne andere Sprache verwenden oder eine Bibliothek verwenden, die ne komfotabelere Stringklasse anbietet (zB die STL in C++). Das wird allerdings IMMER Zeit kosten. Die Frage ist dann eben immer, ob du so sehr darauf angewiesen bist Laufzeit zu sparen, dass du dafür in Kauf nimmst c-Strings zu verwenden.

Ähnliche Themen

Social Bookmarks