以下的初始化有什么区别?
char a[] =“string literal”;
char *p = “string literal”;
当我向p[i]赋值的时候,我的程序崩溃了。
字符串字面量(string literal)—— C 语言源程序中用双引号包含的字符串的正式名称——有两种稍有区别的用法:
- 用作数组初始值(如同在 char a[]的声明中),它指明该数组中字符的初始值,可以通过改变制定下标的数组元素修改字符串;
- 其它情况下,它会转化为一个无名的静态字符数组,可能会存储在只读内存中,这就导致它只能读不能写。在表达式环境中,数组通常被立即转化为一个指针,因此第二个声明把p 初始化成指向无名数组的第一个元素,指针可以修改指向,但是不能修改内容。
char a[]和char*a 是等价的,是这样的吗?
- 完全不是。
- 数组不是指针。数组定义 char a[6]请求预留 6 个字符的位置,并用名称 a表示。也就是说,有一个称为“a”的位置,可以放入 6个字符。
- 而指针声明char*p请求一个位置放置一个指针,用名称“p”表示。这个指针几乎可以指向任何位置,任何字符或任何连续的字符,或者哪里也不指。
- 数组和指针在表达式中出现会按照不同的方法计算。
是否每个字符数组都应该包含空字符的空间呢?
- 不是必需的。因为不是所有的字符数组都作为字符串使用。为空字符预留的空间(并实践在数组中存储一个空字符)只针对于计划调用以空字符结尾的字符串的函数情况。
- 如果只对独立的字符进行处理,那么就不需要空字符。(要按照字符串处理,那就要有空字符作为结尾,按数组处理就不用加空字符)
- 例如,可能有一个作为翻译表的字符数组: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寻找的时候忽略大小写