(1) strcpy( )
strcpy(str1,str2);//将str2的字符拷贝至str1数组中,注意,str2遇到\0结束,会将\0拷贝至str1 (会把str1的内容覆盖掉)
strncpy(str1,str2,n); // 将str2中的前n个字符拷贝至str1中,如果拷贝时不足n个,遇到\0拷贝结束
(2) strcat( )
strcat(str1,str2) //将str2字符数组中的字符连接到str1字符数组中,遇到\0结束
strncat(str1,str2,n) //将str2字符数组中的n个字符连接到str1字符数组中,遇到\0结束
(3) strcmp ( ) 注意: 比较时遇到\0结束比较
int main()
{
char str1[] = "a\0bcdef";
char str2[] = "a\0cdrrr";
//str1数组中和str2数组拿出一个元素进行比较,相等继续往后比较
//比较的是字符的ascii值
//如果str1> str2 返回值等于1
//如果str1== str2 返回值等于0
//如果str1 <str2 返回值等于 - 1
//printf("%d\n",strcmp(str1,str2));
printf("%d\n", strncmp(str1, str2,3));
system("pause");
return 0;
}
(4) sprintf() : 组包函数
int len = sprintf(buf,"格式","数据"); // 将数据安装格式组包,存放在数组buf中,
// len : sprintf函数的返回值是组完包的有效长度
(5) sscanf() 拆包函数
sscanf (buf,"格式",数据); // 将buf的内容格式化输出到数据
int main()
{
//%d 0-9的字符
int year =0 ;
int month = 0;
int day = 0;
char buf[1024] = "beijing:2018:t:10:20";
//scanf("%d:%d:%d",&year,&month,&day);//从键盘按照相应的格式获取数据
sscanf(buf, "beijing:%d:t:%d:%d", &year, &month, &day);//从buf中按照`相应的格式获取数据
printf("%d %d %d\n",year,month,day);
system("pause");
return 0;
}
(6) strchr( ) 字符串比较函数
char *p = strchr(buf,ch) // 在buf字符数组中查找字符ch出现的位置,如果成功返回此字符出现位置的地址,如果没有找到,返回NULL
(7) strstr( )
strstr(str1,str2) // 在str1字符数组中查找str2字符串出现的位置,并且返回这个位置的地址
(8) strtok ( )
char *strtok(char *str, const char *delim)
' str—要被分解的字符串
' delim—用作分隔符的字符(可以是一个,也可以是集合)'
' 该函数返回被分解的第一个子字符串,若无可检索的字符串,则返回空指针'
#include <string.h>
#include <stdio.h>
int main () {
char str[80] = "aaa - bbb - ccc";
const char s[2] = "-"; // 字符串里可以是一个分隔符,也可以是分隔符的集合, 他会在底层给这些字符串置 '\0'
char *token;
/* 获取第一个子字符串 */
token = strtok(str, s);
/* 继续获取其他的子字符串 */
while( token != NULL ) {
printf( "%s\n", token );
token = strtok(NULL, s); // 而且会记录下位置,所以第二次切割一定是从NULL切割,也就是第一个分隔符开始
}
return(0);
}
// 第一次调用strtok(),传入的参数str是要被分割的字符串{aaa - bbb -ccc},而成功后返回的是第一个子字符串{aaa};
// 而第二次调用strtok的时候,传入的参数应该为NULL,使得该函数默认使用上一次未分割完的字符串继续分割 ,就从上一次分割的位置{aaa-}作为本次分割的起始位置,直到分割结束。
strtok()注意事项:
(1)delim 替换成 \0
-
在这个函数里strtok()在分解字符串的时候,第一个参数str是在不断变化的,这个函数是在改变原字符串,把原字符串{aaa - bbb - ccc}通过第二个参数delim将所有的分割符{-}替换成字符串结束标志字符{\0},则原字符串变化为{aaa \0 bbb \0 ccc}
// 错误示范案例
#include <string.h>
#include <stdio.h>
int main () {
char *str = "aaa - bbb - ccc"; //****注意这里!
const char s[2] = "-";
char *token;
/* 获取第一个子字符串 */
token = strtok(str, s);
/* 继续获取其他的子字符串 */
while( token != NULL ) {
printf( "%s\n", token );
token = strtok(NULL, s);
}
return(0);
}
注意:在这里,我实现函数的时候将字符串数组直接用指针指向它了,结果运行错误,后面发现虽然第一个参数是可以传指针,但我们要考虑空间内存布局,在strtok()函数里是delim的{分隔符}替换{ \0}改变原字符串,而我们用指针指向这个字符串的时候,其实指向的是字符串常量,它的内存分布在文字常量区是不可被改变的,所以出现了错误!
(2)delim分隔符
-
strtok()的第二个参数delim,其实delim里可以是所有分隔符的集合,比如{aaa - bbb ; ccc | ddd},第二个参数delim可以是{-;|},用一个或多个分隔符去分解字符串都可以
(3)delim分隔符可不可以出现在第一个字符?
-
答案是:可以
-
当strtok分解的字符串首字符就是分隔符,那么strtok()会忽略首个分隔符,直接从第二个分隔符往下继续分解,例如:{- aaa - bbb - ccc} 那么strtok()会忽略第一个{-},还是以{aaa - bbb - ccc}的字符串形式继续分解。
strtok()函数源码:
char *strtok(char *str,const char *delim)
{
static char *next_start = NULL; //保存到静态存储区,函数返回后不会被销毁
if(str == NULL && (str = next_start) == NULL)
{
return NULL;
}
char *s = str;
const char *t = NULL;
while(*s)
{
t = delim;
while(*t)
{
if(*t == *s)
{
next_start = s + 1;
if(s == str) //第一个字符就是分隔符
{
str = next_start;
break;
}
else
{
*s = '\0';
return str;
}
}
else
{
t++;
}
}
s++;
}
return NULL;
}
注意: char* str 与 char str[]区别
一、C语言中的几个存储区
一个由C/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域;未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、常量区:常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区
int a=0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[]="abc"; //栈
char *p2; //栈
char *p3="123456"; //123456\0在常量区,p3在栈上。
static int c=0; //全局(静态)初始化区
p1 = (char*)malloc(10);
p2 = (char*)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p1,"123456"); //123456\0放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。
}
二、问题:
使用两个字符串指针,使用memcpy为什么会报段错误?
1、问题
void test(void)
{
char * a = "hello world1";
char * b = "hello world2";
memcpy(a, b, strlen(b) + 1); // 此时会直接段错误, 不能修改 a指向的字符串常量 ,(关键问题就是a没有开辟空间直接指向常量区)
return 0;
}
void main(void){
test();
}
解释:
char *str = "hello world"//一份拷贝,"hello world"是常量字符串存在静态数据区,把该字符串常量存在的静态数据区的首地址赋给指针str,所以test()函数退出时,该字符串常量所在内存不会被回收,故能通过指针访问; 而memcpy的第一个参数是变量,不能是常量,因此在此时会报错。
2、问题:
void test(void)
{
char a[] = "hello world1";
char b[] = "hello world2";
memcpy(a, b, strlen(b) + 1); // 此时程序正常
return 0;
}
void main(void){
test();
}
解释:
str[] = "hello world" (自己开辟了空间)// "hello world"常量字符串在内存中有两份拷贝,一份在动态分配的栈中,一份在静态存储区,str[]数组为函数内部局部变量,存储在栈上,在test()函数退出时,栈要清空,局部变量的内存也被清空
void c(int n,char *pName)
{
char *a[4] = {"aaa","bbb","ccc","ddd"};
pName = a[n];
}
void main()
{
int n=0;
char *pName = "DB";
c(2,pName);
printf("%s\n",pName); //输出为DB
}
解释:
//输出DB,因为char *pName = “DB”;已经使得pName指向了DB,但c(2,pName);并不能改变pName指向的地址。形象点说就是:我有一个箱子给你用,你可以在里面装东西,但是你不能把我的箱子换成另外一个给我。在这里指的是不能函数调用不能使pName变成函数中的二维数组a
atoi() : 将字符串转整数
atof(): 将字符串转float类型的数据
int n = atoi( 需要转的字符串)