C之典型字符串(二十八)

        我们在上节博客中介绍了 C 语言中字符串相关的概念,那么我们今天就来看看在字符串这块的典型问题。

        A、我们先来看看下面的示例代码会输出什么,代码如下

#include <stdio.h>

int main()
{
    char buf[15] = {0};
    char src[] = "hello %s";
    
    snprintf(buf, sizeof(buf), src);
    
    printf("buf = %s\n", buf);
    
    return 0;
}

        我们先来说说 snprintf 函数,它本身是可变参数的函数,原型是这样的:int snprintf(char* buf, int buf_size, const char* fomart, ...)。当函数只有3个参数时,如果第三个参数没有包含格式化信息,函数调用没有问题;相反,如果第三个参数包含了格式化信息,但缺少后续对应参数,则程序行为不稳定。上面的程序中第8行调用了 snprintf 函数,但是在第6行定义的 src 字符数组中包含了 %s,则它的行为是不确定的。我们来看看编译结果图片.png

        我们看到编译其实已经提示了,打印的结果果然是不确定的。那么我们在 snprintf 函数中再加上第四个参数字符串“world”试试(或者直接把第6行后面的 %s 变成 world 也是同样的效果)。编译结果如下

图片.png

        那么我们看到编译没有警告,程序也完美运行。

        B、我们再来看看下面这份示例代码

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

int main()
{
    #define STR "Hello, \0World\0"
    
    char* src = STR;
    char buf[255] = {0};
    
    snprintf(buf, sizeof(buf), src);
    
    printf("strlen(STR) = %d\n", strlen(STR));
    printf("sizeof(STR) = %d\n", sizeof(STR));
    
    printf("strlen(src) = %d\n", strlen(src));
    printf("sizeof(src) = %d\n", sizeof(src));
    
    printf("strlen(buf) = %d\n", strlen(buf));
    printf("sizeof(buf) = %d\n", sizeof(buf));
    
    printf("src = %s\n", src);
    printf("buf = %s\n", buf);
    
    return 0;
}

        我们先来分析下这个程序,第6行定义了一个宏,但是它里面有两个 \0,实际上是3个,因为编译器还会为字符串自动去分配个 \0。在程序的第11行进行 src 到 buf 的内容复制。我们在上届说过字符数组是以 \0 结尾的,因此第13行打印的长度为7。但第14行打印的是它整个宏定义的长度,所以为15。第16行打印的也便是 7 了,第17行打印的指针的长度,便是4。第19行打印的 buf 中内容的长度同样也是 7,第20行打印的 数组 buf 的长度便是 255。第22和23行分别打印 src 和 buf 中的内容,便是 hello 了。我们来看看编译结果图片.png

        结果和我们分析的一致,字符串字面量的本质为数组。

        C、再来看第三个示例程序,代码如下

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

int main()
{
    #define s1 "hello world"
    #define s2 "hello world"
    
    if( s1 == s2 )
    {
        printf("Equal\n");
    }
    else
    {
        printf("Non Equal\n");
    }
    
    if( strcmp(s1, s2) == 0)
    {
        printf("Equal\n");
    }
    else
    {
        printf("Non Equal\n");
    }
    
    return 0;
}

        我们先来分析下,我们在第6和7行分别定义了两个宏字符串(但是它们的内容是相同的)。接下来我们直接将 s1 和 s2 进行判断是否相等。那么在这块我们判断的应当是他两的地址,它们在这块就是数组,两个数组怎么可能进行相等比较呢。如果是判断地址,第一个 if 语句应当打印出不相等的。下面的 if 语句是用 strcmp 函数进行判断的饿,那么这个当然是相等的啦,因为这个函数判断的是他两的内容。所以经我们分析,第一个 if 语句打印出 Non Equal,第二个 if 语句打印出 Equal。我们来看看编译器就是是怎么处理的

图片.png

        我们看到第一个和我们分析的不一样,那么我们再来看看 BCC 编译器

图片.png

        那么 BCC 编译器的结果和我们分析的是一致的。在 gcc 编译器中它做了优化,当我们定义 s1 之后,进行 s2 的定义时。编译器发现他俩内容是一样的,便将 s2 也指向了 s1 的地址,因为它觉得你是在浪费内存。我们在程序中加上打印 s1 和 s2 的地址的语句,gcc 打印结果如下

图片.png

        gcc 编译器果然是将他俩放在一个地址上了。但是我们看看 BCC 呢

图片.png

        我们看到 BCC 是这样的,所以我们在以后不能写出依赖于某种编译器的代码,这样的话,代码的可移植性就降低了。所以我们在进行字符串之间的相等比较时需要用 strcmp 完成,不可直接用 == 进行字符串直接进行比较。完全相同的媳妇吃字面量的 == 比较结果为 false。一些现代编译器能够将相同的字符串字面量映射到同一个无名字符数组,因此 == 比较结果为 true。

        D、最后我们再来看个关于字符串循环右移的问题,这也是一道笔试面试题。代码如下

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

void right_shift_r(const char* src, char* result, unsigned int n)
{
    const unsigned int len = strlen(src);
    int i = 0;
    
    for(i=0; i<len; i++)
    {
        result[(i+n) % len] = src[i];
    }
    
    result[len] = '\0';
}

int main()
{
    char result[255] = {0};
    
    right_shift_r("abcde", result, 2);
    
    printf("%s\n", result);
    
    right_shift_r("abcde", result, 5);
    
    printf("%s\n", result);
    
    right_shift_r("abcde", result, 8);
    
    printf("%s\n", result);
    
    return 0;
}

        我们可以利用求余的的方式进行字符串的赋值。那么我们用一个 for 循环就完成右移,它的时间复杂度为 O(n),这个效率无疑是最高的。


        欢迎大家一起来学习 C 语言,可以加我QQ:243343083

猜你喜欢

转载自blog.51cto.com/12810168/2106146