9 Stimmen

Übergabe von char * vs char ** als Parameter an eine Funktion in C

Ich habe mehrere Diskussionen über die Übergabe von char * in C gelesen.

stackoverflow: Übergabe-einer-Reihe-von-Zeichenfolgen-als-Parameter-an-eine-Funktion-in-c
stackoverflow: Wie funktioniert ein Array von Zeigern zu Zeigern?
stackoverflow: was-ist-dein-liebstes-programmierer-ignoranz-tier-peeveve
drexel.edu: Zeichen-Arrays

In vielen von ihnen wird auch über Arrays gesprochen, aber davon möchte ich mich fernhalten.

Ich schreibe ein Beispielprogramm, um mir selbst etwas über die Weitergabe von char * y char ** in C. Dies ist eine Übung zur Übergabe von char *, ohne Verwendung von (Zeigern auf) Arrays. Auch keine Bedenken hinsichtlich der Ausführungseffizienz :-)

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

void get_args_works(int, char **, char **);
void get_args_broken(int, char **, char *);
char *get_string(int, char **);

int main(int argc, char **argv)
{
  char *string_works;
  char *string_broken;

  get_args_works(argc, argv, &string_works);
  get_args_broken(argc, argv, string_broken);

  printf("in main string_works (%p) = %s\n",string_works,string_works);
  free(string_works);

  printf("in main string_broken (%p) = %s\n",string_broken,string_broken);
  free(string_broken);
}

void get_args_works(int argc, char **argv, char **string)
{
    *string = get_string(argc, argv);
    printf("in get_args_works %p string %s\n",*string,*string);
}

void get_args_broken(int argc, char **argv, char *string)
{
  string = get_string(argc, argv);
  printf("in get_args_broken %p string %s\n",string,string);
}

char * get_string(int argc, char **argv)
{
  int i;
  char *string;
  string = malloc(40);

  // placeholder in case -s switch not found below
  strcpy(string,"-s switch not found below");

  for(i = 0; i < argc; i++)
    {
      if(argv[i][0] == '-')
        {
          switch(argv[i][1])
            {
            case 's':
              // release above malloc(40) for "-s switch not found below"
              free(string);
              // make room for storing variable
              string = malloc(strlen(argv[++i]) + 1);
              // the argv just after -s
              strcpy (string,argv[i]);
              break;
            }
        }
    }
  return string;
}

Sie können auch die gleicher Code auf github

Der obige Code ist einigermaßen selbsterklärend. main() deklariert zwei char *-Variablen und übergibt sie als Parameter an ihre jeweiligen get_args() Funktionen.

Jede get_args() Funktionsaufrufe char * get_string(int, char **) mit genau demselben Aufruf (aber mit einer anderen Methode zur Erfassung des Rückgabewerts).

get_string() funktioniert gut; es macht eine malloc() und gibt den Zeiger an die aufrufende Funktion zurück. Dieser Code funktioniert, und jede get_args() Funktion den Rückgabewert wie erwartet erhält.

Aber dann, wenn die get_args()-Funktionen zurückkehren zu main() warum wird der dereferenzierte Zeigerwert an main zurückgegeben (von get_args_works() aber nicht den Wert des Zeigers (aus get_args_broken() )?

(d.h. ich kann sehen, dass, wenn ich den Zeiger dereferenziere ( &string_works ), wenn sie als Parameter gesendet wird, funktioniert es. Aber warum? Ist nicht char * string_broken bereits ein Zeiger? Warum braucht es die "zusätzliche" Dereferenzierung, wenn es als Parameter gesendet wird?)

Ich hoffe auf eine überzeugende Antwort, die erklärt, wie Sie (ja, Sie) Senden char * als Parameter vs Empfangen es als Rückgabewert der Funktion konzeptualisieren.

4voto

Matthew Flaschen Punkte 266507
int get_args_broken(int argc, char **argv, char *string)
{
  string = get_string(argc, argv);
  printf("in get_args_broken %p string %s\n",string,string);
}

Sie ändern nur die string lokale (automatische) Variable. Diese ist für den Aufrufer in keiner Weise sichtbar. Beachten Sie, dass dies bedeutet, dass Sie einen wilden Zeiger in main freigeben.

Das ist aus demselben Grund falsch:

int get_sum(int sum, int a, int b)
{
  sum = a + b;
}

ist; der Parameter ist kopiert nach Wert. Außerdem geben Sie nicht einen int zurück (wie Sie erklärt haben).

int get_args_works(int argc, char **argv, char **string)
{
    *string = get_string(argc, argv);
    printf("in get_args_works %p string %s\n",*string,*string);
}

ist korrekt (außer dem fehlenden Return). Sie ändern nicht die string was sinnlos wäre. Sie modifizieren das Objekt unter den Standort in string die in diesem Fall eine char * .

EDIT: Sie müssten argv verdreifachen, wenn es eine Funktion gäbe, die main aufruft, und Sie die Variable dieser Funktion auf ein anderes Zeichen ** setzen wollten. Z.B..

void trip_main(int *argc, char ***argv)
{
  *argc = 10; 
  *argv = malloc(*argc * sizeof(char *));
}

void caller()
{
  char **argv;
  int argc;
  trip_main(&argc, &argv);
}

1voto

Srikar Appalaraju Punkte 68866

Eines der Erfordernisse für die Verwendung von Pointer to a pointer (hier get_args_works() ) ist es, mehr als eine Variable aus einer Funktion zu ändern (oder zurückzugeben), da es in C nicht möglich ist, mehr als eine Variable zurückzugeben.

get_args_works() funktioniert, weil Sie einen Zeiger auf einen Zeiger übergeben und ein Verweis darauf in Ihrer Datenbank vorhanden ist. main() .

Aber in get_args_broken() übergeben Sie nur einen Zeiger. Nichts falsch hier, jetzt tun Sie malloc() & die dem Speicher zugewiesene Zeichenkette zurückgeben an get_args_broken() noch nichts falsch. Aber jetzt ist diese aus dem Speicher zugewiesene Zeichenfolge lokal & main() hat keinen Verweis auf diese Variable. Wenn Sie also eine Dereferenzierung char *string_broken; in main() kann es zu undefiniertem Verhalten führen.

Ich hoffe, das ist klar.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X