字符串与字符数组的区别在过去我一直是处于一知半解的状态,字符串是通常被认为是常量,是保存在一段固定的内存中的,这段内存是以'\0'为结束符,这段内存通常只能通过一个指针来找到。字符数组其实和其他数组没什么区别,只是保存的数据类型是字符类型(char),它没有强制要求最后的元素是否是'\0'。字符数组的数组名是指向第0个字符的指针,而不是指向这个字符串的。这与我前期博客中对数组的分析结论是相同的,数组名并不像指针存在一个地址来保存指针名,数组名可以看做是汇编程序中的一个标号,并没有专门的地址来保存数组名。
但是字符串和字符数组又有很多相似的地方,特别是当我们结合指针操作字符数组时候,就会导致错误的产生,我觉得只有搞清楚一些概念就能较好的避免这种错误的产生。这个概念就是指针就是指针,数组就是数组,两个搞混只是因为某些巧合使我们误以为指针和数组是等值的。两者实质上是不相同的。
字符数组和字符串的定义如下:
1. //字符串
2. char *str = "string is a constant";
3. //字符数组
4. char strarray[] = {'s','t','r','i','n','g',' ','i','s',' ','a',' ','c','o','n','s','t','a','n','t','\0'};
5. //或者
6. char strarray[] = "string is a constant";
7. char strarray[] = {"string is a constant"};
其中从上面的定义可以知道字符串实质上一块内存,其中保存的值是常量,常量其实就是不能采用程序对内存中的值进行修改,当然只是程序不能,我认为就像嵌入式C语言中的const类型一样,虽然我们通常认为这个变量是常量,但实质上是只读性变量,只是不能通过程序修改,还可以通过其他的方式修改。这时候str指向的内存空间中保存的数据是不能被程序修改的,也只能通过str指针对这段内存进行访问。
如果字符数组定义成单个的字符,我们也许还能较好的分别,但如果是如第二种定义、第三种形式定义,我们可能就会产生很大的不理解,这与字符串的类型基本上没有什么差别,只是多了一个数组符号[ ],但是这个数组符号就意味着数据类型的改变,我们可以知道数组中的值是可以改变的,不是常量,说明strarray是一个数组变量,而str却是一个字符串常量指针。Strarray是一个标号,存储器中并没有专门分配内存空间存储strarray的值。但是指针和数组名很多时候的一些相似性使得我们在处理的时候很难去判断,比如下面这段代码:
char * str = "Constant String";
char strarray[] = "Constant String";
int str_len,size_str,array_len,size_array;
str_len = strlen(str);
array_len = strlen(strarray);
size_str = sizeof(str);
size_array = sizeof(strarray);
size_str = sizeof(*str);
如果知道其中的一些数组与指针的一些区别可以较好的得到这些值,首先strlen(str)是指求解字符串的长度,但是不包含'\0',因此str_len就是字符串的长度,也就是str_len = 15,array_len也是求解字符串长度的值,我们前面已经指出数组和字符串存在差别,但为什么还可以这样做呢。我们暂且认为包含'\0'的字符数组可以看做是字符串或者伪字符串,两者非常的相似,都是存在一段内存,保存一段数据,格式也非常的相似,所以strlen认为两者是相同的,但实质上还是存在差别的,但是采用strlen求解字符串长度时,array_len和str_len 是相同的。
size_str 和size_array这两个变量的值是我写这篇文章的初衷,因为我之前根本没有起分析其中的一些差别,理所当然的认为两者是相同的,但细细比较发现两者确实存在问题。size_str = sizeof(str)的值是多少?sizeof是指对变量或者类型求解存储空间,是C语言中不被重视的关键字,被大多数人认为是一个函数,实质上是一个关键字。这样我们就能比较快速的确定后面三个变量的值啦,size_str = sizeof(str)是指对str的存储器长度进行计算,str是一个字符型指针,我们知道,指针类型的变量实质上就是一个地址,在32机中,地址通常需要4个字节的内存空间来存储,因此,size_str = 4;知道了sizeof的意义,size_array就比较简单了,strarray是一个数组变量,是一段内存空间的标示,sizeof(strarray)是指上就是求解数组的内存大小,这时候的内存大小包含'\0',因此size_array的值应该是15+1=16。而接下来的size_str = sizeof(*str)就更加简单了,因为我们知道对于字符串指针而言,指向的只是字符串的第0个元素,而不是整个字符串,这时候又会和数组的数组名有一定的交叉,数组的数组名我们通常被认为是一个常量指针(左值右值的差别),也就是指向的起始地址是不变的,但是通常被认为是指向数组首个元素的常指针,不能进行++,--操作。接着说明sizeof(*str),*str是指指向字符串的第0个元素的指针的值,也就是说*str是指上就是字符串的第0个元素,也就是一个字符类型,字符类型的大小也就是1字节了,因此这时候的size_str = 1。
上面的分析说明了在字符串和数组的相关操作中使用strlen的安全性总是比sizeof好,特别是不清楚sizeof的一些意义的情况下。
通过上面的分析我们应该字符串和字符数组还是存在很多差别的,但是只是理论上的分析,接下来采用程序进行测试。这个程序主要检测字符串是否为常量,而字符数组是可以修改的。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFS 20
int main()
{
char s[] = {"This is just a test!!"};
char d[] = {"Hello String strncpy!!"};
char *str = "String constant!";
char *str1 = "Testing!!";
/*不能采用定义指针的情况下就进行字符串的操作,
*需要分配一定长度的空间,
*因为没有内存空间不能操作
*/
char *p = malloc(BUFS * sizeof(char));
/*修改字符数组*/
strncpy(p,s,strlen(s));
printf("p = %s\n",p);
printf("d = %s\n",d);
strncpy(d,s,strlen(s));
printf("d = %s\n",d);
strncpy(d,s,strlen(d));
printf("d = %s\n",d);
/*修改字符串*/
printf("Test the string constant\n");
printf("Constant String str = %s\n",str);
strncpy(str,d,strlen(str));
printf("p = %s\n",p);
printf("d = %s\n",d);
strncpy(d,s,strlen(s));
printf("d = %s\n",d);
strncpy(d,s,strlen(d));
printf("d = %s\n",d);
/*修改字符串*/
printf("Test the string constant\n");
printf("Constant String str = %s\n",str);
strncpy(str,d,strlen(str));
printf("Constant String str = %s\n",str);
return 0;
}
编译以后出现的效果如下:
[gong@Gong-Computer c_languange]$ gcc -g strncpytest.c -o strncpytest
[gong@Gong-Computer c_languange]$ ./strncpytest
p = This is just a
d = Hello String
d = This is just a
d = This is just a
Test the string constant
Constant String str = String
Segmentation fault (core dumped)
结合上面的代码可以知道关于字符数组的操作都顺利的完成了,但是字符串的修改却没有正常的完成,出现了
异常(Segmentation fault (core dumped)),实质上就是因为字符串是不能修改的,导致了问题的抛出。据说在ANSI C中规定对字符串的操作会导致不确定的结果,这也说明了字符串是不能进行修改的。
通过上面的分析,数组和指针还是存在非常大的差别的,在字符串的操作过程中这种差别更加的明显,因此在实际写代码的过程中要时时注意这些小的差别,只有铭记一些概念才能真正的写好代码。
总结:
字符串是一个常量,不应该被修改,修改会出现错误,字符数组可以进行修改,因为数组中的值并不是不变的。在关于字符串的操作中,最好是采用首先将字符串赋值给字符数组,然后通过指针的方式访问进行修改等相关操作,而不能直接对字符串指针进行修改操作。
数组是一段内存的标示,指针只是保存了一个地址值。