第十章 字符串

字符串常量是由一对双引号括起来的一个字符串序列

字符串实际就是由若干个有效数字构成且以字符'\0'作为结束的一个字符序列

C语言没有提供字符串数据类型,因此字符串的存取要用字符型数组来实现

字符型数组是由字符构成的数组,仅当其最后一个元素是'\0'时才表示字符串

字符串结束标志'\0'也占一个字节的内存,但它不计入字符串的实际长度,只计入数组的长度

正确写法:

char str[6] = {'H','e','l','l','o','\0'};

char str[] = {'H','e','l','l','o','\0'};

char str[] = {"hello"};

char str[] = "hello";

编译系统会根据字符串中字符的个数来确定数组的大小

数组的大小为字符串中实际字符的个数加1

多个字符串需要存放在二维字符数组中

数组第一维的长度代表要存储的字符串的个数,可以省略

但是第二维的长度不能省略

字符指针:指向字符型数据的指针变量

只要将字符串的首地址赋值给字符指针,即可让字符指针指向一个字符串

字符串常量本身代表的就是存放它的常量存储区的首地址,是一个地址常量

char *ptr = "Hello";

等价于

char *ptr;

ptr = "Hello";      /* 将保存在常量存储区中的"Hello"的首地址赋值给ptr,不能理解为将字符串赋值给ptr */

常量存储区中的字符串是只读的

数组中的字符串是可以修改的

char str[10] = "Hello";

char *ptr = str;

等价于

char str[10] = "Hello";

char *ptr;

ptr = str;            /* 等价于ptr = &str[0] */

*ptr = 'W';          /* 等价于ptr[0] = 'W'; 相当于str[0] = 'W'; */

正确使用字符指针,必须明确字符串被保存到了哪里以及字符指针指向了哪里

测试

#include <stdio.h>
int main()
{
char str[10] = "Hello";
char *ptr;
ptr = str;
printf("%p\n",ptr);
printf("%c\n",ptr[0]);
printf("%s\n",str);
*ptr = 'W';
printf("%p\n",ptr);
printf("%c\n",ptr[0]);
printf("%s\n",str);
}
//运行结果
0060FF02
H
Hello
0060FF02
W
Wello

如何访问字符串中的单个字符:

  • 使用下标:str[i]
  • 使用指针:若字符指针ptr指向了字符数组str的首地址,*(ptr+i)相当于str[i]
  • 可以通过ptr++操作,移动指针ptr,使ptr指向字符串中的某个字符
  • 对于数组名str,不可以通过str++操作使其指向字符串中的某个字符,因为数组名是一个地址常量,其值是不能被改变的

字符串的输入/输出:

 for(i = 0; i < 10; i++)//输入
{
scanf("%c", &str[i]);
}
for(i = 0; str[i]! = '\0'; i++)//输出
{
printf("%c",str[i]);
}
scanf("%s", str);//输入
printf("%s", str);//输出

//L10-1

#include <stdio.h>
#define N 12
int main()
{
char name[N];
printf("Enter your name:");
scanf("%s", name);
printf("Hello %s!\n",name);
scanf("%s", name); /* 读取输入缓冲区中余下的上次未被读走的字符 */
printf("Hello %s!\n",name);
return 0;
}
//运行结果
Enter your name:dingdingdangdang
Hello dingdingdangdang! Enter your name:dingding dangdang
Hello dingding!
Hello dangdang!

用%d输入数字或者%s输入字符串时,忽略空格、回车或制表符等空白字符(被作为数据的分隔符)

读到这些字符时,系统会认为读入结束,因此用函数scanf()按s格式符不能输入带空格的字符串

gets()

gets()以回车作为字符串的终止符,同时将回车符从输入缓冲区读走,但不作为字符串的一部分,而scanf()不读走回车符,回车符仍留在输入缓冲区中

//L10-2

#include <stdio.h>
#define N 12
int main()
{
char name[N];
printf("Enter your name:");
gets(name);
printf("Hello %s!\n",name);
return 0;
}
//运行结果
Enter your name:dingding dangdang
Hello dingding dangdang!

puts()

函数puts()用于从括号内的参数给出的地址开始,依次输出存储单元中的字符

当遇到第一个'\0'时输出结束,并且自动输出一个换行符

函数puts()输出字符串简介方便,唯一不足是不能像函数printf()一样在输出行中增加一些其他的字符信息并控制输出的格式

gets()和puts()都是C语言的标准输入/输出函数

//L10-2

#include <stdio.h>
#define N 12
int main()
{
char name[N];
printf("Enter your name:");
fgets(name, sizeof(name), stdin); /* 限制输入字符串长度不超过数组大小 */
printf("Hello %s!\n",name);
return 0;
}
//运行结果
Enter your name:dingding dangdang
Hello dingding da!

函数gets()不能限制输入字符串的长度,很容易引起缓冲区溢出

函数scanf()也同样存在这个问题

fgets(name, sizeof(name), stdin);

可以从标准输入stdin中读入一行长度为sizeof(name)的字符串送到以name为首地址的存储区中

由于该语句限制了输入字符串的长度不能超过数组的大小,所以用户输入的多余的字符都被舍弃了

//L10-3

#include <stdio.h>
#define N 12
int main()
{
char name[N];
char str[] = "\"Hello\", I said to";//\"是一个转义字符,代表双引号
printf("Enter your name:");
fgets(name, sizeof(name), stdin);
printf("%s %s.\n", str, name);
return 0;
}
//运行结果
Enter your name:dingding dangdang
"Hello", I said to dingding da.

字符串处理函数:(需在程序开头将头文件<string.h>包含到源文件中来)

strlen(str) 求字符串长度
strcpy(str1,str2) 将字符串str2复制到字符数组str1中
strcmp(str1,str2) 比较字符串str1和str2的大小,对两个字符串从左至右按字符的ASCII码值大小逐个字符相比较,直到出现不同的字符或遇到'\0'为止
strcat(str1,str2) 字符串连接,将字符串str2添加到字符数组str1中的字符串的末尾,字符数组str1中的字符串结束符被字符串str2的第一个字符覆盖,连接后的字符串存放在字符数组str1中,函数调用后返回字符数组str1的首地址
strncpy(str1,str2,n) “n族”
strncmp(str1,str2,n) “n族”
strncat(str1,str2,n) “n族”

//L10-4

#include  <stdio.h>
#include <string.h>
#define MAX_LEN 10 /* 字符串最大长度 */
#define N 150 /* 字符串个数 */
void SortString(char str[][MAX_LEN], int n);
int main()
{
int i, n;
char name[N][MAX_LEN]; /* 定义二维字符数组 */
printf("How many countries?");
scanf("%d",&n);
getchar(); /* 读走输入缓冲区中的回车符 */
/* 前面的scanf()在读取输入时会在缓冲区中留下一个字符'\n'
所以如果不在此加一个getchar()把这个回车符取走的话
gets()就不会等待从键盘键入字符,而是会直接取走这个“无用的”回车符,从而导致读取有误 */
printf("Input their names:\n");
for (i=0; i<n; i++)
{
gets(name[i]); /* 输入n个字符串 */
}
SortString(name, n); /* 字符串按字典顺序排序 */
printf("Sorted results:\n");
for (i=0; i<n; i++)
{
puts(name[i]); /* 输出排序后的n个字符串 */
}
return 0;
}
/* 函数功能:交换法实现字符串按字典顺序排序 */
void SortString(char str[][MAX_LEN], int n)
{
int i, j;
char temp[MAX_LEN];
for (i=0; i<n-1; i++)
{
for (j = i+1; j<n; j++)
{
if (strcmp(str[j], str[i]) < 0)
{
strcpy(temp,str[i]);
//对单个字符进行赋值操作可以使用赋值运算符,但是赋值运算符不能用于字符串的赋值操作
//字符串赋值只能使用函数strcpy()
strcpy(str[i],str[j]);
strcpy(str[j],temp);
}
}
}
}
//运行结果
How many countries?5
Input their names:
America
England
Australia
Sweden
Finland
Sorted results:
America
Australia
England
Finland
Sweden

因为字符数组和字符指针都可以存取C字符串

因此向函数传递字符串时,既可以使用字符数组作函数参数,也可以使用字符指针作函数参数

//L10-5

#include  <stdio.h>
#define N 80
void MyStrcpy(char dstStr[], char srcStr[]);
int main()
{
char a[N], b[N];
printf("Input a string:");
gets(a); /* 输入字符串 */
MyStrcpy(b, a); /* 将字符数组a中的字符串拷贝到b中 */
printf("The copy is:");
puts(b); /* 输出复制后的字符串 */
return 0;
}
/* 函数功能:用字符数组作为函数参数实现字符串拷贝 */
void MyStrcpy(char dstStr[], char srcStr[])
{
int i = 0; /* 数组下标初始化为0 */
while (srcStr[i] != '\0') /* 若当前取出的字符不是字符串结束标志 */
{
dstStr[i] = srcStr[i];/* 复制字符 */
i++; /* 移动下标 */
}
dstStr[i] = '\0'; /* 在字符串dstStr的末尾添加字符串结束标志 */
}
//运行结果
Input a string:Hello China
The copy is:Hello China

或者

/* 函数功能:用字符指针作为函数参数,实现字符串拷贝 */
void MyStrcpy(char *dstStr, char *srcStr)
{
while (*srcStr != '\0') /* 若当前srcStr所指字符不是字符串结束标志 */
{
*dstStr = *srcStr; /* 复制字符 */
srcStr++; /* 使srcStr指向下一个字符 */
dstStr++; /* 使dstStr指向下一个存储单元 */
}
*dstStr = '\0'; /* 在字符串dstStr的末尾添加字符串结束标志 */
}

//L10-6

#include  <stdio.h>
unsigned int MyStrlen(const char str[]);
int main()
{
char a[80];
printf("Input a string:");
gets(a);
printf("The length of the string is: %u\n", MyStrlen(a));
return 0;
}
/* 函数功能:用字符型数组作函数参数,计算字符串的长度 */
unsigned int MyStrlen(const char str[])
{
int i ;
unsigned int len = 0; /* 计数器置0 */
for (i=0; str[i]!='\0'; i++)
{
len++; /* 利用循环统计不包括'\0'在内的字符个数 */
}
return len; /* 返回实际字符个数 */
}
//运行结果
Input a string:Hello China
The length of the string is: 11

为防止实参在被调函数中被意外修改,可以在相应的形参前面加上类型限定符const

这样如果在函数体内试图修改形参的值,就会产生编译错误

函数

char *strcpy(char *str1,const char *str2);

char *strcat(char *str1,const char *str2);

返回的都是字符指针str1的值,即存储字符串str1的内存空间的首地址

这样设计的目的是为了增加使用时的灵活性

函数之间的握手(信息交换)是通过函数参数和返回值来实现的

从函数返回字符串指针:

返回指针的函数在定义时需要在函数名的前面加一个 * 号

char *f();定义了一个函数f,该函数的返回值是一个字符指针

char (*f)();定义了一个函数指针f,该指针指向的函数没有形参,返回值是字符型

//L10-7

#include  <stdio.h>
#define N 80
char *MyStrcat(char *dstStr, char *srcStr);
int main()
{
char first[2*N]; /* 这个数组应该足够大,以便存放连接后的字符串 */
char second[N];
printf("Input the first string:");
gets(first);
printf("Input the second string:");
gets(second);
printf("The result is: %s\n", MyStrcat(first, second));
return 0;
}
/* 函数功能:将字符串srcStr连接到字符串dstStr的后面 */
char *MyStrcat(char *dstStr, char *srcStr)
{
char *pStr = dstStr; /* 保存字符串dstStr的首地址 */
/* 将指针移到字符串dstStr的末尾 */
while (*dstStr != '\0')
{
dstStr++;
}
/* 将字符串srcStr复制到字符串dstStr的后面 */
for(; *srcStr!='\0'; dstStr++, srcStr++)
{
*dstStr = *srcStr;
}
*dstStr = '\0'; /* 在连接后的字符串的末尾添加字符串结束标志 */
return pStr; /* 返回连接后的字符串dstStr的首地址 */
}
//运行结果
Input the first string:Hello
Input the second string:China
The result is: HelloChina

const类型限定符:

如果只希望数据传到被调函数内部,而不希望它们在函数内被修改

此时,为了防止数据被意外修改,让函数的功能更加明确

可以使用const对参数进行限定

(1)const放在类型关键字的前面

int a,b;

const int *p = &a;

p是一个指针变量,可以指向一个整型常量(Integer Constant)

*p是一个常量,而p不是

*p是只读的,不可以在程序中被修改

指针变量p的值是可以修改的

对p所指向的变量a进行赋值也是合法的

(2)const放在类型关键字的后面和*变量名的前面

int const *p = &a;

p是一个指针变量,可以指向一个常量整数(Constant Integer)

*p是一个常量,而p不是

与第一种情况是等价的

(3)const放在类型关键字*的后面,变量名的前面

int *const p = &a;

p是一个指针变量,可以指向一个整型数据(Integer)

p是一个常量,而*p不是

p是只读的,不可以在程序中被修改,即不可以让它指向其他变量

但是它所指向的变量的值是可以修改的

(4)一个const放在类型关键字的前面,另一个const放在类型关键字和*变量名之间

const int *const p = &a;

p是一个指针变量,可以指向一个整型常量(Integer Constant)

p和*p都是常量,都是只读的

字符处理函数:

int isdigit(int c) 如果是数字返回真
int isalpha(int c) 如果是字母返回真
int isalnum(int c) 如果是数字或字母返回真
int islower(int c) 如果是小写字母返回真
int issupper(int c) 如果是大写字母返回真
int tolower(int c) 如果是大写字母则转换为小写字母返回
int toupper(int c) 如果是小写字母则转换为大写字母返回
int isspace(int c) 如果是空白字符(换行符\n、空格符 、换页符\f、回车符\r、水平制表符\t、垂直制表符\v)返回真
int iscntrl(int c) 如果是控制字符(水平制表符\t、垂直制表符\v、换页符\f、响铃报警符\a、退格符\b、回车符\r、换行符\n)返回真
int isprint(int c) 如果是包含空格在内的可打印字符($、#、(、)、[、]、{、}、;、:、%)返回真
int isgraph(int c) 如果是除空格以外的可打印字符返回真

//L10-8

#include <stdio.h>
#define N 80
int main()
{
char str[N];
int i, letter = 0, digit = 0, space = 0, others = 0;
printf("Input a string:");
gets(str);
for (i=0; str[i]!='\0'; i++)
{
if (str[i]>='a' && str[i]<='z' || str[i]>='A' && str[i]<='Z')
letter ++; /* 统计英文字符 */
else if (str[i] >= '0' && str[i] <= '9')
digit ++; /* 统计数字字符 */
else if (str[i] == ' ')
space ++; /* 统计空格 */
else
others ++; /* 统计其他字符 */
}
printf("English character: %d\n", letter);
printf("digit character: %d\n", digit);
printf("space: %d\n", space);
printf("other character: %d\n", others);
return 0;
}

//L10-8

#include <stdio.h>
#include <ctype.h>
#define N 80
int main()
{
char str[N];
int i, letter = 0, digit = 0, space = 0, others = 0;
printf("Input a string:");
gets(str);
for (i=0; str[i]!='\0'; i++)
{
if (isalpha(str[i]))
letter ++; /* 统计英文字符 */
else if (isdigit(str[i]))
digit ++; /* 统计数字字符 */
else if (isspace(str[i]))
space ++; /* 统计空格(其实还包含了其他空白字符) */
else
others ++; /* 统计其他字符 */
}
printf("English character: %d\n", letter);
printf("digit character: %d\n", digit);
printf("space: %d\n", space);
printf("other character: %d\n", others);
return 0;
}
//运行结果
Input a string:abcd 12345 (*)
English character: 4
digit character: 5
space: 2
other character: 3

//L10-9

#include <stdio.h>
#include <ctype.h>
#define N 80
int main()
{
char name[N];
int i;
printf("Input a name:");
gets(name); /* 输入名和姓 */
i = 0;
while(!isalpha(name[i])) /* 跳过所有空格,直到遇字母为止 */
{
i++;
}
name[i] = toupper(name[i]); /* 将名的首字母变为大写 */
while (!isspace(name[i])) /* 跳过所有字母,直到遇空格为止 */
{
i++;
}
while (!isalpha(name[i])) /* 跳过所有空格,直到遇字母为止 */
{
i++;
}
name[i] = toupper(name[i]); /* 将姓的首字母变为大写 */
printf("Formatted Name:%s\n", name);
return 0;
}
//运行结果
Input a name:john smith
Formatted Name:John Smith

数值字符串向数值的转换:

C语言提供的字符串转换函数可以将数字字符串转换为整型或浮点数型的数值

使用这些函数,必须在程序开头包含头文件<stblib.h>

double atof(const chat *nPtr) 将nPtr指向的字符串转换为双精度浮点数
int atoi(const char *nPtr) 将nPtr指向的字符串转换为整型数
long atol(const char *nPtr) 将nPtr指向的字符串转换为长整型数

//L10-10

#include <stdio.h>
#include <stdlib.h>
int main()
{
char str[] = {" 123.5"};
int intNum;
long longNum;
double doubleNum;
intNum = atoi(str); /* 字符串转换为整型数 */
longNum = atol(str); /* 字符串转换为长整型数 */
doubleNum = atof(str); /* 字符串转换为双精度实型数 */
printf("intNum = %d\n", intNum);
printf("longNum = %ld\n", longNum);
printf("doubleNum = %f\n", doubleNum);
return 0;
}
//运行结果
intNum = 123
longNum = 123
doubleNum = 123.500000

C语言程序设计(十) 字符串的更多相关文章

  1. Swift语言指南(十)--字符串与字符

    原文:Swift语言指南(十)--字符串与字符 字符串是一段字符的有序集合,如"hellow,world"或"信天翁".Swift 中的字符串由 String ...

  2. 中国大学MOOC-翁恺-C语言程序设计习题集-解答汇总

    中国大学MOOC-翁恺-C语言程序设计习题集 PAT 习题集 02-0. 整数四则运算(10) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standar ...

  3. 《C语言程序设计》编程总结汇总

    <C语言程序设计>编程总结汇总 院系: 专业年级: 班级名称: 学号: 姓名: 指导教师: 完成时间: 自我评价: 计算机科学与技术专业教研室 2018 年秋季学期 第四周编程总结 题目4 ...

  4. 2019年春季学期《C语言程序设计II》课程总结

    2019年春季学期<C语言程序设计II>课程总结 1.课程情况 教学内容 课堂小结 作业安排 优秀作业 备注 1.开学谈心 2.测验数据类型.运算符与表达式的自学情况,并讲解测验题目3.第 ...

  5. 资深程序员推荐必备书籍 《C语言程序设计》

    当下,IT行业发展日趋迅猛,产值成倍增长,高薪的诱惑更是驱使许多人想要进入IT行业发展.为了使大家更全面理解C语言程序设计,由千锋教研院高教产品研发部编著.清华大学出版社倾情出版的<C语言程序设 ...

  6. 【MOOC课程学习记录】程序设计与算法(一)C语言程序设计

    课程结课了,把做的习题都记录一下,告诉自己多少学了点东西,也能给自己一点鼓励. ps:题目都在cxsjsxmooc.openjudge.cn上能看到,参考答案在差不多结课的时候也会在mooc上放出来. ...

  7. C语言程序设计课程设计自查表格

    课程设计自查表格 序号 项目 完成与否(完成打勾) 1 格式是否符合标准(缩进是否规范) 2 是否模块化设计(使用函数分解系统功能) 3 函数名否易懂(不得使用f1(int a1,int a2)这样的 ...

  8. C语言程序设计课程总结

    第一次教授C语言程序设计课程,相比计算机组成原理.arm体系结构等偏向硬件的课程,C的教学方式要灵活一些.计算机组成原理课程偏向理论,哈尔滨工业大学的计算机组成原理是国家精品课,增加了mooc+spo ...

  9. 2018年秋季学期《c语言程序设计》编程总结

    <c语言程序设计>第四周编程总结 <c语言程序设计>第五周编程总结 <c语言程序设计>第六周编程总结 <c语言程序设计>第七周编程总结 <c语言程 ...

随机推荐

  1. cs231n spring 2017 lecture15 Efficient Methods and Hardware for Deep Learning

    讲课嘉宾是Song Han,个人主页 Stanford:https://stanford.edu/~songhan/:MIT:https://mtlsites.mit.edu/songhan/. 1. ...

  2. 德国、日本的制造业为什么不能完全执行SOP?

    在过去几十年,德国.日本的制造企业简直就是"以质取胜"的代名词,一些制造业的CEO非常自豪,甚至在公开场合调侃:大家好,我就是"保质保量"本人,也正因如此,德国 ...

  3. 转载——Python Selenium 常用方法总结

    selenium Python 总结一些工作中可能会经常使用到的API. 1.获取当前页面的Url 方法:current_url 实例:driver.current_url 2.获取元素坐标 方法:l ...

  4. spring配置ConcurrentMap实现缓存

    spring本身内置了对Cache的支持,本次记录的是基于Java API的ConcurrentMap的CacheManager配置. 1.xml文件中增加命名空间 <beans xmlns=& ...

  5. 孙鑫VC视频教程观看记录

    01: 了解了SDK编程,消息队列,消息响应,消息循环,窗口函数等. 02: 可以冒号:父类构造函数和a(1) protected子类可以访问 覆盖:父类子类之间   重载:同一个类中 ::作用域标识 ...

  6. Linux中的一些点

    前言 本文记录一些日常使用linux的一些点. 系统负载评估 理解Linux系统负荷 查看 ps -ef [root@deployer ~]# ps -ef UID PID PPID C STIME ...

  7. List、Set、数组之间的转换

    数组转Collection 使用Apache Jakarta Commons Collections: import org.apache.commons.collections.Collection ...

  8. Linux的date用法

    显示时间是个常用的命令,在写shell脚本中也经常会用到与日期相关文件名或时间显示.无论是linux还是windows下都是date命令. Linux下date命令用法 date [OPTION]… ...

  9. 图解教你如何使用ANT打包java程序

    1:在eclipse中建立如下的工程 值得注意的就是build.xml文件(这个是重点后面会提到) ,其他HelloWorld中的就是一句简单的输出语句 2: 使用build打包(右键然后选择运行), ...

  10. Canvas和svg总结比较

    Canvas 基本用法 getContext() 描边和填充 strokeStyle:设置描边样式fillStyle:设置填充样式stroke():描边fill():填充 绘制矩形 fillRect( ...