Как автоматически инициализировать последний элемент в массиве struct? - PullRequest
1 голос
/ 12 июля 2010

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

Теперь, есть ли какой-нибудь способ автоматически инициализировать массив, чтобы в конце он имел дополнительный пустой элемент, поэтому у меня нет шансов забыть его оттуда? Подобно тому, как char [] работает, он добавляет дополнительный ноль в конец IIRC.

Вот мой код, который я сейчас использую:

struct twostrings {
    string s1, s2;
};

twostrings options[] = {
    {"text1", "more text1"},
    {"text2", "more text2"},
    {"text3", "more text3"},
    {""}, // tells that the array ends here
}

int get_len(twostrings opt[]){
    int p = 0;
    while(1){
        if(opt[p].s1 == ""){
            return p;
        }
        p++;
        // now here is a possibility to go in infinite loop if i forgot the empty string.
        // currently i have a code here that checks if p > 10000 and gives error message to me if i manage to forget that empty string in accident.
    }
    return p;
}

void dosomething(twostrings options[]){
    int len = get_len(options);
    for(int p = 0; p < len; p++){
        // do stuff
    }
}

int main(){ // yes its not valid written main function. dont bother about it.
    dosomething(options);
}

Ответы [ 6 ]

6 голосов
/ 12 июля 2010

Обход массивов C не очень идиоматичен в C ++.Попробуйте использовать std::vector вместо:

#include <vector>
#include <string>

struct twostrings {
  std::string s1, s2;
};

typedef std::vector<twostrings> option_type;

twostrings options[] = {
    {"text1", "more text1"},
    {"text2", "more text2"},
    {"text3", "more text3"}
};

int get_len(const option_type& options){
  return options.size();
}

void dosomething(const option_type& options){
    int len = get_len(options);
    for(int p = 0; p < len; p++){
        // do stuff
    }
}


int main() {  // This main function is perfectly fine!
    option_type opt_vector(options, options + (sizeof options / sizeof options[0]));
    dosomething(opt_vector);
}
2 голосов
/ 12 июля 2010

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

char x[] = "ABC"; // size 4, contains A, B, C, \0.
char x[] = {'A','B','C'}; // size 3, contains no terminating null.

Так что короткий ответ - нет, нет способа автоматически завершать массивы с помощью автоматического ввода. Однако есть множество других опций, таких как векторы STL, которые имеют другие средства определения, когда вы достигли конца. В C ++ 0x, вероятно, (IIRC) будет способ инициализировать вектор так, как вам хотелось бы.

НТН.

EDIT
Лично я предпочитаю добавлять лишние 0 в конце массива самостоятельно, но я полагаю, что есть способы обойти это с помощью макросов.

#define ARRAY(...) {__VA_ARGS__, {0}}

и используйте его вот так

struct foo { char* x; char* y; }

struct foo x[] = ARRAY({"abc", "xyz"}, {"def","uvw"});

Я понятия не имею, работает ли это (и у меня нет удобного препроцессора), и, как я уже сказал, лично мне это не нравится. Также требуется, чтобы первый элемент в структуре был чем-то, что может быть присвоено 0, чтобы отметить конец массива.

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

EDIT:
У меня просто была возможность проверить это, и это работает. Оказывается, что макросы variadic до сих пор только C. Однако некоторые (большинство?) Компиляторы C ++ все равно их поддерживают, быстрый поиск обнаружил g ++ и visual studio. Тем не менее, я бы не одобрил такой подход, я просто добавил его для полноты.

1 голос
/ 12 июля 2010

Передавайте длину или конец вместо использования часового:

template<class T, int N>
int len(T (&)[N]) { // exists in a more general form as boost::size
  return N;
}

typedef std::pair<std::string, std::string> twostrings;
// std::pairs have first and second members of the given types

void dosomething(twostrings options[], int size);
// call as: dosomething(array, len(array));

# or:

template<class T, int N>
T* end(T (&a)[N]) { // exists in a more general form as boost::end
  return a + N;
}

void dosomething(twostrings* options_begin, twooptions* options_end);
// call as: dosomething(array, end(array));

// usage example:
void dosomething(twostrings* options_begin, twooptions* options_end) {
  // you might name the parameters just 'begin' and 'end'
  for (; options_begin != options_end; ++options_begin) {
    // the 'begin' var advances through the entire sequence
    // use for (twostrings* current = options_begin; current != options_end; ++current)
    // if a separate copy is required
    cout << options_begin->first << ": " << options_begin->second << '\n';
  }
}

Обратите внимание, что шаблон итератора [начало, конец) (включающий начало, исключительный конец) распространен в stdlib (например, посмотрите на std :: sort из ).

Это хорошая промежуточная мера между массивами и контейнерами, такими как std :: vector, и позволяет вам сохранить простой синтаксис инициализации, который вы используете сейчас (C ++ 0x дает вам тот же синтаксис с контейнерами, как std :: vector , но 0x еще не совсем готов).

0 голосов
/ 12 июля 2010

Кстати, if(opt[p].s1 == "") проверяет 2 const char * указатели на равенство, а не 2 строки. Хотя компилятор обычно оптимизирует одинаковые строковые константы для указания на одно место, это все равно ошибка.

Вы должны использовать NULL sentinell, как это было рекомендовано Svisstack ранее.

редактировать: Доказательство

#include <stdio.h>

const char *one = "the string";
void main(){
    const char *other = "the string";
    printf("adress of 'one' = %x, it contains \"%s\"\n", one, one);
    printf("adress of 'other' = %x, it contains \"%s\"\n", other, other);
    if(one == other){
        printf("one == other\n", one);
    } else {
        printf("one != other\n", one);
    }
}

Выход:

k:\temp>cl test.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
/out:test.exe
test.obj

k:\temp>test.exe
adress of 'one' = 3d8140, it contains "the string"
adress of 'other' = 3d814c, it contains "the string"
one != other
0 голосов
/ 12 июля 2010

Не используйте массивы в стиле C в C ++, они просто не стоят усилий по сравнению с vector.size ().Вы должны использовать boost::array<twostrings, length> для статического массива.

Черт, вы, вероятно, просто не должны использовать статическое значение.

0 голосов
/ 12 июля 2010

Существуют лучшие способы определения длины массива. Вы можете использовать:

 1. sizeof(options) / sizeof(twostrings);

 2. sizeof(options) / sizeof(options[0]);

 3. std::vector<twostrings> options;
    options.size();

 4. ARRAYSIZE(options); (windows only)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...