字符串的初始化
- 字符数组
(1) char str[10]={'j','u','l','i','a','n'}
申明固定长度的字符数组,如果赋值长度大于数组长度,报错;如果小于数组长度,后面都是0(即‘\0’)
(2)char str[10]={"julain"}
(3)char str[10]="julian"
后两个结果一样,当成字符串去初始化,最后一位是str[6] =’\0’
(4)char str[]={'j','u','l','i','a','n'}
与(1)对比,如果定义成隐式长度,则长度会以后面的字符数来决定。数组最后一个是’n’,不是’\0’。
(5)char str[]={"julian"}
(6)char str[]="julian"
后两个是一样的,当成字符串处理,str数组的长度是julian+\0,即7。str不能再被其他字符串赋值。
- 字符串指针
(1) char* str="julian"
(2)char* str; str="julian"
这种将字符串赋值给指针是OK的,它可以再被其他字符串赋值。能以str[]方式来访问,但不能修改。str可以再被指向其他字符串常量,或者字符串数组。
char* 和char[] 的区别
char* str ="julian"
"julian"存放在常量区,是无法修的,但是str可以再指向其他内容。但是定义char str[]="julian"
这种形式的,字符串数组是在栈中分配的,可以修改数组内容的。
(1) 读写能力:
char* str ="julian";
此时"julian"存放在常量区。通过指针只可以访问字符串常量,而不可以改变它。- 而
char str[]="julian";
此时 "julian"存放在栈。可以通过指针去访问和修改数组内容。
(2) 首指针的可修改性:
char* str ="julian";
str指向常量的首地址,可以通过str[]去逐个访问字符,str指向可以改变的,比如
char* str="julian";
char str1[]="kerr";
str=str1;
printf("%s",str); // 编译通过,结果是kerr,也可以通过str去改变kerr的内容
char str[]="julian";
str指向第一个元素(即j的地址),一经分配就str不能更改,比如:
char* str="julian";
char str1[]="kerr";
str1=str; //报错
(3)赋值时刻
char* str ="julian";
编译的时候就确定了,因为是常量。char str[]="julian";
在运行的时候确定,在栈中。
(4)存取效率
char* str ="julian";
存于只读数据区(.rodata常量区),慢。char str[]="julian";
存于栈上。快。
字符串处理函数
注意:字符数组dest必须是数组名形式,不能是字符串指针,(其实如果char* p指向的不是常量,而是指向栈区分配的数组,也是可以的),src可以是数组名也可以是字符串常量 ,dest要能够容纳src。
下面介绍的函数基本都需要注意红色部分。
字符串的输入输出函数
- scanf() printf() 在头文件<stdio.h>
- 格式说明:
符号 | 含义 |
---|---|
%d | 十进制有符号整数 |
%u | 十进制无符号整数 |
%f | 浮点数 |
%s | 字符串 |
%c | 单个字符 |
%p | 指针的值 |
%e | 指数形式的浮点数 |
%x, %X | 无符号以十六进制表示的整数 |
符号 | 含义 |
---|---|
%9.2f | 浮点数, 其中小数位为2, 整数位为7, |
%3d | 表示输出3位整型数, 不够3位,右对齐 |
%-3d | - 表示左对齐 |
%03d | 表示输出3位整型数, 不够3位,左补0,右对齐 |
%8s | 表示输出8个字符的字符串, 不够8个字符,右对齐 |
typedef struct PERSON
{
int ID;
char name[64];
}Person;
int main(void)
{
Person p1;
scanf("%d %s",&p1.id,p1.name); //""之间除了格式不要加符号
printf("%d , %s",p1.id,p1.name);
return 0;
}
输入:1 julian
输出:1 ,julian
注意:假如name是 Julian Andrison ,输入name只是Julian,因为scanf遇到第1个空白(空格、制表符,换行符)时,就不会再读取输入
- puts() gets() 在头文件<string.h>
- 可以读字符串(可以包含空格),编译器会提示警告,说使用它不安全,使用时,保证存的buffer足够大来容纳读取的字符串
char str[64];
gets(str);
puts(str);
字符串拷贝、追加
- char *strcpy(char * dest, const char *src); <string.h>和<stdio.h>
功能:将src地址开始且包含’\0’结束符的字符串复制到以dest开始的空间
库原型:
char* strcpy(char* des,const char* source)
{
char* r=des;
assert((des != NULL) && (source != NULL));
while((*r++ = *source++)!='\0');
return des;
}
- char *strncpy(char *dest,char *src,int size_t n); <string.h>
功能:把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest。
char str[64]="julian";
char*p = "kerr";
strncpy(str,p,4);
printf("%s"str); 输出kerran
strncpy(str,p,5);
printf("%s"str); 输出kerr
- char *strcat(char *dest,const char *src); <string.h>
功能: 把src中的内容复制到dest结尾处(覆盖’\0’)。
char str[64]="julian";
char*p = "kerr";
strcat(str,p);
printf("%s",str); 输出juliankerr
- char * strncat(char *dest, const char *src, size_t n);
功能:把src所指字符串的前n个字符添加到dest所指字符串的结尾处,并覆盖dest所指字符串结尾的’\0’
char str[64]="julian";
char*p = "kerr";
//strcat(str,p,4);
strcat(str,p,5);
printf("%s",str); 两种情况都输出juliankerr,因为str[64]后面没赋值都是0
字符串比较
- int strcmp(const char *s1,const char *s2); <string.h>
功能:比较两个字符串,
若s1==s2,则返回零;
若s1<s2,则返回负数;
若s1>s2,则返回正数
char str1[]= "julian";
char* str2="mike";
int i=strcmp(str1,str2);
printf("%d",i); 输出是:-3(即比较的不同字母的ascii差值)
- int strncmp ( const char * str1, const char * str2, size_t n ); <string.h>
功能:比较指定长度字符串
返回值:和上面一样
-
int strcasecmp(const char *s1,const char *s2); (linux下的函数,windows下使用_stricmp) <string.h>
功能:忽略大小写比较字符串
返回值:和上面一样
-
int strncasecmp(const char *s1,const char *s2); (linux下的函数,windows下使用_strnicmp) <string.h>
功能:忽略大小写比较字前n个字符
返回值:和上面一样
字符串查找
- char* strchr(const char*string, char c) <string.h>
功能:查找字符串 string 中首次出现字符 c 的位置
返回值:成功则返回要查找字符第一次出现的位置(指针),失败返回NULL
char str[]="julian";
char* pos=strchr(str,'n');
printf("%c",*pos); 输出:n
- char strrchr(const char string, int c); <string.h>
功能:查找字符c在字符串string中最后一次出现的位置, 也就是对string进行反序搜索, 包含NULL结束符.
返回值:返回一个指针, 指向字符c在字符串string中最后一次出现的位置, 如果没有找到, 则返回NULL.
char str[]="qweu13ll23i3mi";
char* p = strrchr(str,'3');
printf("%c",*(p+1)); //最后一个3后面是'm' ,输出:m
- char *strstr(const char *str1, const char *str2); <string.h>
功能:判断字符串str2是否是str1的子串
返回值:如果是子串,返回str2在str1中首次出现的地址;否则,返回NULL。
char str1[]="julian";
char str2[]="lian";
char* pos=strstr(str1,str2);
printf("%s",pos); 输出lian
- int strspn(const char *current, const char *accept) <string.h>
功能:以目标字符串accept的所有字符作为集合,在当前字符串current查找不属于该集合的第一个元素的偏移
返回值:偏移值n(也可以理解current字符串中前n个字符在accept里面),第n+1个不在了
char str[]="jul123";
int num = strspn(str,"jdjiu,2341"); // "jul123"第一个不在"jdjiu,2341" 集合里面的是l,所以返回:2
printf("%d",num); //num:2
- int strcspn(const char *current, const char *accept) <string.h>
功能:以目标字符串accept的所有字符作为集合,在当前字符串current查找属于该集合的第一个元素的偏移
char str[]="qweul123";
int num = strcspn(str,"jdjiu,2341"); //"qweul123"中的u是第一个属于"jdjiu,2341"字符串的,所以num=3
printf("%d",num); //num=3
- char * strpbrk(const char* current, const char* accept) <string.h>
功能:以目标字符串accept的所有字符作为集合, 在当前字符串current查找属于该集合的第一个元素,并返回地址
返回值:返回current中第一个满足条件的字符的指针,如果没有匹配字符则返回空指针NULL。
char str[]="qweul123";
char* p=strpbrk(str,"jdjiu,239"); //"qweul123"中的u是第一个属于"jdjiu,2341"字符串的,所以返回u的地址
printf("%c",*p); //u
字符串到数值类型的转化
- int atoi(const char *nptr) <stdlib.h>
功能:跳过前面的空白字符(例如空格,tab缩进),把字符串转换成整型数
返回值:遇到非数字的字符就停止转化,返回之前的转化结果,“1y.34” -> 1
char str[]="123";
int i=atoi(str);
printf("%d",i); 输出:123
- int atof(const char *nptr) <stdlib.h>
功能:把字符串转换成浮点数
返回值:float型浮点数,“123.123” 输出不一定是123.123,转成float型,小数点位数变化
char str[]="123.123";
float res=atof(str);
printf("%f",res); 输出:123.123001
- long atol(const char* nptr); <stdlib.h>
功能:把字符串转换成 long 型
- double strtod(const char* nptr, char ** endptr); <stdlib.h>
功能:把字符串转换成 double型,如果遇到不能转的字符,并将后续的字符串指针存储到 endptr指向的 char* 类型存储,如果不需要填NULL。
char str[]="1t3.a32"; //可以"1001e-2" 形式,输出是10.01
char*p;
double val=strtod(str,&p);
printf("%f",val); 输出:1.000000
printf("%s",pos); 输出:t3.a32
- long int strtol(const char* nptr, char** endptr, int base); <stdlib.h>
功能:从字符串 nptr 中转换 long 类型整型数值,endptr和上面一样,base :将字符串当成什么进制来处理。
base:0 根据字符串前缀来识别,0:当做8进制处理,0x:当成16进制处理,都不是就是10进制处理
base:2-32 指定当成什么进制处理
char str[]="1001";
int val = strtol(str,NULL,0); val:1001
int val = strtol(str,NULL,2); val:9
int val = strtol(str,NULL,8); val:513
int val = strtol(str,NULL,10); val: 和0一样,1001
int val = strtol(str,NULL,16); val:4097
int val = strtol("01001",NULL,0); val:513
int val = strtol("0x1001",NULL,0); val:4097
- unsigned long strtoul(const char* nptr, char** endptr, int base);
功能:从字符串 nptr 中转换unsigned long 类型整型数值 ,其他和上面一样
memry操作
- void* memset(void *dest, int value, size_t count); <string.h>
功能:将dest前面count个字节置为value.
返回值:dest的值.
char str[10];
for(int i=10; i<10; i++)
{
printf("%d",str[i]); 因为申明的str在栈上,没有初始化,野值
}
memset(str,0,10,);
for(int i=10; i<10; i++)
{
printf("%d",str[i]); 经过memset之后,值都是0
}
note:常见错误
(1) memset函数按字节对内存块进行初始化,所以不能用它将int数组初始化为0和-1之外的其他值(除非该值高字节和低字节相同)。
(2)memset(void *dest, int value, size_t count);中value实际范围应该在0~~255,因为该函数只能取ch的后八位赋值给你所输入的范围的每个字节。
(3)容易弄反 value 和 count的顺序
(4)过渡使用memset。
char buffer[4];
memset(buffer,0,sizeof(char)*4);
strcpy(buffer,"123");
//"123"中最后隐藏的'\0'占一位,总长4位。memset是多余的. 因为这块内存马上就被全部覆盖,清零没有意义
char buffer[20];
memset(buffer,0,sizeof(char)*20);
memcpy(buffer,"123",3);
//这一条的memset并不多余,memcpy并没把buffer全部覆盖,如果没有memset,
//用printf打印buffer会有乱码甚至会出现段错误。
//如果此处是strcpy(buffer,"123");便不用memset,
//strcpy虽然不会覆盖buffer但是会拷贝字符串结束符
(5)传参过程中的指针降级
typedef struct PERSON
{
int ID;
char name[64];
}Person;
void func(Person* p)
{
printf("%d",sizeof(p)); //64位机,8,地址的大小
}
- void* memcpy(void *dest, const void *src, size_t count); <string.h>
功能:从src复制count字节的数据到dest. 不能处理src和dest出现重叠.
返回值:dest的值.
typedef struct PERSON
{
int ID;
char name[64];
}Person;
int mian(void)
{
Person p1={1,"julian"};
Person p2;
memcpy(&p2,&p1,sizeof(p1));
printf("%s's ID is %d \n" p2.name,p2,id);
return 0;
}
输出:julian's ID is 1
- strcpy和memcpy主要有以下3方面的区别。
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy