使用for_each迭代NULL终止的字符串数组

时间:2010-10-27 12:20:35

标签: c++

使用for_each迭代NULL终止的字符串是可能的:

const char *name = "Bob";

void func(const char &arg)
{
   cout << arg;
}

int main()
{
    for_each(name, name + strlen(name), func);
}

类似于NULL终止的字符串列表(不必首先确定列表的总长度),例如:

const char *names[] = { "Bob", "Adam", "Simon", NULL };

10 个答案:

答案 0 :(得分:8)

std :: for_each在一个范围内“迭代”,因此要将它与不确定长度的数组一起使用,您需要使用自定义迭代器来指示数组的结尾(在NULL成员上)。如果你坚持使用以NULL结尾的char *数组,你当然可以为它创建自己的for_each函数,例如:

template <typename Function>
void for_each_in_null_terminated_cstring_array(const char** array, Function f)
{
    while (*array) {
        f(*array);
        array++;
    }
}

const char *names[] = { "Bob", "Adam", "Simon", NULL };
for_each_in_null_terminated_cstring_array(names, func);

但我并不是真的推荐这个解决方案。

编辑:是的,更通用的总是更多更好,不是吗?

template <typename T, typename Function>
void for_each_in_null_terminated_array(T* array, Function f)
{
    while (*array) {
        f(*array);
        array++;
    }
}

(这里是我之前提到的null终止(“false”-terminated)迭代器的实现 - 根据以下建议进行了一两次更改。它应该是一个真正的InputIterator)

template <class T>
class nt_iterator: public std::iterator<std::input_iterator_tag, T>
{
public:
    typedef typename nt_iterator<T>::pointer pointer;
    typedef typename nt_iterator<T>::value_type value_type;

    nt_iterator(): p(), pte(true) {}
    nt_iterator(pointer p_): p(p_), pte(!p_) {}
    nt_iterator(const nt_iterator<T>& rhs): p(rhs.p), pte(rhs.pte) {}
    nt_iterator<T>& operator++() {
        ++p;
        if (!*p) pte = true; // once past-the-end, always past-the-end
        return *this;
    }
    nt_iterator<T> operator++(int) {
        nt_iterator n(*this);
        operator++();
        return n;
    }
    bool operator==(const nt_iterator<T>& rhs) {
        return pte && rhs.pte || p == rhs.p;
    }
    bool operator!=(const nt_iterator<T>& rhs) {
        return !(operator==(rhs));
    }
    value_type operator*() { return *p; }

private:
    pointer p;
    bool pte; // past-the-end flag
};

它是如何使用的:

void print(const char* str);

int main()
{
    const char* array[] = {"One", "Two", "Three", NULL, "Will you see this?"};
    std::for_each(nt_iterator<const char*>(array),
                  nt_iterator<const char*>(),
                  print);
}

它可能比循环版本慢一些,因为等价检查的数量增加 - 与例如打印文本相比,速度差异当然是微不足道的 - 但是应该注意std::for_each做< em> not 神奇地使循环更快(实际上,您可能会惊讶地看到您的编译器供应商如何定义函数 - 也就是说,如果您期望太多)。

答案 1 :(得分:3)

const char *names[] = { "Bob", "Adam", "Simon" };

你可以打电话

std::for_each(names, names + sizeof(names)/sizeof(names[0]), func );

或更好,使用two helper functions

std::for_each(begin(names), end(names), func );

当然,这在数组衰变成指针的时刻失败了(但至少编译器不接受它)。如果必须依赖于尾随NULL,您需要编写自己的循环函数或事先计算,与std::strlen()一样:

std::ptr_diff_t num = std::find( names
                               , names + std::numeric_limits<std::size_t>::max()
                               , NULL);
std::for_Each( names, names+num, func );

答案 2 :(得分:3)

扩展Basilevs,回答一个完全有效的解决方案。

自定义迭代器可以定义如下:

template <class T>
class NullTerminatedIterator
    :public std::iterator<std::forward_iterator_tag,
    T,ptrdiff_t,const T*,const T&>
{
public:
    typedef NullTerminatedIterator<T> NTI;

    NullTerminatedIterator(T * start): current(start) {}
    NTI & operator++() {current++; return *this;}
    T & operator*() { return *current; } 
    static NTI end() { return NTI(0); }
    bool operator==(const NTI & that) { return *current == *that.current; }
    bool operator!=(const NTI & that) { return *current != *that.current; }
private:
    T * current;
};

然后像这样使用:

const char *names[] = {"Bob", "Adam", "Simon", NULL};

NullTerminatedIterator<char*> iter((char**)names);

for_each(iter, NullTerminatedIterator<char*>::end(), func);

NullTerminatedIterator的基类取自此custom iterator个问题。

这仅在for_each调用期间根据请求遍历列表。

答案 3 :(得分:1)

有多个答案可以告诉您可以做什么。然而,你的特定问题的答案只是“不,你不能”:)

答案 4 :(得分:0)

您可以将sizeof()用于编译时大小的数组。

const char *names[] = { "Bob", "Adam", "Simon" };
std::for_each(names, names + sizeof(names)/sizeof(*names), [](const char* arg) {
    std::cout << arg << "\n";
});
std::cin.get();

对于动态大小的数组,您应该使用std::vector<std::string>并迭代它。

请原谅我使用lambdas,你的编译器(可能)不支持它们。

答案 5 :(得分:0)

将它们添加到容器中,并使用for_each迭代它们 我在我的例子中使用了一个向量:

void function(string name)
{
    cout << name;
}

int main()
{
    vector<string> nameVector;

    nameVector.push_back("Bob");
    nameVector.push_back("Adam");
    nameVector.push_back("Simon");

    for_each(nameVector.begin(), nameVector.end(), function);

    return 0;
}

答案 6 :(得分:0)

为了达到你想要的效果,你能不能通过引用指向const char的指针替换传递给func的参数。有点像这样:

const char *names[] = { "Bob", "Adam", "Simon" };

void func( const char* &arg )
{
   cout << arg << endl;
}

int main()
{
    for_each( names, 
              names + sizeof( names ) / sizeof( names[ 0 ] ), 
              func );
}

显然对于以NULL结尾的字符串数组,只需从数组大小中减去1 ...

答案 7 :(得分:0)

template <class T>
struct NullTerminatedIterator {
  typedef NullTerminatedIterator<T> NTI;
  T * current;
  NTI & operator++() {current++; return this;}
  T & operator*() {return *current;} 
  NullTerminatedIterator(T * start): current(start) {}
  static NTI end() {return  NTI(0);}
  bool operator==(const NTI & that) {return current==that.current;}

}

答案 8 :(得分:0)

我知道这不是for_each,但我想使用旧的常规for循环来做同样的事情。这个来自MSDN blog

  

重新解释一个双重空终止的字符串,实际上是一个带有空字符串作为终结符的字符串列表,这使得编写代码可以非常简单地遍历一个以双空终止的字符串:

for (LPTSTR pszz = pszzStart; *pszz; pszz += lstrlen(pszz) + 1) {
   // ... do something with pszz ...
}

对我来说看起来很干净!

答案 9 :(得分:0)

    // C version 
    const char* vars[16]={"$USER","$HOME","$DISPLAY","$PASSWORD",0};

    for(const char** pc = vars; *pc!=0; pc++)
    {
            printf("%s",*pc);
    }