2013-05-26 4 views
2
const char *SITE_NAME = "test"; 
char SITE_ROOT[19]; 
sprintf (SITE_ROOT, "/var/www/html/%s", SITE_NAME); 

Я не могу понять, почему я получаю сообщение об ошибке из:переменная конкатенации на другую переменную

error: expected ‘)’ before string constant

В основном я просто хочу, чтобы сцепить переменную SITE_NAME на SITE_ROOT. Ошибка находится в строке sprintf. Есть идеи?

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

+0

Какая линия дает вам ошибку? –

+0

@JackManey - Я обновил свой пост ... ошибка находится на линии sprintf. – user983223

+0

Неожиданный макрос может быть запутанным. Проверьте выход препроцессора. ('cc -E file.c' для unix-подобных компиляторов, расскажите, какой компилятор вы используете, если вам нужна дополнительная помощь) –

ответ

2

Как уже отмечалось, прямая проблема заключается в том, что вы пытаетесь вызвать sprintf из вне функции.

Вы упомянули, что вы устанавливаете строки таким образом, потому что вы сами используете SITE_NAME в дополнение к конкатенации с помощью пути, и вы хотите изменить его только в одном месте. Это хорошая цель, известная в некоторых кругах как "don't repeat yourself" (often abbreviated DRY). Однако, даже если код работал (например, потому что вы переместили sprintf вызов в основной), вы на самом деле не достигли своей цели из-за следующей строкой:

char SITE_ROOT[19]; 

Вы объявляете массив фиксированной длины точно большой достаточно, чтобы провести "/var/www/html/test", который просто просит переполнение буфера. Когда вы меняете SITE_NAME от "test" до, например, "someRealSiteName", вы, скорее всего, перепишете что-нибудь еще, когда вы соединитесь, вызывая непредсказуемые результаты. Поэтому вам нужно вручную пересчитать длину финальной строки и обновить размер массива (что было бы легко ошибиться, скажем, забыв добавить 1 для нулевого терминатора) каждый раз, когда вы меняете SITE_NAME.

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

Вместо этого, вы можете динамически изменять размер SITE_ROOT так:

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

const char SITE_PATH[] = "/var/www/html/"; 
const char SITE_NAME[] = "someRealSiteName"; 
char *SITE_ROOT = NULL; // just making this explicit, would be set to 0 anyway 

int main(void) 
{ 
    size_t siteRootLength = strlen(SITE_PATH) + strlen(SITE_NAME); 

    SITE_ROOT = malloc(siteRootLength + 1); //don't forget to account for the terminating '\0' 

    strcpy(SITE_ROOT, SITE_PATH); 
    strcat(SITE_ROOT, SITE_NAME); 

    printf("%s\n", SITE_NAME): 
    printf("%s\n", SITE_PATH): 
    printf("%s\n", SITE_ROOT): 

    return 0; 
} 

Это решение хорошо, но имеет некоторые недостатки:

  • SITE_ROOT не может быть const указатель, так как строка, так и сам указатель могут быть случайно изменены
  • Путь и имя сайта будут храниться в памяти дважды (хотя похоже, что с этим все в порядке)
  • конкатенации делается во время выполнения, когда это может быть сделано во время компиляции
  • код более и более сложными, чем это необходимо для такой простой задачи
  • Риск того, что SITE_ROOT используется, прежде чем он имеет правильное значение (или даже действительный указатель/строка!) в другом коде инициализации или другом потоке.

Я чувствую что-то вроде следующего лучше:

#include <stdio.h> 

#define SITE_PATH_MACRO "/var/www/html/" 
#define SITE_NAME_MACRO "someRealSiteName" 

// the preprocessor will merge the two string literals into one 
#define SITE_ROOT_MACRO SITE_PATH_MACRO SITE_NAME_MACRO 

// you could do without some or all of these if you don't need them 
// (or are willing to use the macros directly) 
const char SITE_PATH[] = SITE_PATH_MACRO; 
const char SITE_NAME[] = SITE_NAME_MACRO; 
const char SITE_ROOT[] = SITE_ROOT_MACRO; 

int main(void) 
{ 
    printf("%s\n", SITE_NAME); 
    printf("%s\n", SITE_PATH); 
    printf("%s\n", SITE_ROOT); 

    return 0; 
} 
+0

большое спасибо. Ваши плюсы и минусы были очень полезными. – user983223

+0

@ user983223 Я рад, что вы сочли это полезным.Я должен указать, что слияние строковых литералов не только работает в сочетании с макросами, но и просто полезно. Таким образом, вы можете сделать 'const char SITE_ROOT [] = SITE_PATH_MACRO SITE_NAME_MACRO', чтобы избавиться от одного макроса или' const char SITE_ROOT [] = "/ var/www/html /" SITE_NAME_MACRO', чтобы избавиться от двух (если вы этого не сделали нужна переменная 'SITE_PATH') – jerry

4

ошибка выглядит она не может быть показано в коде, но sprintf должно быть:

sprintf (SITE_ROOT, "/var/www/html/%s", SITE_NAME); 

EDIT:

Вот мой полный тестовый код, если это помогает на всех:

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

int main() 
{ 
    const char *SITE_NAME = "test"; 
    char SITE_ROOT[19]; 
    sprintf (SITE_ROOT, "/var/www/html/%s", SITE_NAME); 

    printf("%s\n", SITE_ROOT) ; 

    return 0 ; 
} 
+0

Я снял амперсанд перед SITE_NAME и все еще выдавал ошибку. Я добавил амперсанд, потому что я думал, что SITE_NAME определяется как указатель и для вызова этого указателя мне нужно поставить амперсанд, нет? – user983223

+0

@ user983223 Странно, я не могу воспроизвести эту ошибку вообще. Спецификатор формата '% s' ожидает строку стиля c, которая является символом' char * ', что и является' SITE_NAME'. Я обновлю свой пост с полным кодом проверки, если это поможет. –

+0

@ user983223 Затем отправьте сообщение по адресу [SSCCE] (http://sscce.org). – jerry

1

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

strcpy(SITE_ROOT, "/var/www/html/"); 
strcat(SITE_ROOT, SITE_NAME); 
+1

Если вы собираетесь хранить 'SITE_ROOT' как буфер фиксированной длины (который, кажется, вы подразумеваете), вы можете напрямую инициализировать его с помощью' char SITE_ROOT [19] = "/ var/www/html /" 'и пропустите вызов 'strcpy'. – jerry

Смежные вопросы