第十章-字符串

以下的初始化有什么区别?

char a[] =“string literal”;
char *p = “string literal”;
当我向p[i]赋值的时候,我的程序崩溃了。

字符串字面量(string literal)—— C 语言源程序中用双引号包含的字符串的正式名称——有两种稍有区别的用法:

  1. 用作数组初始值(如同在 char a[]的声明中),它指明该数组中字符的初始值,可以通过改变制定下标的数组元素修改字符串;
  2.  其它情况下,它会转化为一个无名的静态字符数组,可能会存储在只读内存中,这就导致它只能读不能写。在表达式环境中,数组通常被立即转化为一个指针,因此第二个声明把p 初始化成指向无名数组的第一个元素,指针可以修改指向,但是不能修改内容

char a[]和char*a 是等价的,是这样的吗?

  1. 完全不是。
  2. 数组不是指针。数组定义 char a[6]请求预留 6 个字符的位置,并用名称 a表示。也就是说,有一个称为“a”的位置,可以放入 6个字符。
  3. 而指针声明char*p请求一个位置放置一个指针,用名称“p”表示这个指针几乎可以指向任何位置,任何字符或任何连续的字符,或者哪里也不指。
  4. 数组和指针在表达式中出现会按照不同的方法计算。

是否每个字符数组都应该包含空字符的空间呢?

  1. 不是必需的。因为不是所有的字符数组都作为字符串使用。为空字符预留的空间(并实践在数组中存储一个空字符)只针对于计划调用以空字符结尾的字符串的函数情况。
  2. 如果只对独立的字符进行处理,那么就不需要空字符。(要按照字符串处理,那就要有空字符作为结尾,按数组处理就不用加空字符)
  3. 例如,可能有一个作为翻译表的字符数组:char translation_table[128]; 对这个数组唯一可以执行的操作就是使用下标。这里不会把 translation_table 看成是字符串,而且也不会对它执行任何字符串操作。

scanf和gets的区别

函数scanf()和 gets()有何不同?

scanf都可以读取,但是%s读字符串的时候不能读入回车空格tab,而且这些个符号回流才缓冲区中。%c可以读空格了,但是只能输入一位。

gets可以读带空格字符串,回车表示输入,回车会被读走,但只能读字符串。

 能否输入带空格字符串结束标志可以读取类型
scanf()

%s格式,不能读入带空格字符串。(一空格就结束了)

%c格式可以输入空格,但是只能输入一位字符。

回车,空格,tab键结束输入,会自动添加'\0',且会留在缓冲区中所有类型的变量
gets()可以读入带空格字符串,因为空格和制表符都是字符串的一部分。回车结束输入,用'\n'替代 '\0',同时回车符会从缓冲区读走,但不作为字符串的一部分只读字符串
/*【例10.1】下面程序从键盘输入一个人名,并把它显示在屏幕上。*/ 
#include <stdio.h>

int main(){
	printf("Please input a name:");
	char name;
	gets(&name);
	puts(&name); 	
	gets(&name);
/*看前面的gets是否把回车从缓冲区读走了,如果没读走,会打印一个回车然后直接结束程序*/
	puts(&name); 
}

gets可以输入带空格字符串,以回车结束输入。

正确使用字符指针的基本原则是什么?

1)明确字符串被保存到了哪里(决定着是否能对字符串读写,如果在常量区和代码区就只能读不能写)
2)明确字符指针指向了哪里(字符指针必须有指向,否则会发生意想不到的错误,如果还不知道指向哪,可以先NULL)

字符数组和其他类型的数组在使用时有何不同?

与使用其他类型数组不同的是,通常不使用长度即计数控制的循环来判断数组元素是否遍历结束,而使用条件控制的循环,利用字符串结束标志'\0'判断字符串中的字符是否遍历结束。

赋值运算符能否用于字符串的赋值操作?

对单个字符进行赋值操作可以使用赋值运算符,但是赋值运算符不能用于字符串的赋值操作,字符串赋值只能使用函数strcpy()。(这个函数没有返回值,因为是用新的字符串覆盖原来的,也就是修改了原来的字符串,原来的字符串的首地址就是我们要的结果,所以不需要返回值)

比较字符串能否直接使用关系运算符?

比较字符串的方法不同于比较单个字符的方法。比较单个字符可使用关系运算符,但比较字符串不能直接使用关系运算符。而应使用函数 strcmp()来比较字符串的大小。(这个函数,字符串中的每个字符比较ASCII码值,相等返回0,大于小于返回不同的值来表示比较结果)

"\\"的含义是一个字符"\",所以 s2 所指向的内容实际上是"abc123\"7 个字符, 所以 strlen(s2)=7

缓冲区溢出 (Buffer Overflow )

是指当向缓冲区内写入数据时超过了缓冲区本身的容量,溢出的数据覆盖在合法的数据上。

 

/*案例 11.1——黑客系统攻击(缓冲区溢出)

【项目描述】
阅读并执行如下程序,当用户输入密码:“aaaaaaaaaaaa”时,提示密码错误,并要求再次输入,当再次输入“aaaa”时,攻入系统。请在 VC6.0 下利用 debug 工具调试程序,并说明原因。*/

/*案例 11.1——黑客系统攻击(缓冲区溢出)
【项目描述】
阅读并执行如下程序,当用户输入密码:“aaaaaaaaaaaa”时,提示密码错误,并
要求再次输入,当再次输入“aaaa”时,攻入系统。请在 VC6.0 下利用 debug 工具调
试程序,并说明原因。*/

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

int main(){
	char password[5]="abcd";
	char input[5]="";
	
	printf("please input your code:\n");
	gets(input);
	while(strcmp(input,password)!=0){
		printf("Wrong!\n");
		gets(input);
	}
	printf("Successful Hacked!\n");
	return 0;
} 

#include <stdio.h>
#include <string.h>
int main()
{
	char password[8] = "secret", input[8];
  	while (1)
	{
	  printf("Enter your password:");
	  gets(input);
	  if (strcmp(input, password) ==0)
	  {
	    printf("Welcome!\n");
	    break;
	  }
	  else
    {
	    printf("Sorry!\n");
	  }
	}	
	return 0;
}

安全的方法 

/*字符串的安全输入方法*/
#include <stdio.h>
#include <string.h>
int main()
{
	char password[8] = "secret", input[8];
  	while (1)
	{
	  printf("Enter your password:");
	  scanf("%8s", input);
	  if (strcmp(input, password) ==0)
	  {
	    printf("Welcome!\n");
	    break;
	  }
	  else
    {
	    printf("Sorry!\n");
	  }
	}	
	return 0;
}

#include <stdio.h>
#include <string.h>
int main()
{
	char password[8] = "secret", input[8];
  	while (1)
	{
	  printf("Enter your password:");
	  fgets(input, sizeof(input), stdin); 
        /*限制输入字符串的长度,更灵活*/
	  if (strcmp(input, password) ==0)
	  {
	    printf("Welcome!\n");
	    break;
	  }
	  else
    {
	    printf("Sorry!\n");
	  }
	}	
	return 0;
}

n族是更安全的 

 

多个国名实际上就是多个字符串,可用二维字符数组来表示,对奥运会参
赛国国名按字典顺序进行排序,实际上就是按字符串由小到大的顺序进行排序。 

/*请编程实现
按奥运会参赛国国名在字典中的顺序
对其入场次序进行排序。假设参赛国不超过150个。*/ 
#include <stdio.h> 
#include <string.h>
#define N 150
#define MAX_LEN 40

void Sort(int num,char country[][MAX_LEN]);

int main(){
	
	char country[N][MAX_LEN];
	
	printf("How many contraies:\n");
	int num;
	scanf("%d",&num);
	getchar();     /*读走缓冲区的回车*/ 
	
	printf("Please enter the name of the country:\n");
	for(int i=0;i<num;i++)
	{ 		
		gets(country[i]);
	} 	
	Sort(num,country);	
	printf("Sorted results:\n");
	for(int i=0;i<num;i++)
	{ 		
		puts(country[i]);
	}
	return 0;	
}

void Sort(int num,char country[][MAX_LEN]){        
	for(int i=0;i<num-1;i++) 
     /*可以减一是因为总共4: 0和1 2 3比、1和2 3比、2和3比、3不用再比了*/ 
	{
		for(int j=i+1;j<num;j++)
		{
			if(strcmp(country[j],country[i])>0)
			{       			
        /*后面的大于前面的就交换(函数返回值>0为真),降序排序*/ 
				char temp[MAX_LEN];
				strcpy(temp,country[i]);
				strcpy(country[i],country[j]);
				strcpy(country[j],temp)	;		
			}
		} 
	}
}

/*【例10.5】从键盘输入一个字符串a,将字符串 a复制到字符串 b 中,再输出字符串 b,即编程实现字符串处理函数strcpy()的功能,但要求不能使用字符串处理函数 strcpy()。*/ 

【例题解析】与使用其他类型数组不同的是,通常不使用长度即计数控制的循环来判断数组元素是否遍历结束,而使用条件控制的循环,利用字符串结束标志'\0'判断字符串中的字符是否遍历结束。注意循环结束后要将'\0'添加到 dstStr 字符串的末尾。

/*【例10.5】从键盘输入一个字符串a,将字符串 a复制到字符串 b 中,再输出字符串 b,
即编程实现字符串处理函数strcpy()的功能,但要求不能使用字符串处理函数 strcpy()。*/ 

#include  <stdio.h>
#define N 80 
void  MyStrcpy(char a[], char b[]);

int main(){
	char  a[N], b[N]; 
	printf("Input a string:");
	gets(a);                     
 /* 输入字符串,是对整个字符串复制,所以不要用字符数组一个一个输入 */
	MyStrcpy(b, a);              
	printf("The copy is:");
	puts(b);                   
	return 0;
}

void  MyStrcpy(char b[], char a[])  {
	int i=0;
	while(a[i]!='\0'){/*如果a的字符串当前不是结束字符,就复制过去*/
		b[i]=a[i];
		i++;
	} 
	b[i]='\0';/*注意循环结束后要将'\0'添加到 dstStr 字符串的末尾。*/
}

/*例10.6】从键盘任意输入一个字符串,计算其实际字符个数并打印输出,即不使用字符串处理函数strlen()编程实现strlen()的功能。*/

【例题解析】向函数传递字符串时,既可使用字符数组作函数参数,也可使用字符指针作函数参数。

字符数组做参数

/*例10.6】从键盘任意输入一个字符串,计算其实际字符个数并打印输出,
即不使用字符串处理函数strlen()编程实现strlen()的功能。*/
#include <stdio.h>
#define N 30

void Mystrlen(char a[],int *len); 

int main(){
	char a[N];
	int len;
	gets(a);
	
	Mystrlen(a,&len);
	printf("字符串长度=%d",len);
}
/*字符数组做参数*/
void Mystrlen(char a[],int *len){
	int i=0;
	while(a[i]!='\0') i++;
	*len=i;
} 

字符指针做参数 

/*例10.6】从键盘任意输入一个字符串,计算其实际字符个数并打印输出,
即不使用字符串处理函数strlen()编程实现strlen()的功能。*/
#include <stdio.h>
#define N 30

int Mystrlen(char a[]); 

int main(){
	char a[N];
	int len;
	gets(a);
	
	len=Mystrlen(a);
	printf("字符串长度=%d",len); 
}
/*字符指针做参数*/
int Mystrlen(char *a){
    int len=0;

	while(*a!='\0') {
		a++; 
		len++; 	
	} 
	return len;  
} 

/*【例 10.7】不使用字符串处理函数strcat()编程实现 strcat()的功能,
即任意输入两个字符串,然后连接这两个字符串,返回连接后字符串的首地址。*/

【例题解析】函数 MyStrcat()的返回值就是连接后的字符串的地址值。程序第 22~25 行利用一个 while 循环将字符指针 dstStr 移到目标字符串的末尾(找到'\0'为止)。程序第 27~30行用一个 for 循环,依次将字符指针 srcStr 指向的源字符串中的字符依次添加到目标字符串的末尾。

字符数组做参数

/*【例 10.7】不使用字符串处理函数strcat()编程实现 strcat()的功能,
即任意输入两个字符串,然后连接这两个字符串,返回连接后字符串的首地址。*/
#include <stdio.h>
#define N 30
void Mystrcat(char a[],char b[]);/*为了bug free要注意每一个错误*/ 

int main(){
	char a[N],b[N];
	gets(a);
	gets(b);
	
	Mystrcat(a,b);
	puts(a);	
}
 /*字符数组做参数*/
void Mystrcat(char a[],char b[]){    
   
	
	int i=0; 
	while(a[i]!='\0') i++; /*令人难以置信的马虎,把\0写成\n,找了半天错!*/ 
	/*找到a的结束位置,a的\0位置就是b字符串的开始位置*/
	int j;
	for(j=0;b[j]!='\0';j++,i++){
		a[i]=b[j];
	}
    a[i]='\0';/*别忘了是个字符串,后面就必须有结束标志*/  
}

字符指针做参数 

/*【例 10.7】不使用字符串处理函数strcat()编程实现 strcat()的功能,
即任意输入两个字符串,然后连接这两个字符串,返回连接后字符串的首地址。*/
#include <stdio.h>
#define N 30
void Mystrcat(char a[],char b[]);/*为了bug free要注意每一个错误*/ 

int main(){
	char a[N],b[N];
	gets(a);
	gets(b);
	
	Mystrcat(a,b);
	puts(a);	
}
/*用字符指针做参数(刚开始觉得不行,因为觉得指针只能做移动不能做赋值)*/
void Mystrcat(char *a,char *b){	
	while(*a!='\0') a++; 

	for(;*b!='\0';b++,a++){ /*指针++就可以往不同的位置写入字符了*/ 
		*a=*b;
	}
	*a='\0';  
}

 

字符串

  • 末尾的\0使得这个word成为字符串数组,但计算字符串长度的时候不包括这个0
  • 字符串以数组形式存在,可以以数组或指针的形式访问,更多是以指针的形式,可以遍历等等
  • string.h

字符串变量的几种表达方法:

  • 自动生成的结尾\0,所以hello是6个字符,但计算长度时不算结尾的0
  • char *str=”hello”:str是一个指针,初始化为指向一个字符串常量,是只读不写的。

    • 如果需要修改字符串,应该用数组:char str[]=”hello”

    • 何时使用指针,何时使用数组?

      • 构造一个字符串 用数组:这个字符串在这里,作为本地变量空间被自动回收

      • 处理一个字符串 用指针:这个字符串不知道在哪里:1、只读不写2、处理参数3、动态分配空间

    • 字符串可以表达为char*的形式,但char*不一定是字符串,本意是指向字符的指针,可能指向的是字符的数组。只有它所指的字符数组有结尾的0,才能说它所指的是字符串。

字符串常量

  • “hello”
  • 两个相邻的字符串常量会被自动连接起来“你好”“戴鑫”=你好戴鑫

字符串

  • C语言的字符串是以字符数组的形态存在的
  • 不能用运算符对字符串做运算

  • 用过数组的方式可以遍历字符串

  • 唯一特殊的和其他语言不同的是:字符串的字面量可以用来初始化字符数组
  • 提供了一系列的字符串库函数

字符串的输入输出

  • 字符串的赋值:

char *t=”title”;

char *s;

s=t;

中并没有产生新的字符串,只是让指针s指向了t所指的字符串,对s的任何操作就是对t的

  • 输入输出:

char string[8];

scanf(“%s”,string);

printf(“%s”,string);

中scanf读入一个单词,到空格tab或者回车为止。scanf是不安全的,因为不知道要读入的内容的长度。%ns表示最多只读n个字符,此时根据长度来划分输入结束,没有n的话就是按空格tab和回车来确定输入结束。

  • 常见错误:
    • char *string;

scanf(“%s”,string);

中以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接用了,其实只定义了一个指针变量,只是个指针,而且指向不明。由于没有对string初始化,所以不一定每次运行都出错。

 空字符串:char b[100]=””;是一个空的字符串,b[0]==’\0’,里面什么都放不下

字符串数组

  • char **a:a是一个指针,指向另一个指针,那个指针指向一个字符(串)

  • char a[][]

    • 第二个[]必须确定大小,确定之后,则输入的字符长度不得超过此长度

    • 相当于a[i][10]:每个a[i]都是10

  • char *a[]相当于a[0] char*:每个a[i]指向不同的字符串:这就是月份练习的那个字符串数组

  • strcpy

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

    • 把src的字符串拷贝到dst
      • restrict表明src和dst不重叠,内存上是不能重叠的

      • 返回dst
      • char *dst=(char*)malloc(strlen(src)+1);strcpy(dst,src);
        • 动态申请内存:+1表示加上0
        • 拷贝过来,万一就是外面那个可能会丢
  • 如何利用字符串函数只输出hello中的he?

  • strchr找到l,把l存到c,把l处赋值为0,给t找一块地方,用strcpy把s复制到t,输出t,就得到了he,
  • 字符串中找字符串

1、strstr字符串中找字符串,strchr是字符串中找单个字符

2、strcasestr寻找的时候忽略大小写

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值