Проблема с висячими указателями и массивами символов в C - PullRequest
2 голосов
/ 02 марта 2010
main(){
   char *cmd1[20] = {NULL};
   int x = parse_command(cmd1);
   printf("%s\ ",cmd1[0]);
}

parse_command(char *inTempString){
   char tempString[256];
   (call to function that assigns a string to tempString)
   cmd1[0] = tempString;
}

При распечатке cmd1[0] в main возникла проблема. Я почти уверен, что это ошибка висящего указателя. Я действительно не знаю, как это исправить.

Ответы [ 5 ]

4 голосов
/ 02 марта 2010

Есть две проблемы с вашей программой.

Во-первых, когда вы говорите:

char *cmd1[20] = {NULL};

cmd1 - это массив из 20 указателей на char. Это означает, что cmd1[i] для i в [0,20) является указателем на char.

В C существует правило, которое гласит, что передача массива в функцию только передает указатель на первый элемент массива функции. То есть, если у вас был такой код:

int ai[20];
f(ai);

тогда тип ai в вызове функции f(ai); равен int *, а указатель, переданный f(), равен &ai[0], первый элемент ai.

Итак, когда вы делаете:

parse_command(cmd1);

вы сразу знаете, что "вещь", переданная в parse_command(), является &cmd1[0], то есть указателем на первый элемент cmd1. Поскольку cmd1[0] имеет тип char *, вы передаете char ** в parse_command. Поэтому ваша декларация:

parse_command(char *inTempString);

неправильно, вы должны сделать:

parse_command(char **inTempString);

в соответствии с вашим звонком. Предполагается, что parse_command() будет анализировать более одного значения в cmd1. Если это так, вы также должны передать количество элементов в cmd1 в parse_commnd() - так как он не может знать, сколько элементов cmd1 имеет.

Ваша вторая проблема в том, что вы не можете вернуть адрес локальной переменной из функции в C. Как и выше, помимо вызова функции, возврата массива в C или присвоения чего-либо массиву в C также делает имя массива "распадающимся" для указателя на его первый элемент.

Итак, учитывая вашу функцию:

/* changed inTempString to cmd1 because that's what you probably meant */
int parse_command(char *cmd1)
{
    char tempString[256];
    /* call to function that assigns a string to tempString */
    cmd1[0] = tempString;
    /* you need to return an int from here */
}

tempString в присваивании cmd1[0] на самом деле &tempString[0], то есть указатель на первый элемент tempString. Но так как tempString уничтожается, как только функция возвращается, указатель становится недействительным. Вы не можете использовать значение позже.

На самом деле, в C имя массива уменьшается до указателя на его первый элемент во всех случаях, кроме:

  • при использовании в качестве операнда для оператора sizeof и
  • при использовании в качестве операнда для оператора адреса (&)

Чтобы быть более точным, в контексте объекта имя массива не распадается на указатель, а в контексте значения оно распадается на указатель. См. this для получения более подробной информации.

Теперь, как вы должны исправить вторую проблему? Это зависит от & mdash; вы можете либо динамически выделять память в parse_command(), и назначать эту память для cmd1[0], либо вы можете сделать tempString static в функции. Поскольку static переменные в функции не уничтожаются при возврате функции, вы можете продолжать использовать указатель на нее. Динамическое распределение - это больше работы - вам нужно беспокоиться о сбое выделения и помнить, чтобы освободить указатель, когда закончите. static массив проще, но вы должны быть осторожны, потому что другой вызов parse_command перезапишет массив, делая его менее универсальным.

Предполагая, что вы хотите пойти по пути "динамической памяти", вот схема, которую вы можете использовать:

#include <stdio.h> /* printf */
#include <stdlib.h> /* malloc and free */

int main(void) /* main returns int */
{
    char *cmd1[20] = {NULL};
    /* number of commands.  "sizeof cmd1" is the number of bytes
       used by the cmd1 array, and "sizeof cmd1[0]" is the number
       of bytes used by one element of the array.  The division
       gives you the number of elements.  This is 20 of course
       but doing it this way makes sure that changing "20" to any
       number works. */
    size_t ncmds = sizeof cmd1 / sizeof cmd1[0];
    /* pass the number of commands to "parse_command", since
       it can't know otherwise */
    int x = parse_command(cmd1, ncmds);
    int i;
    for (i=0; i < x; ++i) {
        printf("%s ", cmd1[i]);
        free(cmd1[i]);
    }
    return 0; /* return a value from main */
}

int parse_command(char **cmd1, size_t ncmds)
{
    char *tempString; /* we will malloc this */
    int i; /* the number of mallocs done successfully */
    tempString = malloc(...);
    if (tempString == NULL) {
    /* failure, handle gracefully */
    } else {
        ++i; /* make sure i doesn't exceed or equal ncmds */
    }
    cmd1[0] = tempString;
    /* do the above as many times as you need */
    return i; /* the number successfully assigned to */
}
0 голосов
/ 02 марта 2010

Вы пытаетесь получить доступ к переменной cmd1, которая находится внутри main функции из parse_command.

Я бы сказал, что по крайней мере cmd1[0] будет выглядеть как целое число, потому что оно не объявлено в этом методе.

cmd1 объявлен как array of char*, но параметром метода является char*, который может быть pointer to char array, но не pointer to array of char*.

Лучший способ скопировать массив char в другой массив char - это использовать memcpy, strcpy или аналогичные методы, которые принимают указатели на src, dest и размер для копирования.

0 голосов
/ 02 марта 2010

Да, ты не можешь этого сделать.

char tempString[256];

объявляет переменную в стеке в функции parse_command, эта переменная выходит из области видимости и указатели на нее перестают быть действительными, когда возвращается parse_command.

Вам необходимо разместить командную строку в куче, чтобы она все еще действовала, когда вернется parse_command. Это один из способов.

parse_command(char *inTempString){
   char tempString[256];
   (call to function that assigns a string to tempString)

   int cb = strlen(tempString)+1;
   cmd1[0] = (char *)malloc(cb);
   strcpy(cmd1[0], tempString);
}

Вы должны также позвонить free(cmd[0]) до главных выходов.

Кроме того, этот код не компилируется. Вы не можете ссылаться на cmd1[0] внутри функции parse_command. Вы должны получить несоответствие типов, когда попытаетесь передать cmd1 в parse_command. Если вы хотите вернуть char * из parse_command, тогда должно быть объявлено, что в качестве аргумента принимается char **, больше похоже на это .

parse_command(char **pcmd){
   char tempString[256];
   (call to function that assigns a string to tempString)

   int cb = strlen(tempString)+1;
   pcmd[0] = (char *)malloc(cb);
   strcpy(pcmd[0], tempString);
}
0 голосов
/ 02 марта 2010

Примерно так будет работать:

parse_command(char **inTempString){
        static char tempString[256];
        strcpy(tempString,"some string you want to copy");
        inTempString[0] = tempString;
}
  • В вашем коде tempString не будет существует, как только функция возвращается. Вы нужно сохранить его даже после функция возвращает. Вы можете выделить пространство динамически и выделять позже или вы можете объявить это как статичный.
  • Также вам нужно изменить тип аргумент inTempString из char * в символ **.
0 голосов
/ 02 марта 2010

Вы объявляете cmd1 в main как char ** - то есть указатель на указатель на char. Однако затем вы передаете его в parse_command, который вы определили как взятие символа *; указатель на символ.

Это компилируется только из-за автоматического преобразования указателя на что-либо в указатель на символ. Это исторический артефакт старых версий C, которые использовали «char *» вместо «void *»; в вашем случае это просто означает, что компилятор игнорирует ошибку типа, которую вы сделали, а не сообщает о ней вам.

...