一、字符
字符是用单引号 ’ ’ 括起来的单个普通字符或转义字符。
例:char c='A'; char c='\n';
二、字符串
在 C 语言中,字符串实际上是使用 null 字符 ‘\0’ 终止的“一维字符数组”(本质!)。
三、字符数组
下面的声明和初始化创建了一个 “Hello” 字符串。
例子: char A[10];
//字符数组的赋值方法,三种
赋值: char A[10] = {'h','e','l','l','o'}; --> 后面没有赋值的成员的值都是0
char A[10] = {"hello"};-->等价于{'h','e','l','l','o','\0'} (本质)
char A[10] = "hello";
注意:
1)整型不需要使用任何符号,字符使用单引号’’,字符串使用""。
2)重点:字符数组初始化后不能整体赋值!
四、字符指针
整型指针 --> 指向内容: int
二级指针 --> 指向内容: 指针
数组指针 --> 指向内容: 数组
函数指针 --> 指向内容: 函数
字符指针 --> 指向内容: char
例子:
char a = ‘h’;
char *p = &a; --> p就是字符指针 --> 指向某个字符
char *p = "hello"; --> 不是把整个字符串拷贝到指针变量,指针变量永远只能存放指针/地址
五、char*p="hello"和char p[]="hello"的区别
首先“hello”在程序开启的时候放在了内存的.rodata段,是一个常量区,是一个only-read只读区域。
char *p=“hello”是将’h’的地址赋给指针p,即p指向了常量区‘h’的地址。
char p[]="hello"是将常量区中的"hello"拷贝到数组p[]中。
其在内存中的情况如下:
例:
//例如,我们试图将字符串的'h'改为'k',其结果如上图
char *p="hello";*p=‘k’;//错误,p指向常量区.rodata 'h'的地址,常量区的内容只读,无法修改;
char p[]="hello";*p=‘k’;//正确,p[]在堆栈开辟连续内存并把"hello"从常量区复制过来;
注意:
1) p: 存放"hello"字符串首元素’h’的地址,
2) p是指向一个字符,不是指向一个字符串
3) 字符串在常量区存储时,以’\0’作为结束标志
细节补充1:
char *p="hello";
char *q="hello";
p和q的地址是一样的,编译器在处理的时候会把相同的字符串优化。
细节补充2:
“如果有空间”系统会在字符串后补‘\0’
反言之就是如果空间不够,系统就不能补了;
用strlen()测试下面字符串长度:
char cc[5]="hello"; //结果:19(不确定) //空间不足补\0 {'h','e','l','l','o'}
char cc[10]="hello"; //结果:5 //空间足够补\0 {‘h’,'e','l','l','o','\0','\0','\0','\0','\0'};
char cc[]="hello"; //结果:5 //自适应 {‘h’,'e','l','l','o','\0'}
//strlen()测字符串长度是到'\0'截止,第一张情况由于系统不能补'\0',则测的时候会
一直寻找下去直到找到'\0'(null,0),所以其结果不定。
到后面使用strcpy()同理,一定要注意开辟足够的内存空间
例如存放"hello"应预留‘\0的位置’ char cc[6];
总结:
char A[10] = “hello”; --> 把常量区的字符串"hello"直接拷贝到栈空间A变量中(如果数组是局部变量)
char *p = “hello” --> 把常量区的字符串"hello"的首元素的地址存储在指针变量p中
六、strlen()
原型:
*size_t strlen(const char *s);
功能: 给一个字符串的起始地址s,不断往后计算,直到遇到\0为止就停止计算!
返回值:s指向的字符串对应的字符个数*
问题①:
//strlen()的用法以及和sizeof()的区别
char B[] = "helloworld";
char A[100] = "helloworld";
char *p = "helloworld";
printf("sizeof(A) = %d\n",sizeof(B));//11
printf("sizeof(A) = %d\n",sizeof(A));//100
printf("sizeof(p) = %d\n",sizeof(p));//4
printf("strlen(A) = %d\n",strlen(B));//10
printf("strlen(A) = %d\n",strlen(A));//10
printf("strlen(p) = %d\n",strlen(p));//10
//由char B[]可知'\0'也占一个空间,但是测字符串的时候不会算进去。
//由char A[100]可知,后面全补0,一样会占内存空间,但不会算在字符串长度上。
//sizeof(p)测的是'h'的地址,相当于sizeof(char *),任何指针都是占4个字节(32 位系统机器位长)
问题②:
int main()
{
char a[7]="a0\0a0\0";
char b[7]="a0\0asds";
char c[7]="a0\aasds";
char d[7]="a0\aaa\0";
printf("%d \n",strlen(a)); //2
printf("%d \n",strlen(b)); //2
printf("%d \n",strlen(c)); //12
printf("%d \n",strlen(d)); //5
}
//'\0'结束 结果2
//‘\0’结束 结果2
//'\a'转义字符,算一个字符 总共7个字符 c[7]没空间补'\0' 其结果为12(不确定)
//‘\a’转义字符 ,‘\0’结束,一共5个字符 结果5
补充-关于转义字符:
总结:
1)strlen()传首地址进来,返回长度;
2)strlen()直到‘\0’结束,要考虑是否有‘\0’(空间是否够补);
3)转义字符算一个字符;
七、strcmp()
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
功能:
比较两个字符串是否一致;
参数:
s1与s2都是需要进行对比的字符串的首元素地址
n: 只匹配前n个字节
返回值:
s1与s2匹配: 0
s1与s2不匹配: 非0 (实际可能负数或正数,但一般用不到,知道非0就行)
//Demo
char A[100] = "helloworld";
char *p = "hello";
//if(A == p)//错误
if(strncmp(A,p,5) == 0) //正确
{
printf("ok!\n");
}
注意:
1 )strcmp也是基于strlen的基础上,所以若果不够空间存’\0’也会导致越界对比,导致字符串不匹配;
八、strcpy()
原型:
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
功能:
拷贝字符串到某段内存中
参数:
dest: 需要把字符串拷贝到的空间的起始地址,空间必须足够大
src: 需要拷贝的字符串起始地址
n: 需要的前n个字节 ,需要src指向的内容总长度
返回值:
指向dest这个区域的指针
char a[]="hello";
char b[]="ab";
strcpy(a,b);
//执行后a里面是:{'a','b','\0','l','o','\0'}
printf("%s",a);
//打印 ab
//printf("%c",a+3)
//打印 l
注意:
1)保证被拷贝的空间足够,否则会导致越界
2)strcpy会把’\0’也拷贝过去
3)n指定可以实现追加字符串功能
经验:
① 常用于字符数组初始化后赋值:
char A[100];
A[100] = “gec123456” ; //错误
strcpy(A,“gec123456”);//正确
②malloc()申请堆空间,要用strcpy把字符串赋值过去。
九、strcat()
功能:追加字符串
十、关于数组清零的几种常用方式
1.定义数组时初始化0
char s[50]={0}; – 初始化时,小于开辟的长度,后面自动补0
2.清空某段内存空间 – bzero() –
#include void bzero(void *s, size_t n);
s: 需要清零的内存空间首地址
n:需要清零的字节数
没有返回值
char str[50]; bzero(str,50);
3.内存初始化 --memset()–
#include 或#include void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
功能:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 。
char str[50];memset(str,0,sizeof(str));
十一、开发经验
以上几个函数在开发中会使用到的场景:
1)write()/send() – 确定某些数据的总字节数 – strlen()
2)strcmp() – 检索数据的正确与否 /登录密码,检索某个特征值
3)strcpy() – 给某些字符数组赋值(数组初始化后不能整体赋值)
char A[100];
A[100] = “gec123456” ; //错误
strcpy(A,“gec123456”);//正确
4)strcat() – 拼接字符串,仅限于追加功能 – 后期使用sprintf()代替strcat()
更多:https://www.cnblogs.com/intelwisd/p/8299738.html