欢迎关注AWP资讯
我们所说的字符串就是字符数组的一种,就如我们常见的"hello world!",字符串的使用在C语言中也是非常重要的,常常会遇到一些操作,如字符串的修改、拷贝、字符串长度等,在物联网的应用中也尤为突出,物联网应用中所用的模组,大多是需要使用AT指令的,这就需要对字符串的操作。
在字符数组或字符串的操作中,首先要理解字符串在内存中的存储,比如我们有一个"hello"字符串,我们用sizeof计算它的大小,我们可以看到它的大小为6,是6个字节,明明是5个字符,为什么是6个字节呢?我们打印出来看一下
#include<stdio.h>
int main()
{
char c[] = "hello";
peintf("size of c = %d\n",sizeof(c));
return 0;
}
我们打印出来的结果是6
明明里面是5个字符,为什么使用sizeof出来是6,因为在字符串的结尾还有一个字符’\0’,在字符串中默认是以NULL也就是\0为结尾的,所以这个字符串计算出来的大小是6。
定义一个数组char a[8];将hello拷贝到这个数组中,通过内存分布的图示来分析一下
a[0] = ‘h’;a[1] = ‘e’;a[2] = ‘l’;a[3] = ‘l’;a[4] = ‘o’;a[5] = ‘\0’
最后这里就是以’\0’结尾的,表示这个字符串结束了。这是一个语法规则,必须要记住的。
我们通过一段代码来看一下,没有’\0’结尾的字符串,打印出来是什么样的
#include <stdio.h>
int main()
{
char c[5];
c[0] = 'h';
c[1] = 'e';
c[2] = 'l';
c[3] = 'l';
c[4] = 'o';
printf("char = %s\nsizeof(c) = %d\n",c,sizeof(c));
return 0;
}
这里打印出来的结果就是数组定义的大小,并不是6,我在两个平台下进行验证结果,在linux中打印出来的结果字符串内容正常,在vc++6.0中打印出来的是乱码,这就说明软件运行时没有找到结束符。
我们给这个数组增加一个’\0’再来打印看,打印出来的字符串就正常了
#include <stdio.h>
int main()
{
char c[6];
c[0] = 'h';
c[1] = 'e';
c[2] = 'l';
c[3] = 'l';
c[4] = 'o';
c[5] = '\0';
printf("char = %s\nsizeof(c) = %d\n",c,sizeof(c));
return 0;
}
字符串的操作有很多操作函数,比如strlen计算字符串长度的,这里的长度不是计算占用空间的大小,这里是字符串的个数,我们来看一下hello的字符串长度
#include <stdio.h>
#include <string.h>
int main()
{
char c[] = "hello";
int len = strlen(c);
printf("sizeof(c) = %d\n",sizeof(c));
printf("lenth = %d\n",len);
return 0;
}
打印出来的结果如我们所料,strlen是计算字符数组中元素个数的,不包含结束符,这个一定要区分开,这也是面试中常见的一个知识点。
字符串的初始化方式有多种,我们上面看到的char c[6] = “hello”;这是一种,这是隐式的将’\0’添加到字符串中,还有一种是这样char c[6] = {‘h’,‘e’,‘l’,‘l’,‘o’,’\0’};这是显示地将结束符定义在数组中,并且数组大小要大于或等于大括号中的元素个数,数组在内存中是连续存储的,它的内存分布
c是这个数组的名称,数组元素在内存中的分布,第一个h的地址是100,每个元素占1个字节,整个数组的内存地址分布就如上图所示。
我们定义一个指针变量char * pc;内存示意,通常指针是4个字节,假设这里的地址是200
我们接下来写这样一段代码
char c[6] = {'h','e','l','l','o','\0'};
char * pc;
pc = c;
pc是指向字符的指针,实际上pc中的值是数组c的首元素地址100
这样我们就可以使用pc这个指针变量来操作数组了,比如想要修改数组的内容可以pc[0] = ‘w’,我们打印出c的数组内容就会改变,这里的pc是基地址,pc+i就可以指向数组的对应地址,通过解引用*(pc+i)得到数组内的具体元素。
这里我们要注意的是我们不能写成c = pc,这种是会编译报错的,我们同样不能对数组进行和指针类似的操作如c = c+1;但是可以对指针进行这种操作,比如pc++,这样就可以指向数组的下一个元素。对应地址变为101
我们通过一段代码加深一下,指针和数组之间的关系应用。
定义一个数组,通过调用打印函数将数组中的内容打印出来
#include <stdio.h>
void print(char *c)
{
int i = 0;
while(c[i] != '\0')
{
printf("%c",c[i]);
i++;
}
printf("\n");
}
int main()
{
char c[10] = "hello";
print(c);
return 0;
}
打印出来的结果如下,可以看到完整地将hello打印出来
接下来分析一下上面函数,在main函数中定义了字符串,我们写一个打印函数,将数组的首地址传入打印函数,传入函数的首地址的值就是对应的数组中的元素,通过判断是否到最后一个字符,决定是否结束打印。
我们在打印函数中还可以使用指针的方式来操作,打印函数修改为,我们得到的结果仍然是正确的,传入数组的首地址,遍历数组中的每个元素的地址,对地址中的值进行解引用,得到最终的字符串。
void print(char *c)
{
int i = 0;
while(*(c + i) != '\0')
{
printf("%c",*(c + i));
i++;
}
printf("\n");
}
我们还有一种写法
void print(char *c)
{
while(*c != '\0')
{
printf("%c",*c);
c++;
}
printf("\n");
}
得到的结果同样是准确无误的。
我们在讨论指针的时候,要清楚指针它在内存中的分布,这样才能更好应用指针的一些操作。
在定义字符串的时候,我们使用数组定义,它是被分配在栈上的
char c[10] = "hello";
如果我们使用指针定义字符串,它是被分配在常量区的,是不可修改的。
char *c = "hello";
如果不允许字符串修改的话我们可以在print函数中形参中加上const
void print(const char *c)
{
while(*c != '\0')
{
printf("%c",*c);
c++;
}
printf("\n");
}