首页 文章

strlen并不总是一个好主意

提问于
浏览
-8

我不确定我是否选择了正确的 Headers ,但今天我发现(作为C语言的初学者)对我来说strlen并不总是在我需要时做出正确的决定 .

所以我尝试了以下内容:

#include<stdio.h>
 #include<string.h>

 int foo(char *s){
    int len = strlen(s);
    /* code here */
    return len;
 }

int main(void){
    char *name = "Michi";
    int len = foo(name);
    int a = 20, b = 10, c = a - b;

    if(c < len){
        printf("True:  C(%d) < Len(%d)\n",c,len);
    }else{
        printf("False:  C(%d) > Len(%d)\n",c,len);
    }

    return 0;
}

输出:

错误:C(10)> Len(5)

但当我用“ -Wconversion ”编译时,我得到:

program.c:5:19:警告:从'size_t'转换为'int'可能会改变其值[-Wconversion]
int len = strlen(s);
^

快速解决方法是施放strlen:

int len = (int)strlen(s);

但我不同意,所以我决定我真的需要别的东西,另一种方法可能呢?我尝试了以下方法:

#include<stdio.h>
#include<string.h>

unsigned int size(char *s){
    unsigned int len;
    /* code here */
    len = (unsigned int)strlen(s);

    return len;
}

int main(void){
    char *name = "Michi";
    unsigned int len = size(name);
    int a = 20, b = 10, c = a - b;

    if(c < (signed int)len){
        printf("True:  C(%d) < Len(%d)\n",c,len);
    }else{
        printf("False:  C(%d) > Len(%d)\n",c,len);
    }

    return 0;
}

但我仍然需要强制转换strlen因为它的返回类型(size_t,我知道这是一个无符号类型( typedef long unsigned int size_t; ))

最后,我决定采用另一种方法,创建自己的函数,这样可以使事情变得更容易,并且将来可能的问题更少,我得到了:

#include<stdio.h>

long int stringLEN(char *s){
    int i = 0;
    long int len = 0;

    while (s[i] != '\0'){
        len++;
        i++;
    }

    return len;
 }

 long int foo(char *s){
    long int len = stringLEN(s);
    /* code here */
    return len;
 }

int main(void){
    char *name = "Michi";
    long int len = foo(name);
    int a = 20, b = 10, c = a - b;

    if(c < len){
        printf("True:  C(%d) < Len(%ld)\n",c,len);
    }else{
        printf("False:  C(%d) > Len(%ld)\n",c,len);
    }

    return 0;
}

那里不再需要演员阵容了 .

所以我的问题是:这是(对于我的情况)更好的方法吗?如果不是我需要一些解释,我的书(我有3个)并没有以这种方式解释我能够理解这些事情 .

我知道只是在某种程度上 cast 可能是个大问题 .

编辑:此代码也不会与 -Wconversion 编译:

#include<stdio.h>
#include<string.h>

 size_t foo(char *s){
    size_t len = strlen(s);
    /* code here */
    return len;
 }

int main(void){
    char *name = "Michi";
    size_t len = foo(name);
    int a = 20, b = 10, c = a - b;

    if(c < len){
        printf("True:  C(%d) < Len(%zu)\n",c,len);
    }else{
        printf("False:  C(%d) > Len(%zu)\n",c,len);
    }

    return 0;
}

输出:

error:有符号和无符号整数表达式之间的比较[-Werror = sign-compare] |

但如果我投了 len 的作品 . 我意识到,如果尺寸大于那么 int 它将永远不适合 .

6 回答

  • 0

    挖掘所有其他答案,你真正的问题似乎是如何处理这样的情况:

    #include <string.h>
    #include <libfoo.h>
    
    extern void foo(void);
    extern void bar(void);
    
    void pick_foo_or_bar(const char *s)
    {
       size_t slen = strlen(s);
       int   value = libfoo_api_returning_an_int();
    
       if (slen > value) // -Wconversion warning on this line
          foo();
       else
          bar();
    }
    

    ...在哪里你不能改变 slenvalue 的类型,因为两者都是正确的API,他们收到的结果 .

    -Wconversion 警告试图告诉你一些有意义的事情 . C中有符号和无符号整数类型的比较做了一些非常奇怪的事情,而不是你对_321776中算术定律的期望;像我上面写的那样天真的比较可以并且已经造成了灾难性的错误 . 但治愈不是铸造或发明自己的 strlen ;治愈是为了确定比较,以便它符合你对算术规律的期望 . 其原则是:

    • 首先检查签名数量是否为负数 . 如果是这样,请将其视为小于无符号数量 .

    • 否则,在比较它们之前,将较小的类型转换为较大的类型 .

    在这种情况下, size_t 几乎肯定比 int 大,或者大小相同,所以你会写

    #include <assert.h>
    #include <limits.h>
    #include <string.h>
    #include <libfoo.h>
    
    extern void foo(void);
    extern void bar(void);
    
    // Code below is correct only if size_t is at least as large as int.
    static_assert(SIZE_MAX >= INT_MAX);
    
    void pick_foo_or_bar(const char *s)
    {
       size_t slen = strlen(s);
       int   value = libfoo_api_returning_an_int();
    
       if (value < 0 || (size_t)value < slen)
          foo();
       else
          bar();
    }
    

    static_assert 存在是因为,如果我没记错的话,C标准不保证 size_t 至少与 unsigned int 一样大 . 例如,我可以想象一下80286的ABI,其中 int 是四个字节宽但 size_t 只有两个 . 在那种情况下,您需要以相反的方式进行投射:

    void pick_foo_or_bar(unsigned short a, long b)
    {
        if (b < 0 || b < (long)a)
            foo();
        else
            bar();
    }
    

    如果您不知道哪些是签名的,那么您在标准C中的唯一办法就是 (u)intmax_t

    void pick_foo_or_bar(uid_t a, gid_t b)
    {
        if (a < 0 && b < 0) {
            if ((intmax_t)a < (intmax_t)b)
                bar();
            else
                foo();
        } else if (a < 0) {
           bar();
        } else if (b < 0) {
            foo();
        } else {
            if ((uintmax_t)a < (uintmax_t)b)
                bar();
            else
                foo();
        }
     }
    

    ......而且,鉴于C99 wrt long 设定了非常不幸的先例,可能会有一天 (u)intmax_t 不是编译器支持的最大整数类型,然后你只是被冲洗了 .

  • 1

    字符串的长度永远不会是负数,而整数可能是 - 警告是因为 size_t 的值范围与 int 不同,如果强制转换为 int ,则 size_t 的某些正值将被视为负数 . 更好的选择是让函数的返回类型匹配,在这种情况下,让 foo 返回一个 size_t - 你很快就会看到数据类型会覆盖大部分代码,并留下一些其他奇怪的东西可以做奇怪的事情( size_t - size_t 可能下流...)

  • -3

    解决此问题的常用方法是使用键入 size_t 的变量,并选择适当的格式进行打印 . 然后不需要演员阵容 . 对于printf,请参阅以下内容:

  • 4

    我认为从编译器到编译器必须有所不同.....因为我在在线编译器上尝试过它并没有显示任何警告 .
    enter image description here

  • 4

    这将在没有警告的情况下编译:

    #include<stdio.h>
     #include<string.h>
    
     size_t foo(char *s){
        size_t len = strlen(s);
        /* code here */
        return len;
     }
    
    int main(void){
        char *name = "Michi";
        size_t len = foo(name);
        size_t a = 20, b = 10, c = a - b;
    
        if(c < len){
            printf("True:  C(%zu) < Len(%zu)\n",c,len);
        } else {
            printf("False:  C(%zu) > Len(%zu)\n",c,len);
        }
    
        return 0;
    }
    

    在@thomasdickey,@ onlandshaw,@ andreaghidini,@ olaf,@ juanchopanza等人的回答和评论中也有所解释 .

    你真的做得更好吗?不:为什么stringlen函数会返回可能为负值的值?没有负大小的字符串 .

    标准的strlen函数已经存在,效率更高,能够处理字符串,其最大大小是stringLEN处理的最大大小的两倍,并且具有更精确的返回类型定义 .

  • 2

    有两个问题:

    • strlen() 返回类型 size_t . size_t 是一些无符号整数类型,可能比 int 宽或宽 . 它取决于编译器/平台 .

    • 代码需要比较和 intsize_t . 因为 size_t 是无符号的,所以防止混合签名/无符号比较的警告,显式将 int 更改为无符号整数 . 要将非负 int 更改为无符号整数,请强制转换为 (unsigned) .

    要比较,测试 c 是否为负数,如果不是,则将 (unsigned)c 直接与 len 进行比较 . 编译器将根据需要隐藏类型,并产生算术正确的答案 .

    ..

    size_t len = strlen("SomeString");
    int c = 20;  // some int
    
    if (c < 0 || (unsigned)c < len) puts("c less than len");
    else puts("c >= len");
    

相关问题