基础7.2:string拓展

1. 字符串操作

1.1 字符串遍历

(1)基本for循环写法,下标访问1

for(int i=0;'\0'!=s[i];++i){
		printf("%c ",s[i]);
	}
	printf("\n");

(2)把下标访问改成指针访问,下标访问2

for(int i=0;'\0'!=*(s+i);++i){
		printf("%c ",*(s+i));
	}
	printf("\n");

(3.1)for循环:使用指针操作,指针自己加,类似于数据结构里面的;

char* p = s;
	for(;'\0'!=*p;++p){
		printf("%c ",*p);
	}
	printf("\n");

(3.2)把for循环改成while

char* q = s;
	while('\0'!=*q){
		printf("%c ",*q);
		++q;
	}
	printf("\n");

(4)使用后缀++,先打印,后自增(特殊);合并解引用和数组指针++操作

char* k = s;
	while(*k){
		printf("%c ",*k++);
	}
	printf("\n");

}

完整代码见001_string.c

#include <stdio.h>

int main(){
	char s[20];
	scanf("%s",s);
	// (1)基本for循环写法,下标访问1
	for(int i=0;'\0'!=s[i];++i){
		printf("%c ",s[i]);
	}
	printf("\n");

	// (2)把下标访问改成指针访问,下标访问2
	for(int i=0;'\0'!=*(s+i);++i){
		printf("%c ",*(s+i));
	}
	printf("\n");

	// (3.1)使用指针操作,指针自己加,类似于数据结构里面的;
	char* p = s;
	for(;'\0'!=*p;++p){
		printf("%c ",*p);
	}
	printf("\n");

	// (3.2)把for循环改成while
	char* q = s;
	while('\0'!=*q){
		printf("%c ",*q);
		++q;
	}
	printf("\n");

	// (4)使用后缀++,先打印,后自增(特殊);合并解引用和数组指针++操作
	char* k = s;
	while(*k){
		printf("%c ",*k++);
	}
	printf("\n");

}

1.2 字符串赋值

(1.1)赋值法1:字符串

char s1[] = "Hello World";// (1.1)赋值法1:字符串数组

(1.2)赋值法2:字符串指针

char* s2 = "Hello World"; // (1.2)赋值法2:字符串指针

2.大小
(2.1)字符串数组:字符的个数;

printf("%p s1=%s\t sizeof(s1)=%d\n",s1,s1,sizeof(s1));//(2.1)字符串数组:字符的个数;

(2.2)字符串指针:指针字节数是计算机的长度;

printf("%p s2=%s\t sizeof(s2)=%d\n",s2,s2,sizeof(s2));//(2.2)字符串指针:指针字节数是计算机的长度;
  1. 是否可修改
s1[0] = 'p';
	printf("s1=%s\n",s1);

3.1 字符串指针的指向字符变量,不可修改。

s2[6] = 'p';
s2[0] = 'p';

4.赋值
4.1 字符数组可以给字符串指针(变量)赋值

printf("%p s2=%s\n",s2,s2);
	s2 = s1;
	printf("%p s2=%s\n",s2,s2);

4.2字符数组名只是一个地址值,不能被赋值(错误)

s1 = s2;

3.2 .修改

// 字符串指针强制转化(①赋值②强制转化)指向字符数组,可以修改。
	s2[6]='p';
	printf("%p s2=%s\n",s2,s2);
	printf("%p s1=%s\n",s1,s1);
}

完整代码见002_string.c

#include <stdio.h>

int main(){
	char s1[] = "Hello World";// (1.1)赋值法1:字符串
	char* s2 = "Hello World"; // (1.2)赋值法2:字符串指针

	// 1.大小
	printf("%p s1=%s\t sizeof(s1)=%d\n",s1,s1,sizeof(s1));//(2.1)字符的个数;
	printf("%p s2=%s\t sizeof(s2)=%d\n",s2,s2,sizeof(s2));//(2.2)指针字节数是计算机的长度;

	// 2.是否可修改
	s1[0] = 'p';
	printf("s1=%s\n",s1);
	// 2.1 字符串指针的指向字符变量,不可修改。
	//s2[6] = 'p';
	//s2[0] = 'p';


	// 3.赋值
	// 3.1 字符数组可以给字符串指针(变量)赋值
	printf("%p s2=%s\n",s2,s2);
	s2 = s1;
	printf("%p s2=%s\n",s2,s2);
	// 3.2字符数组名只是一个地址值,不能被赋值
	//s1 = s2;

	// 4.修改
	// 字符串指针强制转化(①赋值②强制转化)指向字符数组,可以修改。
	s2[6]='p';
	printf("%p s2=%s\n",s2,s2);
	printf("%p s1=%s\n",s1,s1);
}

1.3 字符串输入输出

  1. scanf()读入一个单词直到空白符(空格、回车、Tab)
    scanf()不安全,因为不知道要读入的内容长度,容易溢出。
    例如:输入123456789
    解决方式:指定读取的长度。
    %s之间的数字表示最多允许输入的字符数,这个数字要比数组长度少1,打印的时候仅仅显示字符串长度的数据。

完整代码见003_sizeof.c

#include <stdio.h>

int main(){
		char str[8];
		scanf("%7s",str);
		printf("%s\n",str);
}
  1. sscanf()将字符串数组拆分为多个类型的变量,即拆分容器
char* date = "2019" "12" "21";
	int year;
	int month;
	int day;
	sscanf(date,"%4d%2d%2d",&year,&month,&day);//(1)拆分容器
	printf("Y:%d\nM:%d\nD:%d\n",year,month,day);//(1.1)打印;
  1. sprintf()将多个类型的变量组合成字符串数组,即组合容器
char str[18];
	sprintf(str,"%d年"
				"%d月"
				"%d日",year,month,day);//打印组合容器

(完整代码见004_string3.c)

#include <stdio.h>
#include <string.h>

int main(){
	char* date = "2019" "12" "21";
	int year;
	int month;
	int day;
	sscanf(date,"%4d%2d%2d",&year,&month,&day);//(1)拆分容器
	printf("Y:%d\nM:%d\nD:%d\n",year,month,day);//(1.1)打印;

	// YYYY年MM月DD日 8+3*3+1
	char str[18];
	sprintf(str,"%d年"
				"%d月"
				"%d日",year,month,day);//打印组合容器
}

2. 字符串与函数

2.1 字符串传参

字符串传参方式与数组传参方式一样,只不过很多时候不需要传递字符串的长度(为什么?)。

 void print_string(char str[]){
        printf(str);
    }

或者

  void print_string(char* str){
        printf(str);
    }
    

2.2 字符串返回

字符串返回只能使用指针char*

3. 练习

  1. 交换
    实现字符串交换函数void swap(char* s1,char* s2)

  2. 拼接
    实现字符串拼接函数char* strcat(char* s1,char* s2)

  3. 拷贝
    实现字符串拷贝函数char* strcpy(char* s1,char* s2)

  4. 比较
    实现字符串相等判断函数bool strcmp(char* s1,char* s2)

1.交换函数,传入的是指针;
(1.1)保证有足够的长度,存下字符串;
(1.2)字符串的交换;

void swap(char* a,char* b){
	printf("In Swap a=%s\tb=%s\n",a,b);
	//char t = *a; // *(a+0) a[0]
	//*a = *b;
	//*b = t;
	int len = strlen(a)>strlen(b)?strlen(a):strlen(b);//(1.1)保证有足够的长度,存下字符串;
	for(int i=0;i<len+1;++i){  //(1.2)字符串的交换;
	    char t = a[i];
		a[i] = b[i];
		b[i] = t;
	}

	printf("In Swap a=%s\tb=%s\n",a,b);
}

2.字符串拼接
(2.1)返回结果字符串有利于链式操作
(2.2)进行拼接;
(2.3)尾部添加;

void Strcat(char* a,char* b){
	int i;
	for(i=0;'\0'!=a[i];++i);//(2.1)返回结果字符串有利于链式操作
	int j;
	for(j=0;'\0'!=b[j];++j,++i){ //(2.2)进行拼接;
		a[i] = b[j];
	}
	a[i]='\0';  //(2.3)尾部添加;
	//return a;
}

3.字符串复制函数:指针法(用字符指针做形参,以指针法访问元素),拷贝并替换;

void Strcpy(char *s1, char *s2)
{
	while(*s1++ = *s2++);
}

4.字符串比较函数:返回两个字符串的差距;

int Strcmp(char *s1, char *s2)
{
	while(*s1 == *s2)
	{
		if(*s1 == 0)
			return 0;
		s1++;
		s2++;
	}
	return (*s1 - *s2);
}

完整代码见005_string_func.c

#include <stdio.h>
#include <string.h>
// 函数改变外部传入变量
// 1.传入指针
// 2.解引用



//1.交换函数,传入的是指针;
void swap(char* a,char* b){
	printf("In Swap a=%s\tb=%s\n",a,b);
	//char t = *a; // *(a+0) a[0]
	//*a = *b;
	//*b = t;
	int len = strlen(a)>strlen(b)?strlen(a):strlen(b);//(1.1)保证有足够的长度,存下字符串;
	for(int i=0;i<len+1;++i){  //(1.2)字符串的交换;
	    char t = a[i];
		a[i] = b[i];
		b[i] = t;
	}

	printf("In Swap a=%s\tb=%s\n",a,b);
}
//2.字符串拼接

void Strcat(char* a,char* b){
	int i;
	for(i=0;'\0'!=a[i];++i);//(2.1)返回结果字符串有利于链式操作
	int j;
	for(j=0;'\0'!=b[j];++j,++i){ //(2.2)进行拼接;
		a[i] = b[j];
	}
	a[i]='\0';  //(2.3)尾部添加;
	//return a;
}





//3.字符串复制函数:指针法(用字符指针做形参,以指针法访问元素),拷贝并替换;
void Strcpy(char *s1, char *s2)
{
	while(*s1++ = *s2++);
}

//4.字符串比较函数:返回两个字符串的差距;
int Strcmp(char *s1, char *s2)
{
	while(*s1 == *s2)
	{
		if(*s1 == 0)
			return 0;
		s1++;
		s2++;
	}
	return (*s1 - *s2);
}



int main(){
	char a[50],b[50];
	scanf("%49s%49s",a,b);
	printf("a=%s\tb=%s\n",a,b);
	//swap(a,b);
	//Strcat(a,b);

	printf("%d\n",Strcmp(a,b));
	//Strcpy(a,b);
	printf("a=%s\tb=%s\n",a,b);
	//printf("a=%s\tb=%s\n",Strcat(Strcat(a,b),b),b);
}


4. 字符串指针与字符数组的区别

4.1 sizeofstrlen()

(1)strlen()表示实际的字符串的长度,和数组,指针无关;
(2)sizeof()表示所有的长度,包括尾部;尤其是指针,表示计算机字长;

完整代码 见 006_sizeof_strlen.c

#include <stdio.h>
#include <string.h>

int main(){
    char arr[] = "Hello World";
    char* ptr = "Hello World";
    //(1)strlen()表示实际的字符串的长度,和数组,指针无关;
    //(2)sizeof()表示所有的长度,包括尾部;尤其是指针,表示计算机字长;
    printf("sizeof(arr) = %ld\n",sizeof(arr));
    printf("strlen(arr) = %ld\n",strlen(arr));
    printf("sizeof(ptr) = %ld\n",sizeof(ptr));
    printf("strlen(ptr) = %ld\n",strlen(ptr));//
}

}

4.2 替换字符

试一下下面三个例子

  1. 修改字符数组

完整代码见00701——replace.c >>可以执行

   #include <stdio.h>
    #include <string.h>
    
    int main(){
        char arr[] = "Hello World";
        arr[0] = 'h';
        arr[6] = 'w';
        printf("%s\n",arr);    
    }
  1. 字符串指针

完整代码见00702——replace.c(不允许修改,报错)

 #include <stdio.h>
    #include <string.h>
    
    int main(){
        char* ptr = "Hello World";;
        *ptr = 'h';
        *(ptr+6) = 'w';
        printf("%s\n",ptr);    
    }
  1. 指向字符数组的字符串指针

完整代码见00703——replace.c(允许修改)

 #include <stdio.h>
    #include <string.h>
    
    int main(){
        char arr[] = "Hello World";
        char* ptr = arr;
        *ptr = 'h';
        *(ptr+6) = 'w';
        printf("%s\n",ptr);    
    }
  1. const字符数组

完整代码见00704_replace.c
不允许修改,报错;

  #include <stdio.h>
    #include <string.h>
    
    int main(){
        const char arr[] = "Hello World";
        arr[0] = 'h';
        arr[6] = 'w';
        printf("%s\n",arr);    
    }
  1. 指向const字符数组的字符串指针

完整代码见00705_replace.c
特殊的。指向const字符数组的字符串指针,可以进行修改;(双重否定表示肯定)

   #include <stdio.h>
    #include <string.h>
    
    int main(){
        const char arr[] = "Hello World";
        char* ptr = arr;
        *ptr = 'h';
        *(ptr+6) = 'w';
        printf("%s\n",ptr);    
    }

决定能否修改的是指针指向的值能否修改。const的限制只针对定义为const的变量。

4.3 字符串字面量初始化

字符串字面量初始化字符数组
字符串字面量初始化字符串指针

char* str1 = "Hello World";
char str2[] = "Hello World";

str1[5] = '\0'; // Error
str2[5] = '\0'; // OK

printf("str1=%s\n",str1);
printf("str2=%s\n",str2);

str1是一个指针,初始化指向一个字符串常量。(在C99标准中,str1报警告,提示应该使用const char*)
修改字符串常量可能会导致严重后果。
str2是一个字符数组,初始化把字符串字面量自动复制到数组中。

如何选择?

  1. 如果需要构造字符串使用数组;如果需要处理字符串使用指针。
  2. 字符串不需要修改使用字符串字面量初始化字符串指针。
  3. 字符串需要修改使用字符串字面量初始化字符数组。

5. 字符串函数

5.1 字符串长度

  size_t strlen(const char *s);
    

返回字符串长度不包含\0

5.2 字符串比较

int strcmp(const char *s1,const char *s2);

比较两个字符串
返回0,表示s1 == s2
返回>0,表示s1 > s2
返回<0,表示s1 < s2
为什么字符串不能直接比较?
为什么字符串比较会有大小?

5.3 字符串拷贝

  char* strcpy(char* restrict dst,const char* restrict src);

把字符换src拷贝到dst。
restrict是C99关键字,表示src和dst不能重叠,便于并行处理。
返回值为dst,便于连接。
连续赋值。

复制一个字符串

// char* dst = (char*)malloc(strlen(src)+1);
char dst[strlen(src)+1];
strcpy(dst,src);

5.4 字符串连接

char* strcat(char* restrict s1,const char*restrict s2);

把s2拷贝到s1的后面,拼接成一个长的字符串。
返回s1,注意:s1必须有足够的空间。

char* a="Hello";
char* b="World";
char res[strlen(a)+strlen(b)+1] = {0};
strcat(strcat(res,a),b);

strcpy和strcat都会有安全问题:dst空间不足,出现越界。

5.5 字符查找

   char* strchr(const char*s,int c);
    char* strrchr(const char*s,int c);

返回找到字符的指针,没找到返回NULL
如何查找第二个?

5.6 子串查找

char* strstr(const char*s1,const char*s2);
char* strcasestr(const char*s1,const char*s2);

6. 文档

学会通过例子,学会函数的使用。

stdio.h
  • sprintf:数据按格式写入字符串
  • sscanf:按格式从字符串读出数据
stdlib.h
  • atof:字符串转浮点数
  • atoi:字符串转整数
  • atol:字符串转长整数
string.h

复制

连接

比较

查找

  • strchr:查找字符串中第一个出现指定字符的位置
  • strrchr:查找字符串中最后一个出现指定字符的位置
  • strstr:查找字符串中第一个出现指定子串的位置
  • strtok:切分字符串

其他

练习
实现函数times(char* dst,int n,char* src)

7. 实践

  1. 已知十天干和十二地支
    天干:甲、乙、丙、丁、戊、己、庚、辛、壬、癸
    地支:子、丑、寅、卯、辰、巳、午、未、申、酉、戌、亥
    按顺序打印出六十甲子
    甲子、乙丑、丙寅、丁卯、戊辰、己巳、庚午、辛未、壬申、癸酉、
    甲戌、乙亥、丙子、丁丑、戊寅、己卯、庚辰、辛巳、壬午、癸未、
    甲申、乙酉、丙戌、丁亥、戊子、己丑、庚寅、辛卯、壬辰、癸巳、
    甲午、乙未、丙申、丁酉、戊戌、己亥、庚子、辛丑、壬寅、癸卯、
    甲辰、乙巳、丙午、丁未、戊申、己酉、庚戌、辛亥、壬子、癸丑、
    甲寅、乙卯、丙辰、丁巳、戊午、己未、庚申、辛酉、壬戌、癸亥

  2. 已知今年的纪年,输入21世纪任意年份,打印出对应的甲子。

  3. 已知今年的纪年,打印出21世纪所有年份的甲子。

  4. 已知小明同学的属相,推断出可能的年龄。

8. (***)扩展

char*一定是字符串吗?
char*不一定是字符串,只有以0结尾的字符数组才是字符串。

8.1 0'\0''0'

0'\0'

'\0'表示字符串的结束,但不是字符串的一部分。计算字符串长度时不包含'\0'

字符串以数组方式存储,可以用数组或者指针形式访问。

8.2 空字符串

  char str[10]="";

这是一个空字符串,str[0]\0

  char str[]="";

这是也是一个空字符串,str数组长度为1

常见错误:使用未初始化的char*

char* str;
printf("%s\n",str);

同常如果指针定义时无法确定初始值时,使用NULL初始化指针。

8.3 字符串常量连接

两个相邻字符串常量会自动连接。

   char greeting = "Hello" "World";
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值