1.首先看一下 字符串 和字符数组中的细微区别:
1 。定义
- 字符串是最后一个字符为NULL字符的字符数组。字符串一定是字符数组。
- 字符数组,完整地说叫字符类型的数组。字符数组不一定是字符串。(字符串最后一个元素必须为NULL)
- 字符数组的长度是固定的,其中的任何一个字符都可以为NULL字符。
- 字符串只能以NULL结尾,其后的字符便不属于该字符串。
- strlen()等字符串函数对字符串完全适用,对不是字符串的字符数组不适用。
- sizeof()对于字符串得到的是指针类型的长度(为4)对于字符数组得到的是字符数组的长度。
由于C语言中没有字符串类型,所以用char数组代字符串类型,定义字符数组的方式有两种,下面来看它们的区别:
方式一:以单字符形式
要声明初始化一个可存储四个有效值的字符数组,形式如下:
char charArray[4]={'1','2','3','4'};
方式二:以字符串形式
要声明一个可存储四个有效值的字符数组,形式如下:
char stringArray[5]={"1234"}或"1234";//,字符串数组大小要声明为5,因为要留出stringArray[4]给编译器自动存储'\0',如若声明初始化为:char stringArray[4]="1234",报错
看一个例子:
#include <stdio.h>
#include <string.h>
int main(void)
{
//这是字符数组赋初值的方法
char s1[]= {'T','a','o','T','a','o','s' ,'b'};
//char s1[]= "ljsgjlsgjslgjslgj";
//这是字符串赋初值的方法
char s2[]= "taotaosb";
//用sizeof()求长度
printf("s1's length is %d\n", sizeof(s1)); //长度为8
printf("s2's lenght is %d\n", sizeof(s2)); //长度为9,最后一位自动添加了 一个'\0'
//用printf的%s打印内容
printf("s1=%s\n", s1); //不能正确显示
printf("s2=%s\n", s2); //可以正确显示
printf("%x\n",s1);
printf("%x\n",s2);
printf("s1' length is %d\n", strlen(s1)); //不正确的结果
printf("s2's length is %d\n", strlen(s2)); //NULL不在计算范围
return 0;
}
结果:
sizeof()运算符求的是字符数组的长度,而不是字符串长度。strlen()函数求的是字符串长度,而不是字符数组。它不适用于字符串以外的类型。strlen()函数会对你传进来的指针 往后查找,一直找到 '\0' 才结束。二sizeof 是计算字符数组的长度,如果传进去普通的字符指针的话会得不到我们想要的结果。(不会出现编译错误的,根据计算机位数的不同结果可能不同。)所以用的时候要 特别小心。可以看下面的例子。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char a[30];
char *b = (char *)malloc(20 * sizeof(char));
printf("%d\n", sizeof(a)); //30
printf("%d\n", sizeof(b)); //4
printf("%d\n",sizeof(&a[3])); //4
printf("%d\n", sizeof(a[3])); //1
printf("%d\n", sizeof(b+3)); //4
printf("%d\n", sizeof(*(b+4))); //1
return 0 ;
}
结果:
可以看出来,你传进去一个 指向char类型的指针的时候,返回结果是4,因为是32位的。
字符串数组是二维字符数组,同样道理,二维字符数组不一定是字符串数组。
2.字符串数组是二维字符数组,同样道理,二维字符数组不一定是字符串数组。(和一维的类似)
看一下例子:
#include <stdio.h>
#include <string.h>
int main(void)
{
int i;
//字符数组的一种赋值方法
char s1[][2] = {{'S','B'},
{'A','B'},
{'d','A'},
{'s','\0'},};
//用字符串对字符数组进行赋值的 一种方法
char s2[][3] = {"SB","AB","dA","s"};
for(i = 0;i < 4;i++)
{
printf("i = %d ,s1[i] =%d , s2[i]= %d\n",i,sizeof(s1[i]),sizeof(s2[i]));
printf("s1[i] = %s s2[i] = %s\n", s1[i], s2[i]);
}
}
结果:
因为多了一个NULL字符,所以 S2字符数组比S1 要多了一个1。
输出S1
时,因为中间没有NULL字符,所以一直显示到有NULL为止。S2因为每一行都有一个'\0' 结尾。所以打印正常。
3.字符指针指向字符串 和 字符数组的
eg1:
char* s=”hello”;
s[0]='a';//wrong!运行时显示为“段错误”。
运行的时候 “hello” 是常量字符串,存放在常量存储区。该区的值是只读的。任何试图修改该存储区值的都会判定为错误。 s指针 指向 常量存储区的 这个字符串。所以你试图改变 值会出现段错误。而你定义字符数组,可以对字符数组修改的。看下面的例子:
#include <stdio.h>
#include <string.h>
int main()
{
char *s1 = "hello";
char s2[] = "hello";
//s1[0] = 'a'; 这句话是错误的
s2[0] = 'a';
printf("s1= %s\n",s1);
printf("s2= %s\n",s2);
return 0;
}
运行结果:
S2在运行的时候在栈中 专门 分配了内存空间给s2数组的,所以可以对其中的元素 进行修改。在看下一个区别:
2作为返回值时不同。
char* test(){
char ch[5]={'h','i'};//gcc 会警告:返回局部变量。
return ch;
}
ch指向在函数中分配的内存,这片区域在函数执行过后会释放的,如果你去访问的话会造成不可预期的后果。例子:#include <stdio.h>
#include <string.h>
char *test()
{
char ch[10] = "hello";
//char *s = "hello";
return ch;
}
int main()
{
char *s = test();
printf("s=%s\n",s);
return 0;
}
执行结果:
可以看到一堆乱七八糟的东西。所以 不可以这样用。但是可以这样用:
char *test()
{
// char ch[10] = "hello";
char *s = "hello";
return s;
}
s是指向 常量区的东西。这篇常量区的东西,在整个程序执行完之前使用 是应该没问题的。所以 打印结果是正常的:
下面在来一个例子,你可以看出来下面的错误吗~
#include <stdio.h>
#include <string.h>
void hehe(char *str) //转小写
{
int i=0;
while(str[i]!='\0')
{
if(str[i]>='A' && str[i]<='Z') str[i]+=32;
i++;
}
str[i]='\0';
printf("%s\n",str);
}
int main()
{
char s[] = "TAOTAOSB";
hehe("taotaosb");//第1 中传递方法,
hehe(s); // 第2中 传递方法。
return 0;
}
OK,差不多就这么多了,如有错误 欢迎 指正。非常感谢~
2.看一个strcpy的问题#include <iostream>
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[] = "123456789";
char d[] = "123";
strcpy(d,s);
printf("%s ,\n %s",d,s);
return 0;
}
#include <iostream>
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[] = "123456789";
char d[] = "123";
strcpy(d,s);
printf("%s ,\n %s",d,s);
return 0;
}
看一下打印的结果:
这里可以看到 s的地址是在 高地址,d的地址是在低地址上。而且正好相差4个字节,符合下面我的推论。
首先 strcpy中 源字符串的参数 是const char * 类型的,但是这里 发生了变化。
原因个人认为是这样的:(水平有限,有错误请指出,感谢~~)
函数在运行的时候:编译器会自动分配一个 栈区,用来存放函数中用到的参数值以及 局部变量的值。操作方式类似数据结构中的栈。
这里 会首先给 s数组 分配 10个字节的内存空间(包括结束符) ,然后会给 d数组 分配4个字节的 内存空间,不过 这 两个 数组的内存空间是紧挨着的 。栈的 内存空间是从高地址向低地址扩展的,那么 这两个参数在栈中的排序 是这样的:
低地址 <<<----------------高地址
1 2 3 \0 1 2 3 4 5 6 7 8 9 \0
| |
d数组 s数组
前面4个 字节是d数组的,后面的10个字节是s数组的,发生复制函数之后,栈中内存位置上的变化为:
低地址 ----------------->>>高地址
1 2 3 4 5 6 7 8 9 \0 6 7 8 9 \0
| |
d数组 s数组
所以打印d是123456789(低地址到高地址)
打印s是56789 (遇见\0打印结束)
所以我们在使用 strcpy的时候一定要注意 :目标字符串 留足够的空间来复制