开发者

How to initialize last item automatically in a struct array?

I am passing an array to a function, and i am initializing it globally with some values. I am using empty string in end of array to determine the array length.

Now, Is there some way to automatically initialize the array to have extra empty item in the end of it, so i have no chances to forget it from there? Just like the char[] works, it adds extra null to the end IIRC.

Here is my code what im using now:

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 开发者_JS百科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);
}


Passing around C arrays is not very idiomatic in C++. Try using a std::vector instead:

#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);
}


Unforunately, you're not correct. The char array does not end automatically in a null, this is only a side effect of assigning it with a string literal (which has the automatic null at the end).

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

So the short answer is no, there's no way to automatically end arrays with an automatic entry. There are a bunch of other options though, such as STL vectors which have other means of determining when you've reached the end. In C++0x there'll probably (IIRC) be a way to initialize the vector just like you'd like.

HTH.

EDIT:
Personally, I prefer to add the extra 0 at the end of the array myself, but I suppose there are ways to work around it using macros.

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

and use it like so

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

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

I have no idea if this works (and I have no preprocessor handy), and as I said, personally I don't like it. It also requires the first element in the struct to be something which can be assigned 0 to mark the end of the array.

Of course, this forces you to remember to wrap it in the macro call, which is pretty much as bad as forcing you to remember to terminate the array.

EDIT:
I just had a chance to test this and it works. Turns out variadic macros are, so far anyway, C only. However some (most?) C++ compilers support them anyway, a quick search turned up g++ and visual studio. Still I wouldn't favor this approach, I just added it for completeness.


Pass the length or the end instead of using a sentinel:

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';
  }
}

Note the [begin, end) iterator pattern (that's inclusive begin, exclusive end) is common in the stdlib (e.g. look at std::sort from <algorithm>).

This is a good halfway measure between arrays and containers such as std::vector, and allows you to keep the easy initialization syntax you have now (C++0x gives you that same syntax with containers such as std::vector, but 0x is not quite yet ready).


Don't use C style arrays in C++, they're just not worth the effort compared to vector.size(). You should use a boost::array<twostrings, length> for a static array.

Hell, you should probably just not use a static value.


There are better ways of finding array lengths. You can use:

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

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

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

 4. ARRAYSIZE(options); (windows only)


Btw, if(opt[p].s1 == "") is checking 2 const char * pointers for equality, not 2 strings. Although compiller usualy optimizes equal string constants to point to one place, it is still an error.

You should use a NULL sentinell as it was adviced by Svisstack earlier.

edit: Proof

#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);
    }
}

Output:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜