Передача char * vs char ** в качестве параметров функции в C

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

stackoverflow: pass-an- массив-строк-как-параметра-функции-в-с
stackoverflow: как-работает-массив-указателей-указателей-указателей
stackoverflow: whats-your-favourite-programmer-ignorance-pet-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(), почему значение разыменованного указателя возвращается к main (из get_args_works(), но не к значению указателя (из get_args_broken())?

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

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


person Thunder Rabbit    schedule 22.11.2010    source источник


Ответы (2)


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

является; параметр копируется по значению. Кроме того, вы не возвращаете 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, и вы хотели установить эту переменную функции на другой char **. НАПРИМЕР.

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

void caller()
{
  char **argv;
  int argc;
  trip_main(&argc, &argv);
}
person Matthew Flaschen    schedule 22.11.2010
comment
Хм. Думаю, это хороший простой способ понять это. Это никоим образом не говорит о char *. но тогда почему бы нам не утроить * argv? хмммм. - person Thunder Rabbit; 22.11.2010
comment
МОЙ БОГ !!! Как я пропустил неверный тип возврата? ? Я сочиняю этот вопрос дней ... !!!! д'ох !!!! - person Thunder Rabbit; 22.11.2010
comment
@ThunderRabbit: пора узнать, как включить больше предупреждений от вашего компилятора, или получить лучший компилятор. Если ваш компилятор не жалуется на функции, объявленные для возврата значения, которое возвращается без возврата значения, это ... ну, не совсем сломано, но не так полезно, как должно быть. Предупреждения компилятора - ваш друг - послушайте, как он говорит, и убедитесь, что он подробный. Для GCC хорошей отправной точкой является '-Wall'; Я обычно использую -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition (последнее, потому что я работаю над архаичным кодом). '-Wshadow' тоже хорош ... - person Jonathan Leffler; 22.11.2010
comment
Спасибо, Джон; Я использую gcc, поэтому обязательно проверю параметры -W. - person Thunder Rabbit; 23.11.2010

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

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

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

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

person Srikar Appalaraju    schedule 22.11.2010