Как мне вернуть строку из функции, которая предназначена для использования в printf в C? - PullRequest
2 голосов
/ 02 апреля 2019

Моя функция должна вернуть строку, и она строит ее с помощью индексации.В идеале я хотел бы использовать это возвращаемое значение в printf ().Я понимаю, что, учитывая мой тип возвращаемого значения, мне нужно возвращать указатель на строку символов, но я не могу сделать регулярный указатель на строку, потому что мне нужен доступ к отдельным элементам.Кроме того, я знаю, что мне понадобится malloc (), но я не знаю, как освободить эту память, если она используется в качестве аргумента для printf.

Вот мой код:

char * printRegister(int num) {
    char reg[] = "$error";
    memset(reg, 0, sizeof(reg));
    switch(num)
    {
        case 0:
            strcpy(reg, "$zero");
            break;
        case 1:
            strcpy(reg, "$at");
            break;
        case 2:
        case 3:
            reg[0] = '$';
            reg[1] = 'v';
            reg[2] = num - 2 + '0';
            reg[3] = '\0';
            break;
        case 4:
        case 5:
        case 6:
        case 7:
            reg[0] = '$';
            reg[1] = 'a';
            reg[2] = num - 4 + '0';
            reg[3] = '\0';
            break;
        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:
        case 15:
            reg[0] = '$';
            reg[1] = 't';
            reg[2] = num - 8 + '0';
            reg[3] = '\0';
            break;
        case 16:
        case 17:
        case 18:
        case 19:
        case 20:
        case 21:
        case 22:
        case 23:
            reg[0] = '$';
            reg[1] = 's';
            reg[2] = num - 16 + '0';
            reg[3] = '\0';
            break;
        case 24:
        case 25:
            reg[0] = '$';
            reg[1] = 't';
            reg[2] = num - 16 + '0';
            reg[3] = '\0';
            break;
        case 26:
        case 27:
            reg[0] = '$';
            reg[1] = 'k';
            reg[2] = num - 26 + '0';
            reg[3] = '\0';
            break;
        case 28:
            strcpy(reg, "$gp");
            break;
        case 29:
            strcpy(reg, "$sp");
            break;
        case 30:
            strcpy(reg, "$fp");
            break;
        case 31:
            strcpy(reg, "$ra");
            break;
        default:
            strcpy(reg, "error");
            break;
    }
    return reg;
}

Есть ли способ сделать это?

Ответы [ 4 ]

2 голосов
/ 02 апреля 2019

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

Существует несколько способов решения этой проблемы:

  • Вы можете указать reg static класс хранения. Вернуть указатель на reg было бы нормально, но содержимое будет перезаписано последующими вызовами на printRegister, поэтому вы не можете использовать printRegister несколько раз в качестве аргументов для одного printf вызова.

  • вы можете вернуть выделенную копию reg, но вызывающей стороне потребуется освободить этот блок памяти, чтобы избежать утечек памяти. Это делает его громоздким для вашей цели.

  • Вы можете изменить прототип на printRegister, чтобы взять указатель на массив назначения и вернуть его после сохранения текстового представления имени регистра. Обратите внимание, что вам понадобятся отдельные массивы для нескольких вызовов на printRegister в одном вызове printf.

  • , поскольку существует только 33 возможных строки, вы можете вернуть строковые константы. Это простое и эффективное решение, но рекомендуется изменить тип возвращаемого значения на const char *.

Вот реализация последнего подхода:

const char *printRegister(int num) {
    static const char * const regname[] = {
        "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
        "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
        "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
        "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
    };
    return (num >= 0 && num < 32) ? regname[num] : "error";
}
2 голосов
/ 02 апреля 2019

Как мне вернуть строку из функции, которая предназначена для использования в printf в C?

В C99 или новее используйте составной литерал для временного хранения строк.

Сначала перепишите printRegister(), чтобы принять буфер char *.

#define  printRegister_N 7
// char * printRegister(int num) {
char * printRegister(char *reg, int num) {
  // char reg[] = "$error";
  // memset(reg, 0, sizeof(reg));
  memset(reg, 0, printRegister_N);  // not really needed anymore with compound literal
  ...
  return reg;
}

Сформируйте макрос, который вызывает printRegister() с составным литералом .

//                                   v---------------------------v  compound literal
#define PRINT_REG(num) printRegister((char [printRegister_N]){ 0 }, (num))

Тогда позвоните PRINT_REG(num). Возвращенный char* (который является указателем на составной литерал ) действителен до конца блока. Нет *alloc(), free() необходимо.

printf("1st:%s  2nd:%s\n", PRINT_REG(0), PRINT_REG(1));

Код все еще использует обычные украшения для "%s", такие как "%-7s", "%.*s" и т. Д.

0 голосов
/ 02 апреля 2019

Объявляя вспомогательную функцию, которая принимает ваш строковый аргумент

void myprint(char *str) {
    printf("rs: %s\n", str);
}

Вы можете изменить реализацию printRegister() на

void printRegister(int num, void (*fn)(char*)) {
    ...
    fn(reg); // instead of return reg;
}

Затем вызвать

printRegister(rs, myprint);

Это позволяет обойтись без динамически выделяемой памяти путем изменения ролей вызывающего и вызываемого абонентов;вызывающий printRegister() теперь предоставляет callback , который может принять переменную временного стека и что-то с ней сделать.

Попробуйте это онлайн!

0 голосов
/ 02 апреля 2019

Попробуйте strdup (), он выделит вам память и скопирует в нее любую строку, которую вы хотите.

также любой блок памяти, который был динамически выделен , может быть освобожден с помощьюfree ().

Вот пример:

#include <string.h> //note: don't include "string", instead include string.h 
#include <stdlib.h> //note: this header defines free,

int main()
{
    char *data = strdup("the return value of strdup is will be a string containing this");

    /* use dynamically allocated string... */



    free(data); /* free up the memory. */
}
...