(C/C++学习)15.C语言字符串和字符数组

说明:在C语言中字符串和字符数组有很多相似之处,却又有着一些不同。本文将针对其区别与联系,进行分析总结。


一.字符串

1.在C语言中,字符串是由双引号括起来的任意字符序列,如:“china”,”america”等。


2.很显然,一个字符占一个字节,那么 “china” 应该占5个字节,但这样想就错了。其实在上篇文章中提到过了,在生成一个由双引号引起的字符串时,系统会自动在其后面追加一个 ‘\0’ ,这个杠0是相当重要的,可以说是字符串的重要依据,它是字符串的结束标志。字符串的很多处理函数,如上篇文章提到的,都是依据这个杠0工作的。所以在这里如果打印 “china” 的大小,应该是 6 。需要注意的是,空串 “” 的大小是1,可以理解为只有一个 ‘\0’

  1     printf("%d\n",sizeof("china"));
  2     printf("%d\n",sizeof(""));
  3     //打印结果为6、1


3.C 语言将常量字符串处理为一个指向数据段中一段字符串的字符指针或字符数组。我们可以将它付给一个 char * 的指针或 char型的数组。

  1     char *p= "abcdef";
  2     //C++中类型检查严格,将const char *赋给 char* 会有警告
  3     char arr[6] = "abcde";


4.字符串,准确说是字符串常量,因为其是不可以修改的。普通变量通常存储在代码段,而字符串常量则存储在数据段的只读数据段 (RO段) ,这也意味着字符串常量是不可修改的。如运行以下代码,程序将挂掉。

  1     char *p= "abcdef";
  2     p[1] = '0';


二.字符串和字符数组

数组前面已经说过了,字符数组也是其中的一种,只不过数组中的每个元素为字符型的。这里就直接说字符串与字符数组的关系。


1.相同之处:下面将以一段代码加以说明

  1     char *p= "abcde";
  2     char arr[] = "abcde";
  3     char *pp = p;
  4     int i = 0;
  5     printf("%s\n",p);
  6     printf("%s\n",arr);
  7     //打印字符串和字符数组
  8 
  9     printf("%d ",sizeof("abcde"));
 10     printf("%d\n",sizeof(arr));
 11     //打印字符串和字符数组的大小
 12 
 13     while(*pp)
 14         printf("%c ",*pp++);
 15     while(arr[i] != 0)
 16         printf("%c ",arr[i++]);
 17     //分别单独访问各自的元素
程序运行结果:
  1 abcde
  2 abcde
  3 6 6
  4 a b c d e a b c d e

由上述结果可知,在某种情况下(下文将说明),字符串和字符数组是等价的,尤其是程序最后几行,通过单独打印字符串和字符数组的每个元素,更是可以得知两者末尾都有一个 ‘\0’ 存在。那么,在什么情况下二者不等呢?


2.不同之处

(1.)前面已经说过了,字符串是一个常量,不可修改,但字符数组却不是这样的。你可以完全通过下标法对任意一个元素进行修改。

  1     char arr[] = "abcde";
  2     arr[0] = '1';
  3     arr[3] = '5';
  4     printf("%s",arr);


(2.)二者等价条件,及前面提到的某种情况

一个没有 ‘\0’ 作为结尾标识符的字符串不叫字符串,而字符数组中元素的存储并不会像字符串那样自动追加 ‘\0’ ,因此,字符数组和字符串等价的条件便是 ‘\0’ 的拷贝问题。一个正常的字符串,其末尾必定以  ‘\0’ 结尾,如 “china” ,虽然杠0 并没有显示出来,但我们应该明确的知道其末尾有一个杠 0 的存在,这点,在前面通过打印字符串的大小也可以证明。因此,在初始化字符数组的时候,其大小应该总是大于等于字符串的大小,这样以便于将字符串末尾的 ‘\0’拷贝到数组。如以下代码,其中 n 应该大于等于 sizeof(“abcde”) = 6。

  1 char arr[n] = "abcde";


(3.)越界情况

当数组的大小小于字符串的大小时,由于 ‘\0’没拷贝到数组中,因此,对数组的打印可能会发生越界行为,产生不确定结果。

  1     char arr[5] = "abcde";
  2     printf("%s",arr);

在笔者电脑上输出结果为:

  1 abcde?6


(4.)最优做法

利用数组可以省略大小的特点,依据数组的大小自适应。这样也会避免浪费空间。

  1     char arr[] = "china";
  2     printf("%s\n",arr);


三.字符数组的拓展

既然字符数组可以存储任意字符元素,那么万一字符数组在前面元素中就已经出现了 ‘\0’ 而非最后一个元素是 ‘\0’ 呢?

  1 #include<stdio.h>
  2 #include<string.h>
  3 int main()
  4 {
  5     unsigned int i = 0;
  6     char arr[] = "c0hi0n\0a ";
  7     printf("%s\n",arr);
  8     printf("%d\n",sizeof(arr));
  9     printf("%d\n",strlen(arr));
 10     for(;i<sizeof(arr);i++)
 11         printf("%x ",arr[i]);
 12     return 0;
 13 }

程序运行结果:

  1 c0hi0n
  2 10
  3 6
  4 63 30 68 69 30 6e 0 61 20 0
由以上测试可知,数组只是一个存储元素的构造类型,其中的元素可以是任意的,打印数组大小时,只会跟元素的多少以及元素的类型有关,与其中元素是不是杠 0 无关。而当用 strlen()来判断该数组的长度时,由于其判断标识为 ‘\0’ ,因此不能正确得到数组的实际长度,只能得到 ‘\0’前面元素的长度。而对整个数组以字符串的格式打印时,也是同样的道理。只有当对字符数组的元素挨个单独打印,才能见到其真貌。上述代码是以十六进制 ASCII 对数组元素进行打印的。需要注意的是:0 并不是 ‘\0’,应该加以区分。
posted @ 2018-10-23 11:13  退後。  阅读(415)  评论(0编辑  收藏  举报