C: добавить литерал ко всем строкам в массиве, сохраняя доступ к исходным строкам - PullRequest
0 голосов
/ 26 апреля 2019

Предположим, у вас есть массив строк:

const char  str1[] = "some/path";
const char  str2[] = "another/path";
const char  str3[] = "and/a/third/path";
const char* strs[3];
strs[0] = str1;
strs[1] = str2;
strs[2] = str3;

Сохраняя исходные определения строк (т. Е. str1, str2 и str3), я хотел бы иметь возможность изменять пути в массиве одним символом в управление директивой препроцессора. Простейший способ, который я нашел для этого, - это удалить квалификатор const, добавить лишний нулевой байт и сдвинуть каждый элемент вправо.

char  str1[] = "some/path\0";
char  str2[] = "another/path\0";
char  str3[] = "and/a/third/path\0";
char* strs[3];
strs[0] = str1;
strs[1] = str2;
strs[2] = str3;
#ifdef build_option
for(int i=0; i<3; i++) {
    for(int j=strlen(strs[i]); j>=0; j--) {
        strs[i][j] = strs[i][j-1];
    }   
    strs[i][0] = '.'; // prepend each path with this character
}   
#endif

fprintf(stdout, "%s\n%s\n%s\n", strs[0], strs[1], strs[2]);

Выход:

.some/path
.another/path
.and/a/third/path

Это работает достаточно хорошо, но мне интересно, есть ли более простой или более идиоматический способ сделать то же самое, предпочтительно статически (хотя это не обязательно).

редактирует:

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

Спасибо всем за множество замечательных предложений; их комбинация использовалась для получения следующего:

#include <stdio.h>

//#define build_option  // rare compilation route

#ifdef build_option
    #define STR_OFFSET  0
#else
    #define STR_OFFSET  1
#endif

int
main(int argc, char *argv[])
{
    const char  str1[] = ".some/path";
    const char  str2[] = ".another/path";
    const char  str3[] = ".and/a/third/path";
    const char* strs[3] = { str1,
                            str2,
                            str3 };

    for(int i=0; i<3; i++)
        fprintf(stdout, "%s\n",strs[i]+STR_OFFSET);

    #ifdef build_option
    fprintf(stdout, "\noriginal:\n");
    for(int i=0; i<3; i++)
        fprintf(stdout, "%s\n",strs[i]+1);
    #endif

    return 0;
}

Без определения build_option это дает

some/path
another/path
and/a/third/path

С определением build_option это дает

.some/path
.another/path
.and/a/third/path

original:
some/path
another/path
and/a/third/path

Ответы [ 5 ]

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

Время компиляции:

#include <stdio.h>

#ifdef build_option
  #define PATH(str) "."str
#else
  #define PATH(str) str
#endif

const char  str1[] = PATH("some/path");
const char  str2[] = PATH("another/path");
const char  str3[] = PATH("and/a/third/path");
const char* strs[3] = { str1, str2, str3 };

int main()
{
  for(int i = 0; i < 3; ++i)
  {
    printf("%s\n", strs[i]);
  }
  return 0;
}

Как это работает? Ну, вам просто нужно знать, что вы можете естественным образом объединять строковые литералы. Либо Google, либо посмотрите этот поток stackoverflow . Все остальное - нормальный макрос. Нет недостатков во время выполнения, нет выделения памяти, нет копирования.

1 голос
/ 26 апреля 2019

Вот возможное статическое решение

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

static char *strsrc[] = {
        "some/path",
        "another/path",
        "and/a/third/path",
};
static char strdst[3][BUFSIZ];

int main(void)
{
        for (int i = 0; i < 3; i++) {
                strncpy(&strdst[i][1], strsrc[i], BUFSIZ); 
                strdst[i][0] = '.';
                puts(strdst[i]);
        }
        return 0;
}

Я просто скопировал строку в адресную строку и затем переписал первый символ. Константы уже завершены нулем, поэтому нет нужды их '\ 0'.

С уважением,

1 голос
/ 26 апреля 2019

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

#ifdef build_option
  #define TK1 "."
#else
  #define TK1 ""
#endif

void main(void) {
  const char  str1[] = TK1"some/path";
  const char  str2[] = TK1"another/path";
  const char  str3[] = TK1"and/a/third/path";
  const char* strs[3];
  strs[0] = str1;
  strs[1] = str2;
  strs[2] = str3;
 }
1 голос
/ 26 апреля 2019

Нет необходимости в отдельных массивах, если вы не хотите обращаться к ним отдельно по имени:

#if defined build_option
    #define MyPrefix "."
#else
    #define MyPrefix
#endif

const char *strs[] =
{
    MyPrefix "some/path",
    MyPrefix "another/path",
    MyPrefix "and/a/third/path",
};

Смежные строковые литералы объединяются после предварительной обработки и перед основной компиляцией (семантический анализ и перевод).

Вот решение, которое определяет как исходные, так и измененные строки:

#define DefineMyStrings(name, prefix) \
    char *name[] =                    \
    {                                 \
        prefix "some/path",         \
        prefix "another/path",      \
        prefix "and/a/third/path",  \
    };

DefineMyStrings(original,)
DefineMyStrings(prefixed, ".")


#include <stdio.h>


#define NumberOf(a) (sizeof (a) / sizeof *(a))

int main(void)
{
    for (int i = 0; i < NumberOf(original); ++i)
        printf("original[%d] = %s.\n", i, original[i]);

    for (int i = 0; i < NumberOf(prefixed); ++i)
        printf("prefixed[%d] = %s.\n", i, prefixed[i]);
}
1 голос
/ 26 апреля 2019

Используйте malloc() для выделения новых строк и копирования из них литералов.

const char* strs[] = {"some/path", "another/path", "and/a/third/path"};
const int numstrs = sizeof strs / sizeof strs[0];
char *newstrs[numstrs];
for (int i = 0; i < numstrs; i++) {
    newstrs[i] = malloc(strlen(strs[i] + 2)); // +2 for added prefix and null byte
    newstrs[i][0] = '.';
    strcpy(&newstrs[i][1], strs[i]);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...