【问题标题】:C++ - strcmp() does not work correctly?C++ - strcmp() 不能正常工作?
【发布时间】:2010-12-29 21:30:25
【问题描述】:

发生了一些非常奇怪的事情:虽然两个字符串完全相同,但 strcmp() 返回 -1。这是调试器 (gdb) 输出的 sn-p:

(gdb) print s[i][0] == grammar->symbols_from_int[107][0]
$36 = true
(gdb) print s[i][1] == grammar->symbols_from_int[107][1]
$37 = true
(gdb) print s[i][2] == grammar->symbols_from_int[107][2]
$38 = true
(gdb) print s[i][3] == grammar->symbols_from_int[107][3]
$39 = true
(gdb) print s[i][4] == grammar->symbols_from_int[107][4]
$40 = true
(gdb) print s[i][5] == grammar->symbols_from_int[107][5]
$41 = false
(gdb) print grammar->symbols_from_int[107][4]
$42 = 0 '\0'
(gdb) print s[i]
$43 = (char * const&) @0x202dc50: 0x202d730 "Does"
(gdb) print grammar->symbols_from_int[107]
$44 = (char * const&) @0x1c9fb08: 0x1c9a062 "Does"
(gdb) print strcmp(s[i],grammar->symbols_from_int[107])
$45 = -1

知道发生了什么吗?

提前致谢,

奥努尔

编辑 1: 以下是我的代码的一些 sn-ps:

# include <unordered_map>       // Used as hash table
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <vector>

using namespace std;
using std::unordered_map;
using std::hash;

struct eqstr
{
  bool operator()(const char* s1, const char* s2) const
  {
    return strcmp(s1, s2) == 0;
  }
};

...
<some other code>
...

class BPCFG {

  public:

        char *symbols;  // Character array holding all grammar symbols, with NULL seperating them.
        char *rules;    // Character array holding all rules, with NULL seperating them.

        unordered_map<char *, int , hash<char *> , eqstr> int_from_symbols; // Hash table holding the grammar symbols and their integer indices as key/value pairs.
...
<some other code>
...

vector<char *> symbols_from_int;        // Hash table holding the integer indices and their corresponding grammar symbols as key/value pairs.
void load_symbols_from_file(const char *symbols_file);
}

void BPCFG::load_symbols_from_file(const char *symbols_file) {
        char buffer[200];
        FILE *input = fopen(symbols_file, "r");
        int symbol_index = 0;
        while(fscanf(input, "%s", buffer) > 0) {
                if(buffer[0] == '/')
                        strcpy(symbols + symbol_index, buffer+1);
                else
                        strcpy(symbols + symbol_index, buffer);
                symbols_from_int.push_back(symbols + symbol_index);
                int_from_symbols[symbols+symbol_index] = symbols_from_int.size()-1;
                probs.push_back(vector<double>());
                hyperprobs.push_back(vector<double>());
                rules_from_IntPair.push_back(vector<char *>());
                symbol_index += strlen(symbols+symbol_index) + 1;
        }


        fclose(input);
}

最后一个函数 (BPCFG::load_symbols_from_file) 似乎是我在整个代码中修改 symbols_from_int 的唯一函数。如果您需要更多代码,请告诉我。我没有把所有东西都放上去,因为它有数百行。

编辑 2: 好的,我想我应该从我的代码中再添加一件事。这是BPCFG类的构造函数:

BPCFG(int symbols_length, int rules_length, int symbol_count, int rule_count):
   int_from_symbols(1.5*symbol_count),
   IntPair_from_rules(1.5*rule_count),
   symbol_after_dot(10*rule_count)
{
    symbols = (char *)malloc(symbols_length*sizeof(char));
    rules = (char *)malloc(rules_length*sizeof(char));
}

编辑 3: 这是通往错误点的路径上的代码。它不可编译,但它显示了代码的单步执行位置(我在调试器中使用 next 和 step 命令检查了代码确实遵循这条路线):

BPCFG my_grammar(2000, 5500, 194, 187);
my_grammar.load_symbols_from_file("random_50_1_words_symbols.txt");
<some irrelevant code>
my_grammar.load_rules_from_file("random_50_1_words_grammar.txt", true);
<some irrelevant code>
my_grammar.load_symbols_after_dots();

BPCFGParser my_parser(&my_grammar);
BPCFGParser::Sentence s;

// (Sentence is defined in the BPCFGParser class with
// typedef vector<char *> Sentence;)

Edge e;
try {
        my_parser.parse(s, e);
}
catch(char *e) {fprintf(stderr, "%s", e);}

void BPCFGParser::parse(const Sentence & s, Edge & goal_edge) {

        /* Initializing the chart */

        chart::active_sets.clear();
        chart::passive_sets.clear();
        chart::active_sets.resize(s.size());
        chart::passive_sets.resize(s.size());

        // initialize(sentence, goal);

        try {
                initialize(s, goal_edge);
        }
        catch (char *e) {
                if(strcmp(e, UNKNOWN_WORD) == 0)
                        throw e;
        }
<Does something more, but the execution does not come to this point>
}

void BPCFGParser::initialize(const Sentence & s, Edge & goal_edge) {
        // create a new chart and new agendas
        /* For now, we plan to do this during constructing the BPCFGParser object */

        // for each word w:[start,end] in the sentence
        //   discoverEdge(w:[start,end])

        Edge temp_edge;

        for(int i = 0;i < s.size();i++) {
                temp_edge.span.start = i;
                temp_edge.span.end = i+1;
                temp_edge.isActive = false;
                /* Checking whether the given word is ever seen in the training corpus */
                unordered_map<char *, int , hash<char *> , eqstr>::const_iterator it = grammar->int_from_symbols.find(s[i]);
                if(it == grammar->int_from_symbols.end())
                        throw UNKNOWN_WORD;
                <Does something more, but execution does not come to this point>
        }
}

我在调试器中运行打印命令的地方是最后一个

throw UNKNOWN_WORD;

命令。我的意思是,我在 GDB 上使用 next 并在看到这一行之后,我运行了所有这些打印命令。

感谢您的关注,
奥努尔


好的,我想我应该从我的代码中再添加一件事。这是BPCFG类的构造函数:

BPCFG(int symbols_length, int rules_length, int symbol_count, int rule_count):
   int_from_symbols(1.5*symbol_count),
   IntPair_from_rules(1.5*rule_count),
   symbol_after_dot(10*rule_count)
{
    symbols = (char *)malloc(symbols_length*sizeof(char));
    rules = (char *)malloc(rules_length*sizeof(char));
}

【问题讨论】:

  • 我的猜测是这两个字符串实际上并不相同。我总是通过意识到这总是我没有系统的事情来解决编程中的问题。
  • 你能分享一个展示问题的最小可编译示例吗?
  • 发布代码,而不是 gdb 输出。
  • 您调用 strcmp 可能使用了错误的指针,发布代码。
  • 请更新原始问题而不是发布“答案”,stackoverflow 不像论坛那样工作,而且它变得非常难以理解。也就是说,由于您似乎在 linux(或类似 linux 的平台)下运行,请尝试使用 valgrind 运行它,因为它可以隔离许多与内存损坏相关的问题。

标签: c++ strcmp


【解决方案1】:

这听起来像s 是一个指向堆栈上的数组的指针,一旦调用新函数就会被覆盖,即strcmp()

strcmp() 调用之后,调试器说它们是什么?

【讨论】:

  • +1。是的。数据损坏的可能性比 strcmp 中的错误高一百万倍!当您使用调试器检查它们时,这些字符串显然是相同的,但这并不意味着在 strcmp 执行时内存位置仍然具有这些值。 (@Onur:发布失败的实际代码将使一切变得更加清晰)。
  • s 是 vector 类型,它的引用是通过使用 const 关键字的函数调用传递的(因此编译器检查它是否正在尝试修改)并且根据调试器,它确实没变。当我在调用 strcmp() 前后键入“print s”时,我得到完全相同的输出。
  • 最好的做法是发布代码 - 如果它没有做您认为它应该做的事情,那么描述您认为它应该做的事情不会帮助我们诊断它。
  • 这不是答案,因为自定义 strcmp 实现在同一代码点工作。关于响应和 cmets 的情况有很多澄清,没有被质疑,在这里寻找评论:stackoverflow.com/questions/1959572/…
【解决方案2】:

在最近的 Linux 发行版中,strcmp 是STT_GNU_IFUNC 类型的符号。 GDB 的最新版本(撰写本文时为 7.2)不支持此功能。这可能是您的问题的原因,尽管在您的情况下返回值看起来是真实的。

【讨论】:

    【解决方案3】:

    鉴于 GDB 输出,我能看到的唯一可能原因是 strcmp() 被窃听。

    你基本上在 GDB 中做了 strcmp 所做的事情:比较每个字符的字符,直到两者都为零(在 4 处)。

    你可以试试print strcmp("Does", "Does"); 吗?


    编辑:也可以试试:

    print stricmp(s[i], grammar->symbols_from_int[107], 1);
    print stricmp(s[i], grammar->symbols_from_int[107], 2);
    print stricmp(s[i], grammar->symbols_from_int[107], 3);
    print stricmp(s[i], grammar->symbols_from_int[107], 4);
    print stricmp(s[i], grammar->symbols_from_int[107], 5);
    

    【讨论】:

    • strcmp 在野外已经存在很长时间了。提问者所做的事情与他/她的期望不符的可能性要大得多。
    • 一个非常常用的库中的错误,尤其是做这种微不足道的事情,是非常不可能的。除非他从标准库以外的地方获取 strcmp。
    • @duffymo:我在想他可能使用了他自己写的strcmp(),或者无论如何不是标准库中的那个。如果您有其他解释,欢迎提供,但鉴于 GDB 输出,strcmp() 的行为似乎不正常。
    • print strcmp("Does", "Does") 返回 0。我包含了 stdlib.h 和 string.h,并使用选项 -std=gnu++0x 进行编译。我对 strcmp() 的唯一期望是,只要给定的字符串相等,它就会返回 0,否则返回其他值。
    • 澄清一下,我没有写我的 strcmp()。我正在使用 string.h 中的那个
    【解决方案4】:

    解决这个问题的唯一方法是使用调试器进入 strcmp。

    【讨论】:

      【解决方案5】:

      我强烈建议您在开始使用之前将内存清零。我意识到 GDB 输出没有意义,因为您确实验证了它是一个以空字符结尾的字符串,但是我遇到了很多 string.h 奇怪的问题,使用 memset、bzero、calloc 或任何您想使用的东西都会消失。

      具体来说,将构造函数中的内存和从文件读取时使用的缓冲区清零。

      【讨论】:

        【解决方案6】:

        正如其他人所指出的,strcmp 几乎不可能出现问题。

        最好将有问题的代码精简到重现问题所需的绝对最低限度(还包括有关如何编译的说明 - 您使用的是哪个编译器、标志、运行时库?)。您很可能在此过程中发现错误。

        如果没有,您将因在最常用的 C 函数之一中发现错误而获得很多赞誉;-)

        【讨论】:

        • 正如我在我的一个自我回复中所写:我编写的字符串比较函数在 strcmp() 失败的同一点正常工作(发现字符串实际上是相同的)(相信它或不 :) )。我编译:g++ -std=gnu++0x -o Experiment Experiments.cpp -g(调试器的最后一个)我在 Linux Ubuntu 9.04,g++ 4.3.3 上工作,你还想要什么其他类型的信息?我已经剥离了我认为相关的代码部分。
        【解决方案7】:

        可能是两个字符串的大小不一样。

        【讨论】:

        • 不,他表明两个字符串在元素 4 处都是空终止的。
        【解决方案8】:

        strlen(s[i])strlen(grammar-&gt;symbols_from_int[107]) 返回相同吗?

        另外,我无法想象这是问题所在,但您可以使用常量值而不是 i 来确保不会发生奇怪的事情吗?

        【讨论】:

        • 在我检查 strcmp() 时 i 的值为 0。 "print s[i]" 和 "print s[0]" 由调试器返回完全相同的输出。
        【解决方案9】:

        我强烈建议您先尝试使用常规 STL 字符串解决问题 - 您将获得更简洁的代码和自动内存管理,因此您可以专注于解析器逻辑。只有在事情正常并且分析证明字符串操作是性能瓶颈之后,我才会更详细地查看所有使用的算法,然后是专门的字符串分配器,并且 - 作为最后的手段 - 在手动字符数组操作,按此顺序。

        【讨论】:

        • -1,总有一个人插入他最喜欢的库/编码风格,而忽略了手头的问题。希望我能给超过-1..
        • @Blindy:STL 不仅仅是一个喜欢的库。 Nikolai 提倡一种避免 strcmp 问题的方法,这是对问题的一种回答。有时最好的解决方案是采取不同的路线。
        【解决方案10】:

        感谢大家抽空回答。我编写了自己的字符串比较函数,它在同一点上正常工作,所以显然这是 strcmp() 的问题。尽管如此,我的代码仍然无法按预期工作。但只有在我彻底分析之后,我才会请求帮助。谢谢。

        【讨论】:

          【解决方案11】:

          将你自己的 strcmp 实现标记为内联,看看会发生什么......

          来自GCC 4.3 Release Series Changes, New Features, and Fixes GCC 4.3.4:

          “在反馈导向优化期间,会发现 memcpy、memset 和 bzero 函数所操作的预期块大小,并且对于常用的小尺寸情况,会生成专门的内联代码。”

          可能存在其他一些相关的错误...
          尝试关闭编译器优化或/和函数内联。

          【讨论】:

            【解决方案12】:

            也许其中一个字符串中有不可打印的字符?

            【讨论】:

            • 不,请仔细检查 GDB 输出。
            • 是的,我看到了 gdb 输出。但这并不意味着失败不是数据损坏的结果。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-06-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-05
            • 1970-01-01
            相关资源
            最近更新 更多