C: проблема с символом * - PullRequest
       2

C: проблема с символом *

2 голосов
/ 22 января 2010
/*
 * code.c
 *
 * TASK
 *      Reverse a string by reversing pointers. Function should use return
 *      type char* and use a char* parameter as input.
 */
#include <stdio.h>
#include <string.h>
#define STRMAX 51

char* reverse(char* sPhrase[]);

int main() {
    char sPhrase[STRMAX];
    char sReverse[STRMAX];
    printf("Enter string (max. 50 chars): ");
    gets(sPhrase);
    sReverse = reverse(sPhrase);

    return 0;
}

char* reverse(char* sPhrase[]) {
    char* sOutput[STRMAX];
    int iCnt = 0, iCntRev;

    for (iCntRev = strlen(*sPhrase)-2; iCntRev >= 0; iCntRev--) {
        sOutput[iCnt] = sPhrase[iCntRev];
        iCnt++;
    }

    *sOutput[iCnt] = '\0';      // Don't forget to close the string

    return sOutput;
}

Этот код имеет некоторые причуды:

  • sReverse = reverse(sPhrase);

    • [Ошибка] несовместимых типов в присваивании
    • [Предупреждение] передача аргумента 1 `reverse 'из несовместимого типа указателя
  • return sOutput;

    • Функция [Warning] возвращает адрес локальной переменной
    • [Предупреждение] возврат из несовместимого типа указателя

Что означают эти предупреждения? Как я могу исправить ошибки? Функция должна сохранять char * как тип возвращаемого значения и как параметр, так как я делаю эту небольшую программу как часть учебного курса C.

Ответы [ 7 ]

6 голосов
/ 22 января 2010

Я вижу несколько проблем. Прежде всего, char* sOutput[STRMAX] - это массив char* - возможно, вы имели в виду char sOutput[STRMAX]?

Во-вторых, и что более важно, когда вы объявляете массив в функции таким способом (char sOutput[STRMAX]), он распределяется в стеке и освобождается, когда функция возвращает . Таким образом, если вы попытаетесь вернуть его, вы получите неопределенные результаты, потому что технически он больше не должен существовать!

Решение состоит в том, чтобы передать в функцию буфер для использования:

char* reverse(char const sPhrase[], char sOutput[])

(я добавил const, чтобы вы случайно не перезаписали sPhrase). Затем позвоните reverse так:

reverse(sPhrase, sReverse);

Теперь о том, работает ли ваш алгоритм ...

3 голосов
/ 22 января 2010

Как убрать пару вещей:

  1. НИКОГДА НИКОГДА НЕ НИКОГДА использовать gets(); это будет вводить точку отказа в вашей программе. Если вы передадите, получите буфер размером 10 символов, и пользователь введет 100 символов, gets() с радостью запишет эти дополнительные 90 символов в память сразу после вашего буфера, что приведет к всевозможным хаосам. Переполнения буфера - это простое и распространенное вредоносное средство, и любой код, использующий gets(), небезопасен по конструкции . Вместо этого используйте fgets().

  2. Вы не можете назначать объекты массива, как вы делаете в строке sReverse = reverse(sPhrase);. Если вы хотите скопировать содержимое строки, возвращаемой reverse, вам необходимо использовать strcpy() или аналогичный: strcpy(sReverse, reverse(sPhrase));

Диагностика "несовместимые типы в назначении" основана на том факте, что вы пытаетесь присвоить значение указателя (char *) объекту массива (char [STRMAX]). Как я упоминал выше, вы все равно не можете назначить объект массива.

Предупреждение «Передача аргумента из несовместимого типа» происходит из-за того, что определение вашей функции типизировано не для ожидания указателя на символ, а для указателя на указатель на символ. Измените определение функции на

char *reverse(char *sPhrase) {...}

Почему *sPhrase вместо sPhrase[] (или *sPhrase[])? Прежде всего, когда выражение массива появляется в большинстве контекстов, его тип неявно преобразуется из «массива N-элементов T» в «указатель на T» (выражение распадается в тип указателя) и его значение устанавливается по адресу первого элемента в массиве; Исключениями из этого правила являются случаи, когда выражение массива является операндом операторов sizeof или & (address-of), или если выражение массива является строковым литералом, используемым для инициализации массива char в объявлении. , Во-вторых, в контексте определения параметра функции T a[] идентичен T *a; обе формы объявляют a как указатель на T.

Когда вы вызываете reverse(sPPhrase); в main, тип выражения sPPhrase затухает от «массива STRMAX-элемента char» до «указателя на char», поэтому необходимо ввести формальный параметр в функции как char *.

Вы все равно можете применить оператор индексации к sPhrase, поскольку подписка определяется в терминах арифметики указателей, но помните, что sPhrase - это значение pointer , а не массив.

Как вы сейчас написали, reverse ожидает параметр типа char *[], который идентичен char **, как если бы вы передавали массив указателей на char, а не массив char. Аналогично, ваша переменная sOutput в reverse должна быть объявлена ​​char sOutput[STRMAX];, а не char *sOutput[STRMAX]; (последняя объявляет sOutput как массив указателей на char, что здесь не то, что вам нужно; это источник предупреждение "возврат из несовместимых типов").

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

  1. Объявите sOutput как статический: static char sOutput[STRMAX];. Это приведет к тому, что память для sOutput будет выделена при запуске программы и останется выделенной до выхода из программы, поэтому содержимое массива будет сохраняться между вызовами reverse. Переменная все еще является локальной для функции (к ней нельзя обратиться по имени вне этой функции). Однако это означает, что функция больше не является поточно-ориентированной, и это уродливое решение.

  2. Динамически выделить буфер в reverse и вернуть адрес этого. Преимущество этого состоит в том, что вы можете изменять размер буфера по мере необходимости, и вам не нужно беспокоиться о безопасности потоков. Буфер будет сохраняться до тех пор, пока вы явно не освободите его (или пока программа не закроется). Недостаток заключается в том, что вызывающая сторона теперь отвечает за освобождение этой памяти, когда она закончена. Лучший способ избежать головной боли с управлением памятью - это, во-первых, избегать управления памятью, и эта проблема на самом деле не требует этого.

  3. Выполните обратную операцию на месте (то есть сделайте обратную операцию во входном массиве) и верните адрес входного массива. Это означает, что вы вводите данные в заблуждение, что может быть не тем, что вы хотите.

  4. Передайте массив назначения как второй вход функции и не беспокойтесь о возвращаемом значении (или, если у вас есть , чтобы вернуть что-то, верните адрес массива назначения) :

    char *reverse(char *src, char *dst)
    {
      // write contents of src in reverse order to dst
      return dst;
    }
    ...
    reverse(sPPhrase, sPReverse);

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

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

0 голосов
/ 22 января 2010

Это работает, если я устанавливаю тип возвращаемого значения void:

/*
 * code.c
 *
 * TASK
 *      Reverse a string by reversing pointers. Function should use return
 *      type char* and use a char* parameter as input.
 */
#include <stdio.h>
#include <string.h>
#define STRMAX 51

void reverse(char* sPhrase[]);

int main() {
    char sPhrase[STRMAX];
    char* sPPhrase[STRMAX];
    char* sPReverse[STRMAX];
    int iCntr;

    printf("Enter string (max. 50 chars): ");
    gets(sPhrase);

    for (iCntr = 0; iCntr < strlen(sPhrase); iCntr++) {
        sPPhrase[iCntr] = &sPhrase[iCntr];
    }

    reverse(sPPhrase);          // Disabled return type char*

    return 0;
}

void reverse(char* sPPhrase[]) {
    char* sPOutput[STRMAX];
    int iCnt = 0, iCntRev;

    for (iCntRev = strlen(*sPPhrase)-2; iCntRev >= 0; iCntRev--) {
        sPOutput[iCnt] = sPPhrase[iCntRev];
        iCnt++;
    }

    *sPOutput[iCnt] = '\0';      // Don't forget to close the string

    // return sPOutput;     // Disabled return type char*
}

Как только я возвращаю типы возврата в игру, все идет не так. sPReverse = reverse(sPPhrase); по-прежнему возвращает несовместимые типы в присваивании ошибка, даже если возвращаемый тип совпадает с типом переменной, в которой я храню возвращенный массив указателей.

0 голосов
/ 22 января 2010

Я новичок в указателях и вообще в c, но похоже, что reverse () ожидает указатель , и вы передаете ему фактический массив символов. попробуйте изменить строку:

char sPhrase[STRMAX];

до:

char *sPhrase[STRMAX];
0 голосов
/ 22 января 2010
char sPhrase[STRMAX];

- это автоматическая переменная в стеке, поэтому ее адрес не может быть переназначен.должен быть объявлен как:

char *sPhrase;

Это определяет массив указателей STRMAX на char (как авто переменную, которая будет уничтожена после возврата из функции).

char* sOutput[STRMAX];

что вам нужно здесьis:

char* sOutput = malloc( STRMAX );

Поскольку вы не хотите, вы возвращаете дублированную строку живым после возврата.

0 голосов
/ 22 января 2010

Параметры не совпадают с именами переменных.

  • sPhrase объявлен как массив символов.
  • Функция reverse имеет параметр sPhrase, который является указателем на массив символов

Это объясняет, почему существует «несоответствие типов». Я включил исправленную версию, попробуйте это.

char* reverse(char *sPhrase);

int main() {
    char sPhrase[STRMAX];
    char sReverse[STRMAX];
    printf("Enter string (max. 50 chars): ");
    gets(sPhrase);
    sReverse = reverse(sPhrase);

    return 0;
}

char* reverse(char* sPhrase) {
    /* <strike>char* sOutput[STRMAX];</strike>*/
    static char sOutput[STRMAX];
    int iCnt = 0, iCntRev;

    for (iCntRev = strlen(sPhrase)-2; iCntRev >= 0; iCntRev--) {
        sPhrase[iCnt] = sOutput[iCntRev];
        iCnt++;
    }

    sPhrase[iCnt] = '\0';      // Don't forget to close the string

    return sOutput;
}

Компилятор достаточно умен, чтобы проверять наличие «несовместимых» типов. C строгий очень расслаблен, когда дело доходит до такого рода вещей, так что вы обязаны убедиться, что объявления соответствуют определениям для успешной компиляции.

Я также исправил проблему с использованием переменной sOutput в функции reverse и изменил ее на static, так как переменная выйдет из области видимости и окажется с мусором, добавив префикс ключевого слова static гарантирует, что переменная останется в области видимости.

Редактировать: По какой-то причине я добавил метку удара, чтобы вычеркнуть линию, но другие как-то ее видят ... ???

При ближайшем рассмотрении код не работает, я исправил его, принимая во внимание чужие отзывы и замечания, обратите внимание, что я использовал fgets вместо злой функции gets, поскольку это питательная среда для переполнения буфера, также изменил сигнатуру функции, чтобы использовать ключевое слово const для параметра.

char * reverse(const char *sPhrase);

int main() {
    char sPhrase[STRMAX];
    char *sReverse;
    printf("Enter string (max. 50 chars): ");
    fgets(sPhrase, STRMAX - 1, stdin);
    sReverse = reverse(sPhrase);
    printf("Reversed string: %s\n", sReverse);
    return 0;
}

char *reverse(const char* sPhrase) {
    char* sOutput;
    int iCnt = 0, iCntRev;
    sOutput = (char *)malloc((STRMAX * sizeof(char)) + 1);
    if (sOutput){
        for (iCntRev = strlen(sPhrase) - 1; iCntRev >= 0; iCntRev--) {
            *sOutput++ = sPhrase[iCntRev];
            iCnt++;
        }
        *sOutput++ = '\0';
    }
    return (sOutput - iCnt);
}

Вот отличное руководство по указателям.

Надеюсь, это поможет, С наилучшими пожеланиями, Том.

0 голосов
/ 22 января 2010

Тип char * sPhrase [] действительно несовместим с char *, [] имеет семантическую структуру указателя, поэтому вы много кодируете для char **. Поэтому подпись функции должна быть:

char* reverse(char* sPhrase)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...