【C Primer Plus第六版 学习笔记】第十一章 字符串和字符串函数

有基础,进阶用,个人查漏补缺

  1. puts()只显示字符串,而且自动在末尾加上换行符

  2. 字符串定义(字符串有字符串常量、char类型数组、指向char的指针)

    1. 字符串常量:

      用双括号括起来,双引号中的字符和编译器自动加入末尾的\0字符,都作为字符串储存在内存中。

      属于静态存储类别,说明如果在函数中使用字符串常量,该字符串常量只会被储存一次,在整个程序的生命期内存在,即使函数被调用多次。

      用双括号括起来的内容被视为指向该字符串储存位置的指针。

      printf("%s, %p, %c\n", "We", "are", *"space farers");
      //输出:We, 0x100000f61, s
      //%s代表字符串型格式符,%p用于以十六进制的形式打印地址(指针地址),%c代表字符型格式符
      //%x、%X和%p的相同点都是16进制,但是%p会按编译器位数长短(32位/64位)输出地址,不够的补零
      //printf()根据%s打印We
      //根据%p打印地址(如果“are”代表一个地址,printf()将会打印该字符串的首字符地址)
      //*"space farers"表示该字符串所指向地址上储存的值,为该字符串首字符//字符串常量之间没有间隔或者用空格分割,C语言会将其视为串连起来的字符串常量
      char a[50] = "hello, and"" how are" " you"" today!";
      //等价于
      char a[50] = "hello, and how are you today!";
      
    2. char类型数组:

      在指定数组大小时,要确保数组元素个数至少比字符串长度多1(为了容纳\0),所有未被使用的元素会被自动初始化为0(指的是char形式的空字符\0,不是数字字符0)

      如果忽略数组初始化声明中的数组大小,编译器会自动根据初始化内容进行计算

      const char b1[] = "ni hao";//char数组初始化 
      const char b2[7] = {'n', 'i', ' ', 'h', 'a', 'o', '\0'};//标准的数组初始化,复杂
      //如果没有最后的\0,则是数组;有\0,则是字符串
      
    3. 指向char的指针

      //这两个声明形式几乎一致
      //初始化数组把静态存储区的字符串拷贝到数组中
      const char arl[] = "something is pointing at me.";//28个字符+\0=29个
      //初始化指针只把字符串的地址拷贝给指针
      const char * pt1 = "something is pointing at me.";
      
      1. 数组形式中,arl为该数组首元素地址的别名(&arl[0]),所以arl是地址常量。不能更改arl,如果改变,则意味着改变了数组的储存位置(即地址)。可进行arl+1的操作,但是不能++arl。递增运算符不能用于常量。

      2. 指针形式中,编译器会为指针变量pt1留一个储存位置,把字符串的地址储存在指针变量中。该变量最初指向该字符的首字符,但是它的值可以改变。因此,可以使用递增运算符,++pt1指向第二个字符。

        由于pt1指向const数据,所以应该把pt1声明为指向const数据的指针,这意味着不能用pt1改变它所指向的数据,但是仍然可以改变pt1的值(即pt1指向的位置)。

      3. 如果不修改字符串,就不要用指针指向字符串常量,即建议在把指针初始化为字符串变量时使用const限定符

        //下面语句都引用了字符串“klingon”的内存位置
        char * p1 = "klingon";
        p1[0] = 'f';      //由于是非const,允许修改为f,所以该地址对应的值变成了f
        printf("klingon");//printf()使用的是内存里的值,故"klingon"对应地址里,k变成了f
        printf(": beware the %s!\n", "klingon");//同上
        //输出:
        //flingon: beware the flingon!
        //当要打印"klingon"时,都会打印"flingon"//建议在把指针初始化为字符串变量时使用const限定符
        const char * p1 = "klingon";
        
  3. 数组和指针的区别

    1. 两者最主要的区别是:数组名ar是常量,指针名pt是变量

      char ar[] = "I love Tim.";       //数组的元素是变量,但数组名不是变量
      const char *pt = "I love Sam."; 
      
    2. 实际使用上:

      1. 都可以使用数组表示法:

        //均输出I love
        for(i=0; i<6; i++)
        {putchar(ar[i]);
        }for(i=0; i<6; i++)
        {putchar(pt[i]);
        }
        
      2. 都可以进行指针加法操作

        //均输出I love
        for(i=0; i<6; i++)
        {putchar( *(ar + i) );
        }for(i=0; i<6; i++)
        {putchar( *(pt + i) );
        }
        
      3. 只有指针法可以进行递增操作

        while( *(pt) != '\0' )  //在字符串末尾处停止putchar( *(pt++) );   //打印字符,指针指向下一个位置 //输出I love Sam.
        
      4. 如果想让数组和指针统一,则

        pt = ar;//pt现在指向数组ar的首元素
        //不能这么做
        ar = pt;//非法构造,赋值运算符左侧必须可以修改(数组元素是变量,但数组名不是变量)
        //但是这么做不会导致pt指向的字符串消失,只是改变了储存在pt中的地址
        
      5. 可以改变a数组中的元素的信息

        ar[7] = 'm';
        *(ar + 7) = 'm';
        
  4. 字符串数组:

    const char *pt[5] = {"one", "tow", "three", "four", "five"};
    char ar[5][40] = {"ONE", "TOW", "THREE", "FOUR", "FIVE"};
    
    1. pt和ar都表示5个字符串
    2. 使用一个下标时都分别表示一个字符串,如pt[0]和ar[0]
    3. 使用两个下标时都分别表示一个字符,如pt[1][2]表示pt数组中第2个指针所指向的字符串的第3个字符,ar[1][2]表示ar数组的第2个字符串的第3个字符
    4. 两者初始化方式一样

    1. pt是一个内含5个指针的数组,在系统中共占40字节

    2. ar是一个内含5个数组的数组,每个数组内含40个char类型的数据,共占用200个字节

    3. 虽然pt[0]和ar[0]都分别表示一个字符串,但是pt和ar的类型并不同

      1. pt中的指针指向初始化时所用的字符串常量的位置,这些常量被储存在静态内存中
      2. ar中的数组则被储存着字符串常量的副本,所以每个字符串都被储存了两次
    4. pt可看做一个不规则数组,每行长度不一样(pt数组的指针元素所指向的字符串不必储存在连续的内存中);ar可看作一个矩形二维数组,每行长度都是40字节

      在这里插入图片描述


    1. 综上所述,如果要用数组表示一系列待显示的字符串,就使用指针数组,效率更高,但是指针指向的字符串常量不能修改;

      而字符串数组可以修改,如果要改变字符串或为字符串输入预留空间,不要使用指向字符串常量的指针

  5. 字符串输入

    要把一个字符串读入程序,要先预留储存该字符串的空间,然后用输入函数获取该字符串

    1. 分配空间

      如果未分配空间,如

      char *name;
      scanf("%s", name);
      

      会通过编译,但是在读入的过程中,name可能会擦写掉程序中的数据或代码。因为scanf()要把信息拷贝到参数指定的地址上,但此时该参数是一个未初始化的指针,name可能会指向任何地方。

      解决方法:在声明时显式指明数组大小,或使用C库函数分配内存(12章介绍)

      char name[81];
      
    2. gets()(切勿使用)

      1. 读取整行输入,直至遇到换行符,然后丢弃换行符,储存其余字符,并在末尾添加\0使其成为C字符串。
      2. 常和puts()配对使用,该函数用于显示字符串,并在末尾添加换行符。即gets丢弃,puts添加。
      3. gets()只知道数组开始处,无法检查数组是否装得下输入行,如果输入过长,则会导致缓冲区溢出,即多余字符超出了指定的目标空间。
        1. 如果这些多余的字符只是占用了尚未使用的内存,就不会立刻出现问题
        2. 如果它们擦写掉程序中的其他数据,会导致程序异常终止
      4. 不建议使用gets()
    3. gets()代替品:

      1. C11不一定支持,专门用于处理文件输入

      2. fgets()

        1. char *fgets(char *str, int n, FILE *stream)

          • str – 这是指向一个字符数组的指针,该数组存储了要读取的字符串。

          • n – 这是要读取的最大字符数(包括最后的空字符),所以将读入n-1个字符或者读到第一个换行符为止。通常是使用以 str 传递的数组长度。

          • stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。即指明要读入的文件。如果读入从键盘输入的数据,则以stdin作为数据,该标识符定义在stdio.h。

          • 如果成功,该函数返回char指针,与传入的第一个参数str相同。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针(NULL)。如果发生错误,返回一个空指针。

          • fgets会读入换行符,puts则不会添加换行符。与gets和puts相反。

          • 举例代码:

            #include <stdio.h>
            #define STLEN 14
            int main(void)
            {char words[STLEN];//该函数用于显示字符串,并在末尾添加\nputs("Enter a string.");//输入apple pie,apple pie\n\0会被存入fgets(words, STLEN, stdin);puts(words);   //会丢弃换行符fputs(words, stdout);return 0;
            }输出:
            Enter a string.————puts()输出,末尾会添加\n
            **apple pie**      ————fgets()输入,会把\n存入,即apple pie\n\0
            ****apple pie      ————puts()输出,末尾会添加\napple pie      ————fputs()输出,末尾不会添加\n
            
        2. 举例代码:虽然STLEN被设置为10,但是该程序在处理过长的输入时完全没有问题。

          1. 程序中的fgets()一次读入STLEN-1 = 9个字符,故一开始只读入了”By the wa”(10个字符),并储存为By the wa\0
          2. 接着fputs()打印该字符,末尾不会添加\n(本此迭代没有),不会换行
          3. while循环进入下一轮迭代,读入”y, the ge” ,并储存为y, the ge\0
          4. 直至读入”tion\n”,由于字符串中的\n被输出,光标被移至下一行开始处
          5. 在按下enter之前,输入都被储存在缓冲区,按下后输入了一个\n,并把整行输入发送给fgets()。fputs()把字符发送给另一个缓冲区,当发送换行符的时候,缓冲区的内容被发送到屏幕。
          #include <stdio.h>
          #define STLEN 10
          int main(void)
          {char words[STLEN];puts("Enter a string.");//该函数会在末尾添加\n//当读到文件末尾或者读到空字符,或者读入了一行中的第一个字符为\nwhile(fgets(words, STLEN, stdin) != NULL && words[0] != '\n')fputs(words, stdout);puts("Done.");//该函数会在末尾添加\nreturn 0;
          }
          输出:
          Enter a string.                 ————puts()输出,末尾会添加\n
          **By the way, the gets() function** ————fgets()输入,会把\n存入
          By the way, the gets() function ————fputs()输出,末尾会丢弃\n
          **Also enter another string**       ————输入
          Also enter another string ****      ————输出————输入前,光标就在该行,然后输入\n
          Done.														    则该行第一个字符为\n,结束
        3. 如何处理换行符?解决方法:

          #include <stdio.h>
          #define STLEN 10
          int main(void)
          {char words[STLEN];int i;puts("Enter a string.");//该函数会在末尾添加\n//当读到文件末尾或者读到空字符,或者读入了一行中的第一个字符为\nwhile(fgets(words, STLEN, stdin) != NULL && words[0] != '\n'){i = 0;//遍历字符串,直至遇到换行符或空字符while(words[i] != '\n' && words[i] != '\0')i++;//如果先遇到换行符,则替换成空字符if(words[i] == '\n')words[i] == '\0';//如果先遇到空字符,else部分便丢弃输入行的剩余字符elsewhile(getchar() != '\n')//读到\0就会结束continue;}puts("Done.");//该函数会在末尾添加\nreturn 0;
          }
          /*
          输出:
          Enter a string.
          **This                                 ————输入,存为**This\0
          This
          **program seems                        ————输入,存为**program s\0
          program s
          **unwilling to accept long line        ————输入,存为**unwilling\0
          unwillingdone
          
        1. char *gets_s( char *str, rsize_t n )
    4. 和fgets()类似,区别如下:

      1. gets_s()只从标准输入中读取数据,故不需要第3个参数
      2. 如果gets_s()读到换行符,会丢弃它而不是储存它
      3. 如果gets_s()读到最大字符数都没有读到换行符,会执行:
        1. 首先把目标数组中的首字符设置为空字符
        2. 读取并丢弃随后的输入,直至读到换行符或文件结尾
        3. 最后返回空指针
    5. char *gets_s( char *str, rsize_t n );

      1. 读stdin入指向的字符数组,str直到找到换行符或发生文件结束。在读入数组的最后一个字符后立即写入空字符。换行符被丢弃,但不存储在缓冲区中。
      2. 读取字符,stdin直到找到换行符或发生文件结束。只将大部分n-1字符写入指向的数组中str,并始终写入终止空字符(除非str是空指针)。换行符(如果找到)将被丢弃,并且不计入写入缓冲区的字符数。在运行时检测到以下错误并调用当前安装的约束处理函数:
      • n是零
      • n大于RSIZE_MAX
      • str 是一个空指针
      • 将n-1字符存储到缓冲区后,不会遇到endline或eof 。

      无论如何,在调用约束处理程序之前,gets_s首先完成读取和放弃字符,stdin直到换行符,文件结束条件或读取错误。

      返回值

      成功时str,失败NULL。

      1. gets()、fgets()、gets_s()的适用性比较
        1. 当输入行太长,gets()会擦写现有数据,gets_s()会丢弃多余字符,故fgets()最好
        2. 故首选fgets()
    6. scanf()

      1. 若fgets()、gets()函数像获取字符串函数,则scanf()是获取单词函数

      2. scanf()如果使用%s转换说明,以下一个空白字符(空行、空格、制表符、换行符)作为字符串结束(字符串不包括空白字符)

      3. 字符宽度和scanf():口表示空格

        输入语句原输入序列name中的内容剩余输入序列
        scanf(’%s’, name);fleebert口hupfleebert口hup
        scanf(’%5s’, name);fleebert口hupfleebert口hup
        scanf(’%5s’, name);ann口ularann口ular
      4. 返回值为成功读取的项数或者EOF(读到文件结尾)

      5. 举例代码

      #include <stdio.h>
      int main(void)
      {char name1[11], name2[11];int count;printf("Enter 2 names.\n");count = scanf("%5s %10s", name1, name2);printf("I read the %d names %s and %s.\n", count, name1, name2);return 0;
      }
      /*输出
      1.两个名字的字符个数都没有超过字段宽度,第1个单词刚好能被完全存入缓冲区
      Enter 2 names.
      **jesse jukes**
      I read the 2 names jesse and jukes.
      2.前一个单词字符个数没有超过限制5,故**遇到空格就算结束**,第2个名字只读入了前10个字符
      Enter 2 names.
      **liza applebottham**
      I read the 2 names liza and applebotth.
      **3.portensia的后4个被存入name2中,
      因为第2次调用scanf()时从上一次调用结束的地方继续读取数据**
      Enter 2 names.
      **portensia callowit**
      I read the 2 names porte and nsia.
      
  6. 字符串输出:puts()、fputs()、pinrtf()——337页最后一段话有误,应该写puts()

    1. puts()

      1. 传入字符串地址作为参数即可,遇到空字符结束,会自动换行

        #include <stdio.h>
        int main(void)
        { //用双引号括起来的是字符串常量//并且会被视为该字符串的地址char str1[80] = "I love you.";const char * str2 = "You love me.";puts("Please say.");puts(str1);puts(str2);puts(&str1[5]);puts(str2 + 4);return 0;
        }
        

        输出:

        Please say.

        I love you.

        You love me.

        e you.

        ——&str1[5]是str1数组的第6个元素(e),从该元素开始输出

        love me.

        ——str2 + 4指向l,从该元素开始输出

    2. fputs()

      int fputs(const char *str, FILE *stream)

      1. 是puts()针对文件定制的版本,区别如下:
        1. fputs()第2个参数指明要写入数据的文件,如果要打印在显示器上,可以用定义在stdio.h中的stdout
        2. 不会自动换行
          函数特点函数特点
          gets()(不使用)丢弃\nputs()添加\n,会自动换行
          fgets()保留\nfputs()不添加\n,不会自动换行
    3. printf()

      1. 不会自动换行
      2. 和puts相比,计算机执行时间更长
  7. 字符串函数:strlen()、strcat()、strcmp()、strncmp()、strcpy()、strncpy()

    函数完整函数声明作用返回值备注
    strlen()size_t strlen(const char *str)用于统计字符串长度返回字符串的长度
    strcat()char *strcat(char *dest, const char *src)把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。返回一个指向最终的目标字符串 dest 的指针(地址)无法检查第一个数组是否能容纳第2个数组,可能导致溢出问题,和gets()类似
    strncat()char *strncat (char *dest, const char *src, size_t n)把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止返回一个指向最终的目标字符串 dest 的指针(地址)解决上述strcat()问题
    strcmp()int strcmp(const char *str1, const char *str2)把 str1 所指向的字符串和 str2 所指向的字符串进行比较str1<str2,则返回值<0; str1>str2,则返回值>0;str1=str2,则返回值=0比较的是字符串内容,而不是地址,也不是整个数组,因为数组大小和字符串大小不一定相同。当字符串长度不一致时,多的部分比较的是空字符
    strncmp()int strncmp(const char *str1, const char *str2, size_t n)把 str1 和 str2 进行比较,最多比较前 n 个字符。str1<str2,则返回值<0; str1>str2,则返回值>0;str1=str2,则返回值=0
    strcpy()char *strcpy(char *dest, const char *src)把 src(源字符串) 所指向的字符串复制到 dest(目标字符串)返回一个指向最终的目标字符串 dest 的指针(地址)第一个指针应指向一个数据对象,如数组,并且该对象有足够的空间。无法检查第一个数组是否能容纳第2个数组,可能导致溢出问题,和gets()类似
    strncpy()char *strncpy(char *dest, const char *src, size_t n)把 src 所指向的字符串复制到 dest,最多复制 n 个字符。返回最终复制的字符串当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

    strcpy()、strncpy()注意:

    1. 声明数组将分配储存数据的空间,而声明指针只分配储存一个地址的空间

    2. 目标字符串必须先初始化

      char * str;
      strcpy(str, "hello");//错误,str未被初始化,该字符串会被拷贝到任何地方!!!
      
    3. 第一个参数不必指向数组开始

      const char * orig = "beast";
      char copy[40] = "be the best that you can be.";
      char * ps;
      puts(orig);//输出beast
      puts(copy);//输出be the best that you can be.
      ps = strcpy(copy + 7, orig);
      puts(copy);//输出be the beast,因为copy+7指向best中的b,从此处开始粘贴orig
      puts(ps);//输出beast,strcpy()返回一个指向最终的目标字符串 dest 的指针(地址)
      
    4. strncpy(dest, src, n)中,如果拷贝到第n个字符时还未拷贝完整个源字符串,就不会拷贝空字符,所以拷贝的副本中不一定有空字符。

      鉴于此,可以把n设置为比目标数组大小小1,然后把数组最后一个元素设置为空字符:

      #define TARGSIZE 7
      strncpy(dest, str, TARGSIZE-1);
      dest[TARGSIZE-1] = '\0';
      
  8. sprintf()

    int sprintf(char *str, const char *format, …)

    • 和printf()类似,这个是把数据写入字符串中,而不是打印在显示器,即发送格式化输出到 str 所指向的字符串

    • str – 这是指向一个字符数组的指针,该数组存储了 C 字符串。

    • format – 这是字符串,包含了要被写入到字符串 str 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是 %[flags][width][.precision][length]specifier

    • 如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。

    • 例子:

      #include <stdio.h>
      int main()
      {char str[80];sprintf(str, "Pi 的值 = %f", 3.1415);puts(str);return(0);
      }
      /*输出:
      Pi 的值 = 3.1415
      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/285041.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据加密标准DES硬件实现(Modelsim)

数据加密标准DES硬件实现 本文内容摘要理论依据和设计内容仿真结果整体代码 本文内容摘要 本文设计并验证了DES的密钥扩展通路&#xff0c;分别采用Round Based方法和Pipeline方法两种方式设计并验证DES的明文通路 理论依据和设计内容 首先&#xff0c;要了解分组密码算法及DE…

react点击事件

使用事件对象传参 //按钮 <button onClick{thisClick}>点击事件</button> //方法 const thisClick (e) >{console.log(这里是点击结果,e)} 输出结果 传递自定义参数 //按钮 <button onClick{()>thisClick(参数)}>点击事件</button> //方法 c…

大数据Doris(三十八):Aggregate 和 Uniq 模型中的 ROLLUP

文章目录 Aggregate 和 Uniq 模型中的 ROLLUP 一、获得每个用户的总消费

云原生之深入解析Thanos在EKS多集群架构上存储多个集群Prometheus

一、前言 随着 HiredScore 的产品和客户群越来越大&#xff0c;已经开始向 Kubernetes 过渡并迅速采用它&#xff0c;它是我们重要的障碍之一&#xff0c;也可能是最大的监控基础设施。我们在使用 Prometheus / Grafana 堆栈进行监控方面有一些经验&#xff0c;了解到希望创建…

得帆信息创始人-张桐,受邀出席 BV百度风投AIGC主题论坛

近日&#xff0c;得帆信息创始人兼CEO张桐&#xff0c;作为百度风投被投代表企业创始人受邀出席“向未来&#xff0c;共成长” BV百度风投AIGC主题论坛。 与包括上海市徐汇区相关部门领导、百度集团相关事业部负责人及代表&#xff0c;以及来自国寿资本、中网投、麦顿投资的投资…

YOLOV5 数据集的划分的详细流程(超详细)

1 数据划分 标注好后的数据集分为两个文件夹&#xff0c;一个文件夹中放置图片另一个文件夹中放置了txt文件。将数据集按照一定的比例进行划分为训练集&#xff0c;测试集&#xff0c;验证集&#xff08;train、test、val&#xff09;&#xff0c;下述的代码中我按照了 8:1:1 的…

php-使用wangeditor实现富文本(完成图片上传)-npm

官网参考连接&#xff1a;快速开始 | wangEditor 样式&#xff1a; 一、新建一个临时文件夹test1和一个文件夹wangeditor 临时文件夹test1&#xff1a;临时存放通过npm下载的文件文件夹wangeditor&#xff1a;用于存放在临时文件夹test1拷贝的css和js 二、安装 editor 在确保有…

云原生之深入解析如何在K8S环境中使用Prometheus来监控CoreDNS指标

一、什么是 Kubernetes CoreDNS&#xff1f; CoreDNS 是 Kubernetes 环境的DNS add-on 组件&#xff0c;它是在控制平面节点中运行的组件之一&#xff0c;使其正常运行和响应是 Kubernetes 集群正常运行的关键。DNS 是每个体系结构中最敏感和最重要的服务之一。应用程序、微服…

去掉乘法的加法神经网络

AdderNet: Do We Really Need Multiplications in Deep Learning? 核心贡献 用filter与input feature之间的L1-范数距离作为“卷积层”的输出为了提升模型性能&#xff0c;提出全精度梯度的反向传播方法根据不同层的梯度级数&#xff0c;提出自适应学习率策略 研究动机 加…

1.[BUU][极客大挑战 2019]EasySQL1

1.了解万能密码--》1 or 11# 这是一种常见的SQL注入攻击。在一个SQL查询中&#xff0c;这个语句会导致条件始终为真&#xff08;11&#xff09;&#xff0c;因此查询会返回所有的结果。#表示注释&#xff0c;可以用来结束SQL语句&#xff0c;防止后续的部分执行。因此&#xf…

HTML_CSS的基本选择器的使用及其作用范围和优先级

目录 ✨CSS的使用&#xff1a;行内样式内部样式外部样式 ✨CSS基本选择器&#xff1a;id选择器class选择器标签选择器 ✨优先级&#xff1a;选择器的优先级样式表的优先级 ✨CSS的使用&#xff1a; 根据定义CSS的位置不同&#xff0c;分为行内样式、内部样式和外部样式 行内样…

【目标检测】视频输出体积太大?分析视频的编码与码率问题

在做视频目标检测时&#xff0c;发现一个问题&#xff0c;检测输出完的视频时大时小&#xff0c;有时输出体积过大&#xff0c;造成播放器播放时严重卡顿现象。本文就这一情况进行分析&#xff0c;并就该问题提出相关解决方案。 视频基础知识 隔行扫描和逐行扫描 早期电视台在…