zoukankan      html  css  js  c++  java
  • 详解C语言字符串

    1、定义字符串

    C语言本身没有string类型,通常使用char数组来表示字符串。常用的定义字符串的方式有:

    char str1[] = {'C', 'h', 'i', 'n', 'a'};
    char str2[] = "China";
    char* str3 = "China";
    
    1. 与Java语言不同,C语言中数组的括号是在变量名的后面。第一条定义字符串的语句等价于Java语言中:char[] str1 = {'C', 'h', 'i', 'n', 'a'};

    2. [] 中可以填入数字,表示char数组的长度。但是,中括号中的数字必须大于等于右边字符串数组的长度。

      • char str1[5] = {'C', 'h', 'i', 'n', 'a'}char str1[6] = {'C', 'h', 'i', 'n', 'a'}, ... , char str1[100] = {'C', 'h', 'i', 'n', 'a'} 都是合法的。
      • char str2[6] = "China";char str2[7] = "China";, ... ,char str2[100] = "China"; 都是合法的。
      • char str2[5] = "China";,如果 str2 括号内填写的数字小于 6 就会编译出错,出现数组界限溢出错误。

    2、计算char数组的长度

    在Java中,数组是看作对象类型的,数组有通用的属性 length,比如 str1.length 可以获取数组的长度。但是 C 语言可没有这么“友善”,想获取char数组的长度:

    char str1[] = {'C', 'h', 'i', 'n', 'a'};
    int len = sizeof(str1) / sizeof(char);  // 这句话计算字符串数组的长度
    printf("%d", len);
    
    1. sizeof 是C语言中保留关键字,用来计算数据类型的“宽度”。用 sizeof 可以获得数据类型变量在内存中所占的字节数。同样,用 sizeof 也可以获得整个数组在内存中所占的字节数。

    2. 因为数组中每个元素的类型都是一样的,在内存中所占的字节数都是相同的,所以 总的字节数 除以 一个元素所占的字节数 就是数组的长度。

    3. sizeof(char) 表示数据类型 char 在内存中所占的字节数,在C语言中,char占一个字节,所以 sizeof(char) == 1

    4. 我这里说的字符串数组的长度,说到底还是数组的长度。

    3、字符串必须以结尾。如果不是,就会出现乱码

    char str1[] = { 'C', 'h', 'i', 'n', 'a' };
    printf("%s
    ", str1);
    
    1. 上面这段代码在 Windows 上,用 Microsoft Visual Studio 运行时,会输出乱码。如图所示:

    2. C语言是一直读取到字节0作为字符串的结尾。Java则可以根据数组长度属性length,来读取指定长度的字符串。

    3. 所以,正确的定义方式应该是 char str1[] = { 'C', 'h', 'i', 'n', 'a', ''};,此时字符串数组的长度是 6。 char str1[] = { 'C', 'h', 'i', 'n', 'a', 0 }; 这样定义也是可以的,但是更推荐前一种。

    4、定义一个空的字符串数组

    char str0[100] = {0}; // 用这条语句定义一个空的字符串数组
    int len0 = sizeof(str0) / sizeof(char); 
    printf("%d", len0); // 数组长度为100
    
    1. 第一条语句就等同于Java中的char[] str0 = new char[100];

    2. 第一句语句也可以写成 char str0[100] = {};,这样写也是正确的。但是,char str0[] = {} 这样写不行,空初始值设定项对于未指定绑定的数组无效!

    5、计算字符串的长度 strlen

    与字符串数组的长度不同,字符串的长度是不把字节0算进去的。strlen 函数可以返回字符串的长度:

    size_t strlen(const char* str);
    

    然后来看几个实验:

    char str1[] = {'C', 'h', 'i', 'n', 'a'};
    char str2[] = "China";
    char* str3 = "China";
    char str4[] = {'C', 'h', 'i', 'n', 'a', ''};
    printf("%d
    ", strlen(str1)); // 输出结果为19
    printf("%d
    ", strlen(str2)); // 输出结果为5
    printf("%d
    ", strlen(str3)); // 输出结果为5
    printf("%d
    ", strlen(str4)); // 输出结果为5
    
    1. 其中,str1 所表示的字符串因为没有以 '' 结尾,所以,strlen 把内存中一直到 结尾的字节都看作字符串的内容,所以长度为 19。

    6、字符串常量不可修改

    我们先来看一段程序:

    char str0[] = {'c', 'h', 'i', 'n', 'a', ''};
    char str1[] = "china"; // 在栈上,因为重载了=操作符
    char* str2 = "china"; // china在常量区,str1在栈上
    
    str0[0] = 'C';
    str1[0] = 'C';
    //str2[0] = 'C';
    //*str2 = 'C';
    
    printf("%s
    ", str0);
    printf("%s
    ", str1);
    printf("%s
    ", str2);
    
    1. 运行结果是,str0[0] = 'C'str1[0] = 'C' 这两句可以正常运行。但是 str2[0] = 'C'*str2 = 'C' 会报错。

    2. char str1[] = "china"char* str2 = "china" 字符串存放的内存区域不同:前者存放在栈中,可以修改;后者存放在常量区,不可修改。

    3. 0xC0000005:安全点就是通过这个技术暂停所有线程的。

    7、拷贝字符串strcpy

    C语言 strcpy() 函数用于对字符串进行复制(拷贝)。

    头文件:string.h

    语法/原型:

    char* strcpy(char* strDestination, const char* strSource);
    

    参数说明:

    • strDestination:目的字符串。
    • strSource:源字符串。

    strcpy() 会把 strSource 指向的字符串复制到 strDestination。

    必须保证 strDestination 足够大,能够容纳下 strSource,否则会导致溢出错误。

    返回值:目的字符串,也即 strDestination。

    示例:

    #include <string.h>
    
    int main() {
      char dest[] = {0};
      char src[] = "Hello, Han Meimei!";
    
      strcpy(dest, src);
    
      printf("%s", dest);
      return 0;
    }
    

    8、连接字符串strcat

    头文件:string.h
    语法:

    char* strcat(char* strDestination, const char* strSource);
    

    参数说明:

    • strDestination:目的字符串;
    • strSource:源字符串。

    strcat() 函数把 strSource 所指向的字符串追加到 strDestination 所指向的字符串的结尾,所以必须要保证 strDestination 有足够的内存空间来容纳两个字符串,否则会导致溢出错误。

    注意:strDestination 末尾的会被覆盖,strSource 末尾的会一起被复制过去,最终的字符串只有一个。

    返回值:指向 strDestination 的指针。

    #include <string.h>
    
    int main() {
      char dest[] = "Hello";
      char src[] = " World!";
    
      strcat(dest, src);
    
      printf("%s", dest);
      return 0;
    }
    

    8、比较字符串strcmp

    C语言 strcmp() 函数用于对两个字符串的内容进行比较(区分大小写)。

    头文件:string.h

    语法/原型:

    int strcmp(const char* stri1,const char* str2);
    
    • 参数 str1 和 str2 是参与比较的两个字符串。

    strcmp() 会根据 ASCII 编码依次比较 str1 和 str2 的每一个字符,直到出现不到的字符,或者到达字符串末尾(遇见)。

    返回值:

    • 如果返回值 < 0,则表示 str1 小于 str2。
    • 如果返回值 > 0,则表示 str2 小于 str1。
    • 如果返回值 = 0,则表示 str1 等于 str2。

    示例:

    #include <string.h>
    
    int main() {
      char str1[] = {'C', 'h', 'i', 'n', 'a', ''};
      char* str2 = "China";
    
      int result = strcmp(str1, str2);
      printf("%d", result); // 输出结果为0
      return 0;
    }
    

    9、字符查找函数strchr

    C语言 strchr() 函数用于查找给定字符串中某一个特定字符。

    头文件:string.h

    语法/原型:

    char* strchr(const char* str, int c);
    

    参数说明:

    • str:被查找的字符串。
    • c:要查找的字符。

    strchr() 函数会依次检索字符串 str 中的每一个字符,直到遇见字符 c,或者到达字符串末尾(遇见)。

    返回值:返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符 c 则返回 NULL。

    示例:

    #include <string.h>
    
    int main() {
      char str1[] = {'C', 'h', 'i', 'n', 'a', ''};
      printf("%p
    ", str1);
    
      char* p1 = strchr(str1, 'b');
      printf("%p
    ", p1); // 输出结果为0,str1中不包含字符b
    
      char* p2 = strchr(str1, 'h');
      printf("%p
    ", p2); // 输出结果比 str1 的内存地址多一个字节
      return 0;
    }
    

    运行结果如下图所示:

    10、查找子字符串strstr

    C语言 strstr() 函数在字符串中查找子字符串。

    语法/原型

    char *strstr(const char *haystack, const char *needle)
    

    参数说明:

    • haystack -- 要被检索的 C 字符串。
    • needle -- 在 haystack 字符串内要搜索的小字符串。

    返回值
    该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。

    示例:

    char haystack[20] = "RUNOOB";
    char needle[10] = "NOOB";
     
    char *ret = strstr(haystack, needle);
     
    printf("子字符串是: %s
    ", ret);
       
    return 0;
    

    运行结果:

    参考文档

    1. Go to 《数组的长度,C语言获取数组长度详解》
  • 相关阅读:
    java-以月为单位,得到一年中某一个月份的范围
    计算两个时间段相差几个月(包含相差的哪些月份)
    单个进程最大线程数
    Dell PowerEdge R720内存安装原则
    Java [parms/options] range -b 100 -c 10 -i 100 -t 300 -s 180
    PhysicalDrive
    classpath和环境变量设置
    MySQL正则表达式
    MySQL模式匹配(LIKE VS REGEXP)
    ubuntu为什么没有/etc/inittab文件? 深究ubuntu的启动流程分析
  • 原文地址:https://www.cnblogs.com/kendoziyu/p/14846638.html
Copyright © 2011-2022 走看看