strncmp正确用法

时间:2010-12-03 02:46:55

标签: c

以下是快速背景:我有一个客户端和一个服务器程序,它们通过Unix套接字相互通信。在服务器端解析收到的消息时,我试图使用strncmp来确定要采取的操作。

我遇到的问题是确切地知道strncmp的长度参数使用了什么。这是有问题的原因是我的一些消息共享一个共同的前缀。例如,我有一条消息“getPrimary”,它使服务器响应主服务器地址和消息“getPrimaryStatus”,这会导致服务器响应主服务器的状态。我最初的想法是做以下事情:

if(strncmp(message,"getPrimary",strlen("getPrimary"))==0){
    return foo;
}
else if(strncmp(message,"getPrimaryStatus",strlen("getPrimaryStatus"))==0){
    return bar;
}

这个问题是当我发送服务器“getPrimaryStatus”时,代码将始终返回foo,因为strncmp在字符串中检查得不够远。我可以将strlen(message)作为strncmp的长度参数传递,但这似乎打败了使用strncmp的目的,即在意外输入的情况下防止溢出。我确实有一个静态变量用于我可以读取的最大消息长度,但它似乎传递了这个,因为长度只是确保如果消息溢出,效果最小化。

我想出了一些解决方案,但它们并不是很漂亮,所以我想知道是否有一种解决这个问题的常用方法。

作为参考,我目前的解决方案是: 以这样的方式命令我的if / else if语句,以便按照长度递减的顺序检查具有公共前缀的任何消息(对于任何试图在以后添加内容的人来说,这似乎是在我的代码中抛出地雷的一种非常好的方法)。

将包含公共前缀的消息分组并首先查找后缀:

if(strncmp(message,"getPrimary",strlen("getPrimary"))==0){
    if(strncmp(message,"getPrimaryStatus",strlen("getPrimaryStatus"))==0){
        return bar;
    else
        return foo;
    }
}

但这只是感觉很乱,特别是因为我正在处理大约20种不同的可能消息。

创建一个包含所有可能消息的数组,向我的init序列添加一个函数,该函数将按降序排列数组,并让我的代码搜索该列表的元素,直到找到匹配为止。这看起来很复杂而且很愚蠢。

看起来这应该是一个足够普遍的问题,应该在某个地方找到解决方案,但到目前为止我还没找到任何东西。

提前感谢您的帮助!

8 个答案:

答案 0 :(得分:4)

假设message中的字符串假定为空终止,那么在此使用strncmp()而不是strcmp()的唯一原因是在message

因此,您传递给message的{​​{1}}应该是n的接收大小,您应该知道(来自strncmp() /的返回值读取消息的message函数。

答案 1 :(得分:2)

一种技术是首先比较最长的名称 - 对测试(或包含关键字的表)进行排序,以便较长的名称在较短的名称之前。但是,举个例子:

GetPrimaryStatus
GetPrimary

您可能希望确保GetPrimaryIgnition无法识别为GetPrimary。所以你真的需要使用两个字符串中较长的字符串的长度进行比较 - 消息或关键字。

您的数据结构可能是:

static const struct
{
    char   *name;
    size_t  name_len;
    int     retval;
} Messages[] =
{
    { "getPrimaryStatus", sizeof("getPrimaryStatus"), CMD_PRIMARYSTATUS },
    { "getPrimary",       sizeof("getPrimary"),       CMD_PRIMARY       },
    ...
};

然后,您可以遍历此表以查找相关命令。有些小心,您可以限制您必须查看的范围。请注意,sizeof()值包括字符串末尾的NUL。如果您可以null终止消息

,这将非常有用

但是,如果您可以通过在某处复制消息或通过原位修改消息来终止消息中的命令字,则最简单。然后,您使用strcmp()代替strncmp()。最短的唯一前缀查找更难编码。

查找命令字的一种合理方法是使用strcspn() - 假设您的命令都是字母或字母数字。

答案 2 :(得分:1)

我感觉你正在使用strncmp来防止缓冲区溢出,但是,消息已经被复制到内存中(即消息缓冲区)。另外,原型

int strncmp ( const char * str1, const char * str2, size_t num );

表示该函数没有副作用(即它不会改变任何输入缓冲区),因此不存在覆盖缓冲区和更改内存的风险。 (strcpy()不是这种情况。)

您可以确保消息缓冲区的长度比最长的命令字符串长。这样你就可以确定你总是访问你拥有的内存。

此外,如果您坚持使用strncmp,您可以将命令列表存储在一个数组中,并将其从最大到最小排序。您可以将每个字符串与一个长度相关联(可能还有一个函数指针来执行处理程序)。

最后,您可以找到C ++调用地图或Ruby或PHP调用关联数组的C版本。这使得库可以高效,正确地为您处理这个if-else树。

答案 3 :(得分:0)

一年前从我的记忆中挖掘C编程,我认为第三个参数应该告诉函数要处理多少个字符进行比较。这就是为什么它是安全的,因为你可以控制要处理的字符数

所以应该是这样的:

if(strncmp(message, "getPrimary", strlen("getPrimary")) {
   //
}

答案 4 :(得分:0)

请勿使用strncmp()。请改用strlcmp()。它更安全。

答案 5 :(得分:0)

您的消息是仅包含其中一个命令,还是包含空格/左括号/等的命令字符串?

如果是前者,请放弃strncmp,然后使用strcmp

如果是后者,只需检查isspace(message[strlen(command)])message[strlen(command)]=='('或类似内容。 (注意:strlen(command)是一个常量,您可能应该这样写,或者使用宏从字符串文字的大小中获取它。)

答案 6 :(得分:0)

使用strncmp确定两个字符串是否相等的唯一安全方法是事先验证字符串是否具有相同的长度:

/* len is a placeholder for whatever variable or function you use to get the length */
if ((len(a) == len(b)) && (strncmp(a, b, len(a)) == 0))
{
    /* Strings are equal */
}

否则,您将匹配比您的比较更长或更短的内容:

strncmp(a, "test", strlen("test"))匹配“测试”,“测试和一大堆其他角色”,等等。

strncmp(a, "test", strlen(a))匹配“”,“t”,“te”,“tes”。

答案 7 :(得分:0)

使用strcmp,还要比较两个字符串的长度。如果长度相同,那么strcmp将为您提供所寻求的结果。