Передача char * vs char ** в качестве параметров функции в C - PullRequest
9 голосов
/ 22 ноября 2010

Я прочитал несколько обсуждений прохождения char * в C.

stackoverflow: передача массива строк в качестве параметра в функцию в c
stackoverflow: как работает массив массивов указателей на указатели
stackoverflow: что-твой-любимый-программист-невежество-любимчик-peeve
drexel.edu: Массивы символов

Многие из них включают обсуждения массивов, но я хочу держаться подальше от этого.

Я пишу пример программы, чтобы научить себя передавать char * и char ** в C. Это упражнение по передаче char *, без использования (указателей на) массивов. Также нет проблем с эффективностью исполнения. : -)

#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;
}

Вы также можете просмотреть такой же код на github

Приведенный выше код несколько самодокументируется. main() объявляет две переменные char * и передает их в качестве параметров своим соответствующим get_args() функциям.

Каждая функция get_args() вызывает char * get_string(int, char **), используя один и тот же вызов (но по-разному собирает возвращаемое значение).

get_string() отлично работает; он делает malloc() и возвращает указатель обратно на вызывающую функцию. Этот код работает, и каждая функция get_args() получает возвращаемое значение, как я ожидаю.

Но тогда, когда функции get_args () возвращаются к main(), почему значение разыменованного указателя возвращается к основному (с get_args_works(), но не к значению указателя (с get_args_broken())?

(то есть я вижу, что если я разыменую указатель (&string_works) при отправке в качестве параметра, он работает. Но почему? Разве char * string_broken уже не указатель? Зачем нужна "дополнительная" разыменование при отправке в качестве параметра?)

Я надеюсь на выигрышный ответ, который объясняет, как вы (да, вы) концептуализируете отправку char * в качестве параметра против получения его в качестве возвращаемого значения функции.

Ответы [ 2 ]

4 голосов
/ 22 ноября 2010
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);
}

Вы изменяете только локальную (автоматическую) переменную string.Это никак не видно звонящему.Обратите внимание, что это означает, что вы освобождаете дикий указатель в main.

Это неправильно по той же причине:

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

is;параметр скопирован по значению.Кроме того, вы не возвращаете int (как вы заявили, что это так).

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);
}

правильно (за исключением пропущенного возврата).Вы не изменяете string, что было бы бессмысленно.Вы модифицируете объект в месте в string, что в данном случае является char *.

РЕДАКТИРОВАТЬ: вам нужно будет утроить * argv, если былфункция вызывает main, и вы хотите установить переменную этой функции в другой символ **.EG

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

void caller()
{
  char **argv;
  int argc;
  trip_main(&argc, &argv);
}
1 голос
/ 22 ноября 2010

Одной из потребностей использования указателя на указатель (здесь get_args_works()) является изменение (или возврат) большего, чем на переменную, функции, поскольку в С невозможно вернуть более одной переменной.

get_args_works() работает, потому что вы передаете указатель на указатель, и ссылка на него есть в вашем main().

Но в get_args_broken() вы передаете только указатель,В этом нет ничего плохого, теперь вы делаете malloc() и возвращаете строку памяти, выделенную для get_args_broken(), здесь все равно ничего плохого.Но теперь эта выделенная строка mem является локальной, и main() не имеет ссылки на эту переменную.Поэтому, когда вы разыменовываете char *string_broken; в main (), это может вызвать неопределенное поведение.

Надеюсь, это понятно.

...