zoukankan      html  css  js  c++  java
  • 字符串与字符串函数

    一、表示字符串与字符串I/O

      【字符串 - 以空字符()结尾的 char 类型数组】

      1、字符串字面量(字符串常量)

      用双引号括起来的内容称为字符串常量(string literal),也叫做字符串常量(string constant)。双引号中的字符和编译器自动加入末尾的 字符,都作为字符串存在内存中。

      ANSI C 标准规定,如果字符串常量之间没有间隔,或者用空白字符分隔,C 会将其视为串联起来的字符串常量。

    char chr1[50] = "Hello, and""how are" " you"
                         " today!";
    char chr2[50] = "Hello, and how are you today!";    //chr1 与chr2 中存储的内容一致

      【如果要在字符串内部使用双引号,必须在双引号前面加上一个反斜杠()。】

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

      2、字符串数组的初始化

      定义字符串数组时,必须让编译器知道需要多少空间。

      可以在初始化数组的时候顺便填充数组,也可以先初始化,之后再填充。不管如何,初始化的时候,数组的大小对于编译器来说,是必须要确定的。

      3、数组与指针的区别

      初始化数组:把静态存储区的字符串拷贝到数组中;初始化指针:只把字符串的地址拷贝给指针。

    #define MSG "I'm special!"
    #include <stdio.h>
    
    int main(void) {
        char ar[] = MSG;
        const char * pt = MSG;
        printf("address of "I'm special!": %p 
    ", "I'm special!");
        printf("        address ar: %p 
    ", ar);
        printf("        address pt: %p 
    ", pt);
        printf("        address of MSG: %p 
    ", MSG);
        printf("address of "I'm special!": %p 
    ", "I'm special!");
        return 0;
    }

      

      通过上述的结果我们知道:

      第一,pt 和 MSG 的地址一样,而 ar 的地址不同;

      第二,虽然字符串自变量“I‘m special” 在程序的两个 printf() 函数中出现了两次,但是编译器只使用了一个存储位置,而且与 MAG 的地址相同,编译器可以把多次使用的相同字面量储存在一处或多处。也有的编译器会在不同的位置存储三个“I'm special”;

      第三,静态数据使用的内存与 ar 使用的动态内存不同,不仅值不同,特定编译器甚至使用不同的位数来表示两种内存。

    char heart[] = "Hello!";
    const char * head = "Hello!";

      首先,heart 是常量名,head 是变量。其两者之间的区别如下:

      第一,两者都可以使用数组表示法【heart[i] 、head[i]】;

      第二、两者都能进行指针加法操作【*(heart + 1) 、*(head + 1)】;

      第三、只有指针可以进行递增操作;

      第四、可以通过 heart 改变数组中元素的信息。

    二、字符串输入

      1、gets() 函数

    #define STLEN 50
    ...
    char words[STLEN];
    ...
    gets(words);    //典型用法
    ...
    puts(words);
    ...

      函数功能:读取整行输入直至遇到换行符,然后丢弃换行符,储存其余字符,并在这些字符的末尾添加一个空字符使其成为一个 C 字符串。经常和 puts() 函数配对使用【puts() 函数会在待输出字符串末尾添加一个换行符】。

      问题:gets() 函数只知道数组的开始处,并不知道数组中具体有多少个元素。如果输入的字符串过长,会导致缓冲区溢出(buffer overflow),即有多余的字符超出了指定的目标空间。如果这些多余的字符只是占用了那些尚未使用的内存,就不会立即出现问题;如果擦写程序中的其他数据,会导致程序异常终止;或者还有其他的情况。在 UNIX 系统中,“Segmentation fault”说明当前程序视图访问未分配的内存。

      【C99 、C11 标准不建议使用该函数】

      2、fgets() 函数

    #define STLEN 14
    ...
    char words[STLEN];
    ...
    fgets(words, STLEN, stdin);
    ...
    fputs(words, stdout);
    ...

      fgets() 函数与 gets() 函数之间的区别如下:

      第一,fgets() 函数的第 2 个参数指明了读入字符的最大数量,如果该参数是 n ,那么 fgets() 函数将读入 n - 1 个字符,或者读到遇到第一个换行符为止;

      第二,如果 fgets() 函数读到一个换行符,会把它储存在字符串中,gets() 函数会丢弃换行符;

      第三,fgets() 函数的第3个参数指明要读入的文件,如果读入的是从键盘输入的数据,则以 stdin (标准输入)作为参数,该标识符定义在 stdio.h 中;与 fputs() 函数配套使用【fputs() 函数的第 2 个参数指明其需要写入的文件,如果要在显示器上进行显示,应使用 stdout(标准输出)作为参数进行传递】【fputs() 函数不会在待输出字符串末尾添加一个换行符】。

    #define STLEN 10
    #include <stdio.h>
    
    int main(void) {
        char words[STLEN];
        
        puts("Enter string (empty line to quit):");
        while (fgets(words, STLEN, stdin) != NULL && words[0] != '
    ') 
            fputs(words, stdout);
        puts("Done.");
    
        return 0;
    }

      在上述的例子中,虽然字符串的长度被限制在了10以内,但是从结果上来看,长字符串的输入并没有受到明显的影响。

       【在程序中,fgets() 函数一次读入 STLEN - 1 个字符,即第一次只读入了“By the wa”,存储在 words 数组中的内容为‘By the wa’,接着 fputs() 函数打印该字符串,没有换行然后 while 循环进入下一轮迭代, fgets() 函数继续从剩余的输入中读取数据,即读入“y, the ge”,并存储为 y, the ge,接着 fputs() 函数在刚才打印字符串的这一行接着打印第2次读入的字符串;继续循环,直到读入最后的“tion ”,fgets() 将其存储为 tion ,fputs()函数将其输出,由于字符串中的 ,光标被移至下一行开始处。】

      【系统使用缓存 I/O,在用户键入 ENTER 键之前,输入的内容都会被存储在临时存储区(即,缓存区)中。键入 ENTER 键之后,在输入缓存区中增加一个换行符,并把整行输入发送给 fgets() 函数。fputs() 函数把字符发送给另一个缓冲区,当发送换行符时,缓冲区中的内容被发送至屏幕上。】

      【空字符与空指针:概念上来说,空字符(或‘’)是用于标记 C 字符串末尾的字符,其对应字符编码是0,由于其他字符的编码不可能是0,所以不可能是字符串的一部分;空指针(或NULL)有一个值,该值不会与任何数据的有效地址对应,通常,函数使用其返回一个有效地址表示某些特殊情况的发生,例如遇到文件结尾或未能按预期执行。空字符是整数类型,是一个字符,占一个字节,而空指针是指针类型,是一个地址,通常占4字节。】

      3、gets_s() 函数

      gets_s() 函数与 fgets() 函数类似,用一个参数限制读入的字符数。

    gets_s(words, STRLEN);

      gets_s() 与 fgets() 的区别:1 - gets_s() 函数只从标准输入中读取数据,所以不需要第 3 个参数;2 - 如果 gets_s() 函数读取到换行符,将丢弃换行符,而不是继续存储换行符;3 - 如果 gets_s() 函数读到最大字符数的时候都没有读取到换行符,则进行以下操作【1,把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直到读到换行符或文件结尾;2,返回空指针;3,调用依赖实现的“处理函数”(或自己选择的其他函数),可能会中止或退出程序。】。

      【总结:1 - 如果输入行未超过最大字符数,gets_s()、gets()、 fgets() 三函数没有太大区别,只不过 fgets() 函数会保留输入末尾的换行符作为字符串的一部分;2 - 如果输入行太长,gets() 函数并不安全,会擦写现有数据,存在安全隐患,gets_s() 函数虽然较为安全,但是并不希望程序的中止或退出。 】

      4、sacnf() 函数

      scanf() 函数更像是“获取单词”的函数,而不是“获取字符串”的函数。

      scanf() 函数的典型用法是读取并转换混合数据类型为某种标准格式。【输入行过长,可能也会导致 scanf() 函数读取溢出,但是,在 %s 转换说明中指定字段宽度可在一定程度上解决该问题。】

  • 相关阅读:
    记druid 在配置中心下的一个大坑: cpu 达到 100%
    常见java日志系统的搭配详解:关于slf4j log4j log4j2 logback jul jcl commons-logging jdk-logging
    HTML一片空白, 无法渲染: Empty tag doesn't work in some browsers
    再见:org.apache.catalina.connector.ClientAbortException: java.io.IOException: Connection reset by peer
    spring boot tomcat 打本地包成war,通过Tomcat启动时出现问题: ZipException: error in opening zip file
    Maven 错误:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project appservice-common: Fatal error compiling: 无效的目标发行版: 1.8
    Maven 错误 :The POM for com.xxx:jar:0.0.1-SNAPSHOT is invalid, transitive dependencies (if any) will not be available
    LocalVariableTable之 Slot 复用
    一些常见的Java面试题 & 面试感悟
    spring 2.5.6 错误:Context namespace element 'component-scan' and its parser class [org.springframework.context.annotation.ComponentScanBeanDefinitionParser] are only available on JDK 1.5 and higher
  • 原文地址:https://www.cnblogs.com/wyt123/p/11042397.html
Copyright © 2011-2022 走看看