【C语言】带你深入剖析字符串相关知识(详细讲解+代码展示)

简介: 字符串,一个C语言中的难点,其包含很多零碎知识,以及我们在算法题中会经常遇见,在这里我们会深入浅出的讲解字符串相关知识,还有以及gets()、fgets()、puts()、fputs()、strlen()等一些函数知识...........

 哈喽大家好,今天我们来为大家介绍字符串的相关知识;在C语言的学习中,字符串的知识与数组会紧密结合。所以,个人建议在大家掌握数组的知识后食用最佳。

学习数组的跳转链接:https://developer.aliyun.com/article/1003455?spm=a2c6h.26396819.creator-center.10.27df3e18gV6odC

下面我们正式来讲解一下字符串的知识吧!

字符数组与字符串区别

我们首先要知道的是,在C语言中是没有字符串这种数据类型的,所以,在这里我们所提到的字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。

需要注意的是,字符串一定是一个char的数组,但是char的数组未必是字符串;如果char数组没有以数字0结尾,那么就不是一个字符串,只是普通字符数组;所以字符串是一种特殊的 char 的数组。

其字符串在内存中的表示如图所示:

字符串的内存表示.png

空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。

关于字符 ‘\0’ : '\0' 就是8位的00000000它于 0 等价,以其结尾的char数组就是一个字符串。并且,我们并不需要把 null 字符放在字符串常量的末尾。因为C 编译器会在初始化数组时,会自动把 \0 放在字符串的末尾。

下面我们看下这段知识与实际应用对应的代码吧:

#include <stdio.h>
int main()
{
  //以'\0'('\0'就是数字0)结尾的字符数组是字符串
  char c2[] = { 'c', ' ', 'p', 'r', 'o', '\0'}; 
  printf("c2 = %s\n", c2);
  //字符串处理以'\0'(数字0)作为结束符,后面的'h', 'l', 'l', 'e', 'o'不会输出
  char c3[] = { 'c', ' ', 'p', 'r', 'o',  '\0', 'h', 'l', 'l', 'e', 'o', '\0'};
  printf("c3 = %s\n", c3);
  return 0;
}

image.gif

所以对于字符串来说,我们就将其看做一个特殊的数组来对待即可,不要将其想象的太过复杂了。

字符串的初始化

因为在C语言中没有字符串类型,所以我们使用字符数组去进行实现字符串的初始化。

1.不设定长度初始化

如果我们不设定字符串长度但进行初始化时,这时结尾将不会去自动添加0结束符,也就是你初始化的长度为多少,那么结果的长度就是多少。

例如:

char buf[] = { 'a', 'b', 'c' };

image.gif

2. 指定长度初始化

如果我们在初始化数组时已经为数组指定长度了,那么我们后面没有赋值的元素,将会被自动补0。

就比如:

char buf2[100] = { 'a', 'b', 'c' };
char buf[1000]={“hello”};

image.gif

两个数组没有赋值的元素将会被自动补0。

3.所有元素赋0

如果我们在初始化的时候想要将一个字符数组里面的元素全部初始化为0,那么我们就不用再去一个一个的输入了,这时我们只需要一个0即可,例如:

char buf3[100] = { 0 };

image.gif

就是将buf3数组里的元素全部初始化为0。

总结代码:

#include <stdio.h>
// C语言没有字符串类型,通过字符数组模拟
int main()
{
  //不指定长度, 没有0结束符,有多少个元素就有多长
  char buf[] = { 'a', 'b', 'c' };
  printf("buf = %s\n", buf);  //乱码
  //指定长度,后面没有赋值的元素,自动补0
  char buf2[100] = { 'a', 'b', 'c' };
  printf("buf2 = %s\n", buf2);
  //所有元素赋值为0
  char buf3[100] = { 0 };
  return 0;
}

image.gif

注意:

1. 如果我们在初始化数组时,将中间的一个元素设置为了 "\0" 那么这个元素后面的元素将不会被输出,因为字符串遇到 "\0" 的时候会自动结束。

举例:

#include <stdio.h>
// C语言没有字符串类型,通过字符数组模拟
int main()
{
  char buf5[50] = { '1', 'a', 'b', '0', '7' };
  printf("buf5 = %s\n", buf5);
  char buf6[50] = { '1', 'a', 'b', 0, '7' };
  printf("buf6 = %s\n", buf6);
  char buf7[50] = { '1', 'a', 'b', '\0', '7' };
  printf("buf7 = %s\n", buf7);
  return 0;
}

image.gif

运行结果:

图片1.png

通过上面的例子,不难看出,字符串在输出的时候,遇见 0(‘\0’)会自动结束,其后面的元素也就无法输出了。

2. 我们在使用'\0'时后面最好不要连着数字,因为有可能几个数字连起来刚好是一个转义字符

例如:

#include <stdio.h>
// C语言没有字符串类型,通过字符数组模拟
int main()
{
  char str[] = "\012abc";
  printf("str == %s\n", str);
  return 0;
}

image.gif

'\ddd'是八进制字义字符,'\xdd'是十六进制转移字符;这里\012就相当于\n;所以就不难看出我们上题的输出结果了。

结果:

图片2.png

字符串的输入输出

由于字符串采用了'\0'标志,所以字符串的输入输出将变得简单方便。

#include <stdio.h>
int main()
{
  char str[100];
  printf("input string1 : \n") ;
  scanf("%s", str) ;
  printf("output:%s\n", str) ;
  return 0;
}

image.gif

看上段代码,此代码也就是从键盘输入一个字符串并打印出来;但是呢,这段代码会出现一个问题, 这个问题就出现在这句语句上:scanf("%s", str); 因为scanf("%s", str);默认以空格分隔 ;也就是如果我们输入一串连续的字符串是没问题的,但是如果我们输入的字符串中间是有空格的,那么系统只会识别第一个空格前面的部分,并输出。

输出对比:

图片3.png

图片4.png

所以在这里我为大家介绍几个输入输出的函数,以遍大家可以更好的去对字符串进行操作。

gets()

gets()函数的功能是:从标准输入读入字符,并保存到s指定的内存空间,直到出现换行符或读到文件结尾为止。

其应照以下方法应用:

#include <stdio.h>
char *gets(char *s);

image.gif

其中s 表示字符串首地址 ;如果成功的话将会读入的字符串,否则返回NULL 。

在这里我们要分析一下gets(str)与scanf(“%s”,str)的区别:

    1. gets(str)允许输入的字符串含有空格
    2. scanf(“%s”,str)不允许含有空格

    需要注意的是由于scanf()和gets()无法知道字符串s大小,必须遇到换行符或读到文件结尾为止才接收输入,因此容易导致字符数组越界(缓冲区溢出)的情况。我们在平时使用的时候一定要注意所开的空间,以避免出现这种情况。

    fgets()

    fgets()函数的功能是:从指定的文件内读入字符,保存到字符串所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了(最大读取字符串长度-1)个字符为止,最后会自动加上字符 '\0' 作为字符串结束。

    其应照以下方法应用:

    #include <stdio.h>
    char *fgets(char *s, int size, FILE *stream);

    image.gif

    其中: s表示字符串 ; size是指定最大读取字符串的长度; stream:文件指针,如果读键盘输入的字符串,固定写为stdin 。  

    返回值

          成功:成功读取的字符串

          读到文件尾或出错: NULL 。

    fgets()在读取一个用户通过键盘输入的字符串的时候,同时把用户输入的回车也做为字符串的一部分。通过scanf和gets输入一个字符串的时候,不包含结尾的“\n”,但通过fgets结尾多了“\n”。所以fgets()函数是安全的,不会存在缓冲区溢出的问题。

    puts()

    puts()函数的功能是标准设备输出s字符串,并且在输出完成后自动输出一个'\n'

    其应照以下方法应用:

    #include <stdio.h>
    int puts(const char *s);

    image.gif

    其中: s表示字符串首地址 。

    返回值

          成功:非负数

          失败:-1

    fputs()

    fputs()函数的功能是将str所指定的字符串写入到stream指定的文件中字符串结束符'\0'  不写入文件。

    其应照以下方法应用:

    #include <stdio.h>
    int fputs(const char * str, FILE * stream);

    image.gif

     其中 str表示字符串 ; stream表示文件指针如果字符串输出到屏幕,固定写为stdout

    返回值:

          成功:0

          失败:-1

    fputs()是puts()的文件操作版本,只不过fputs()不会自动输出一个'\n'

    strlen()

    strlen()的功能是计算指定指定字符串s的长度不包含字符串结束符‘\0’ 。

    其应照以下方法应用:

    #include <string.h>
    size_t strlen(const char *s);

    image.gif

    其中 s 表示字符串首地址

    返回值

           将返回字符串s的长度,size_t为unsigned int类型

    字符串强化训练

    到这里我们字符串的相关知识也了解的差不多了,这里我们通过一个字符串追加的例子,来为大家强化训练一下字符串相关知识。

    在这个例子中,需要我们对两个字符数组初始化,之后将两个字符数组合并后再输出,也就是将两个字符串合并为一个字符串,也可以理解为将第二个字符串追加到第一个字符串的上面。

    具体思路:

    我们在试图解决这个题目的时候,要合理的去运用数组的知识,毕竟我们前面提到了,字符串就是一个特殊的数组。首先,我们可以设出三个数组,其中两个已经完成初始化,有一个作为我们最终的输出数组。对于两个初始化的数组,我们将第一个数组中的每个元素,利用循环,将其搬运到最终数组上,知道遇到 '\0' 时切换到第二个数组,同理,这样最后我们只需要按顺序输出第三个数组里面的元素即可。

    其代码:

    #include <stdio.h>
    int main()
    {
      char str1[] = "abcdef";
      char str2[] = "123456";
      char dst[100];
      int i = 0;
      while (str1[i] != 0)  //判断第一个字符串是否到结尾 
      {
        dst[i] = str1[i]; //将第一个字符串的元素一一对应到最终数组上 
        i++;
      }
      int j = 0;
      while (str2[j] != 0)  //判断第二个字符串是否到结尾 
      {
        dst[i + j] = str2[j]; //将第二个字符串的元素一一对应到最终数组上 
        j++;
      }
      dst[i + j] = 0;   //字符串结束符
      printf("dst = %s\n", dst);  //直接输即可 
      return 0;
    }

    image.gif

    运行结果:

    图片5.png

    好啦,到了这里我们字符串相关知识就讲解的差不多了,希望你可以听懂,当然如果有什么不会的可以随时在评论区提问,我看到后会第一时间解答的。

    目录
    相关文章
    |
    2月前
    |
    存储 搜索推荐 C语言
    深入C语言指针,使代码更加灵活(二)
    深入C语言指针,使代码更加灵活(二)
    |
    2月前
    |
    存储 程序员 编译器
    深入C语言指针,使代码更加灵活(一)
    深入C语言指针,使代码更加灵活(一)
    |
    2月前
    |
    C语言
    深入C语言指针,使代码更加灵活(三)
    深入C语言指针,使代码更加灵活(三)
    深入C语言指针,使代码更加灵活(三)
    |
    2月前
    |
    C语言 C++
    【C语言】解决不同场景字符串问题:巧妙运用字符串函数
    【C语言】解决不同场景字符串问题:巧妙运用字符串函数
    |
    3月前
    |
    安全 C语言
    在C语言中,正确使用运算符能提升代码的可读性和效率
    在C语言中,运算符的使用需要注意优先级、结合性、自增自减的形式、逻辑运算的短路特性、位运算的类型、条件运算的可读性、类型转换以及使用括号来明确运算顺序。掌握这些注意事项可以帮助编写出更安全和高效的代码。
    56 4
    |
    3月前
    |
    存储 C语言
    【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
    本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
    |
    3月前
    |
    存储 Serverless C语言
    【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
    本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
    186 7
    |
    2月前
    |
    C语言
    C语言练习题代码
    C语言练习题代码
    |
    3月前
    |
    存储 算法 C语言
    数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解
    本文详细介绍了单链表的理论知识,涵盖单链表的定义、优点与缺点,并通过示例代码讲解了单链表的初始化、插入、删除、查找等核心操作。文中还具体分析了按位序插入、指定节点前后插入、按位序删除及按值查找等算法实现,并提供了尾插法和头插法建立单链表的方法,帮助读者深入理解单链表的基本原理与应用技巧。
    631 6
    |
    3月前
    |
    存储 C语言 C++
    数据结构基础详解(C语言) 顺序表:顺序表静态分配和动态分配增删改查基本操作的基本介绍及c语言代码实现
    本文介绍了顺序表的定义及其在C/C++中的实现方法。顺序表通过连续存储空间实现线性表,使逻辑上相邻的元素在物理位置上也相邻。文章详细描述了静态分配与动态分配两种方式下的顺序表定义、初始化、插入、删除、查找等基本操作,并提供了具体代码示例。静态分配方式下顺序表的长度固定,而动态分配则可根据需求调整大小。此外,还总结了顺序表的优点,如随机访问效率高、存储密度大,以及缺点,如扩展不便和插入删除操作成本高等特点。
    216 5