字符串的表示方法及输出
首先要说的是C语言中没有字符串string这样的数据类型,所以我们要表示字符串,可以使用字符数组,不过,输出字符串时,必须用%s来输出,下面举个例子
例1:
# include <stdio.h>
int main(void) {
char a[9] = {'g', 'o', 'o', 'd', '!', '\0'};//表示字符数组,可以事先指定数组长度,也可以像下面的代码一样,不指定数组长度
char b[] = {'C', 'o', 'n', 's', 'o', 'l', 'e', '\0'};
printf("%s\n%s\n", a, b);
return 0;
}
输出结果为:
需要说明的是,用字符数组表示字符串,在字符数组的末尾必须加上’\0’,如果不加,那字符数组就是仅仅表示多个字符,而不是表示字符串,像上面的那个程序,如果把两个数组里的’\0’去掉,则输出结果会变成这样:
当然,出现的乱码可能会在进行新的执行时有所改变
在对以字符数组表示的字符串赋初值的时候,还可以使用双引号,示例如下
例2:
# include <stdio.h>
int main(void) {
char a[9] = "good!";
char b[] = "Console";
printf("%s\n%s\n", a, b);
return 0;
}
输出结果为:
需要指出的是,此时数组内仍包括这’\0’这个字符,即a数组中有6个元素被赋予初值,b数组内有8个元素,等下面说了一些字符串操作的函数后,会再举例说明的
表示字符串变量,还可以用指针形式表示示例如下
例3:
# include <stdio.h>
int main(void)
{
char * a = "good!";
char * b = "Console";
printf("%s\n%s\n", a, b);
return 0;
}
输出结果为:
在这个程序中,a和b都是指针,5、6行表示其初始化为一个字符串常量,但是如果需要修改字符串,则需要字符数组
当然,需要注意的是,字符串可以表示为char *形式,但char *不一定是字符串,char *的本意是指向字符的指针,它可能指向字符数组,但只有它指向的字符数组末尾有\0时,它指向的才可以称为字符串
还有,在使用字符指针表示字符串时,需要注意几点,比如说字符串赋值,像下面的代码:
char * s = "good!";
char * t;
t = s;
这样并没有产生新的字符串,而是让指针t也指向了指针s所指向的字符串,对s的任何操作就是对t的操作,反之亦然
字符串的输入
当使用scanf输入字符串时,scanf会在遇到空格、回车和Tab时,一个输入就会停止,下面举几个例子
例4.1:
# include <stdio.h>
int main(void)
{
char a[10];
printf("请输入一个字符串:\n");
scanf("%s", a);
printf("输出该字符串为:%s\n", a);
return 0;
}
示例输出结果为:
输入字符串与输出相同,也是使用%s,这里a不需要使用&,原因在数组那块儿说过了
另举一个错误的输入结果:
这个是字符串中间加空格的错误,加Tab和回车的例子这里就不说了
scanf在输入字符串时,如果在其%s写成这样——%9s,则表示scanf最多识别输入的9个字符,%与s之间最大是数组元素个数减一,多余的字符不会识别,直接舍弃
另外需要注意的是下面的代码是不正确的
char * s;
scanf(“%s”, s);
因为s这个字符指针没有赋初值,所以它指向的位置不确定,有时这样的代码可以正确,但在另一些时候是会出错的,最好不要这么写
char a[10] = “”,这是个空字符串,其内部只有一个元素\0,即a[0] = ‘\0’,如果这样,char a[] = “”,那该数组的长度就会是1,
字符串数组
我们用字符数组表示字符串,那又需要什么表示字符串数组呢?可以这样,char ** a,a是一个指针,它指向另一个指针,而这个所谓的“另一个指针”指向一个字符或字符串;也可以这样char a[][],使用二维数组表示,当然,使用二维数组时,第二个维必须有确定的大小,如果没有,则会出错,举个例子
例4.2
# include <stdio.h>
int main(void)
{
char a[][9] = {"good", "morning"};
return 0;
}
第五行表示一个数组a,其元素都是char [9],即a[0]可表示为一个长度为9的字符数组,a[1]、a[2]等等皆是如此
表示字符串数组,还可以这样,char * a[],其中,a[0]等元素是char *类型,即数组元素分别是a[0]、a[1]等等,每个元素其实是一个指向字符或字符串的指针,它和上面的char a[][]是不一样的
还有在使用编译器新建一个工程,选择控制台应用程序,下一步后再选择新建一个简单的程序,就会出现这样的代码:main(int argc, char *argv[]),在mian函数中有两个参数,一个是整数参数argc,一个是字符串数组参数argv,这个整数表示字符串数组的长度,下面用一个简单的程序查看一下这个字符串数组的内容
例4.3
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
for (i=0; i<argc; i++)
{
printf("%d:%s\n", i, argv[i]);
}
return 0;
}
在C-Free5.0下的输出结果为:
其中C:\Users\CC\Desktop\sss\mingw5\sss.exe是可执行文件所在的路径
在gcc下运行的结果为:
如果在./test后边加空格后添加其他字符,则会出现这样的情况:
argv接收的是命令后面的字符串,前边的0、1、2等是argc所获取的值
单个字符的输入及输出函数,输入单个字符需要用的getchar()函数,输出需要用到putchar()函数,
int getchar(void);
从标准输入都一个字符
返回类型是int是为了返回EOF(-1)
结束程序:
Windows下——Ctrl+Z
Linux下——Ctrl+D
int putchar(int c);
向标准输出写一个字符
返回值为写了几个字符的个数,EOF(-1)表示写失败
举例如下
例5:
# include <stdio.h>
int main(int argc, char const *argv[])
{
int ch;
while ((ch = getchar()) != EOF)
{
putchar(ch);
}
printf("鸟哥的LINUX私房菜\n");
return 0;
}
输入什么字符或字符串就会原样输出,当然,是一个个输入,最终一个个输出,windows环境下只有Ctrl+Z才能正常结束程序(说正常结束,是因为按Ctrl+C也可以直接结束程序,但这种结束不是正常的,像下面的printf语句不会被执行,而正常结束是会执行的,Ctrl+C适用于windows和Linux系统)
字符串操作函数
C语言在标准库中提供了一些对字符串进行操作的函数,这些函数的原型都在string.h头文件中,即在使用这些函数时,需要加# include <string.h>
strlen函数
size_t strlen(const char * s);
功能是获取字符串的长度
返回的是字符串的长度(不包括结尾的\0)
下面举个例子
例6:
# include <stdio.h>
# include <string.h>
int main(int argc, char const *argv[])
{
char a[] = "good!";
printf("strlen=%lu\n", strlen(a));
printf("sizeof=%lu\n", sizeof(a));
return 0;
}
输出结果为:
在C-Free5.0中,对strlen函数
跳至声明,则是这样
_CRTIMP size_t __cdecl __MINGW_NOTHROW strlen (const char*) __MINGW_ATTRIB_PURE;
我们可以推测出strlen的函数体
其函数体为
size_t strlen(const char * s)
{
int cnt = 0;
while (s[cnt] != '\0')
{
cnt++;
}
return cnt;
}
最终下面这个程序
# include <stdio.h>
# include <string.h>
//设一个函数mylen();
size_t mylen(const char * s)
{
int cnt = 0;
while (s[cnt] != '\0')
{
cnt++;
}
return cnt;
}
int main(int argc, char const *argv[])
{
char a[] = "good!";
// printf("strlen=%lu\n", strlen(a));
printf("strlen=%lu\n", mylen(a));
printf("sizeof=%lu\n", sizeof(a));
return 0;
}
得到的结果与使用strlen函数功能相同
strcmp函数
int strcmp(const char * s1, const char * s2);
功能是比较两个字符串
返回值为:
0:当s1 == s2
1:当s1>s2
2:当s1<s2
这个字符串的大小怎么说,举个例子说明一下
例7:
# include <stdio.h>
# include <string.h>
int main(int argc, char const *argv[])
{
char a[] = "asd";
char b[] = "asd";
char c[] = "bsd";
char d[] = "Asd";
char e[] = "asd ";
printf("%d\n", strcmp(a, b));
printf("%d\n", strcmp(a, c));
printf("%d\n", strcmp(a, d));
printf("%d\n", strcmp(a, e));
return 0;
}
第一次C-Free5.0下输出结果为:
(然而,后来我有进行了几次运行,发现结果竟然和gcc下的结果一样了,不知什么情况,下面的那段文字,是我对一开始两个不同结果的解释,至于为什么C-Free下第一次结果不同,而第二天再次运行时,又出现这样的结果,实在是不清楚,以后若是知道,定会在此说明)
gcc下输出的结果为:
两个编译器输出的结果不同,下面一一分析一下:
通过这个例子,我们可以发现,所谓的比较字符串大小,实际上是比较其所有字符的ASCII表的值的大小之和,所不同的是,C-Free只要有大小,只会返回其大小的逻辑值(0、1、-1),而gcc返回的是字符串ASCII表值得总和的差
因为字符串a与b相等,所以两个结果返回的都是0;而字符串c的ASCII值得和比a的ASCII的值大1,所以gcc返回的是a的ASCII值减去b的ASCII值得差,即-1,而C-Free是因为a小于b,所以返回的是-1,这是上面两个结果
这两个值相同的原因;下面,字符串d与a的不同在于其中一个字符是大写的A,因为小写a和大写A的ASCII值分别是97和65,其差值是32,所以gcc得到的结果是32,C-Free得到的结果是1;同理,字符串e比a多一个空格,而空格的ASCII值恰好是32,所以gcc得到的是-32,C-Free得到的是-1,
下面各处这个函数的函数体
int strcmp(const char * s1, const char * s2)
{
while (*s1 == *s2 && *s1 != '\0')
{
s1++;
s2++;
}
return *s1-*s2;
}
strcpy函数
char * strcpy(char * restrict dst, char * restrict src);
功能是把src的字符串拷贝到dst (即src是源操作数,dst是目的操作数)
restrict表明src和dst不重叠
(注:所谓不重叠,是指二者所占的内存单元之间的间隙必须大于src的字符串长度,即假设src占5个字节的内存单元,而dst原来指向的是src所占的5个单元的前面的一个单元,而其到src的首个单元之间的间距小于src所占的单元个数,则dst在复制src的内容时,会占用一部分src原本的内存单元,这个是不被允许的,这个其实是C99的标准)
返回的是dst
经常会这样用
char * dst = (char *)malloc(strlen(src)+1);//加1是为了包含数组最后的\0
strcpy(dst, src);
下面举个例子
例8:
# include <stdio.h>
# include <string.h>
char * mycpy(char * dst, const char * src)
{
char * ret = dst;
while (*dst++ =*src++)
;
*dst = '\0';
return ret;
}
int main(int argc, char const *argv[])
{
char a[] = "asd";
char a1[] = "";
char b[] = "aaa";
char b1[] = "";
strcpy(a1, a);
mycpy(b1, b);
printf("a1=%s\n", a1);
printf("b1=%s\n", b1);
return 0;
}
输出结果为:
由以上代码可知,strcpy的函数体即mycpy函数函数体
注:这些函数的函数体不止一种形式,这里仅列出一种
strcat函数
char * strcat(char * restrict s1, const char * restrict s2);
功能是把s2拷贝到s1的后面接成一个长的字符串
返回的是s1
这个函数要求s1必须有足够大的空间
所谓的接成新的字符串,假设有两个字符串a[10] = “good”,b =[10] “morning”,要知道数组的末尾是一个0(或者说\0),当对这两个字符串进行strcat函数操作时,b字符串中的m会到达a字符串中的\0位置,然后后面的字符串依次跟过来
无论是strcpy还是strcat,都有一个不安全的地方,就是空间可能不够
下面举个例子
例9:
# include <stdio.h>
# include <string.h>
char * mycat(char * s1, const char * s2)
{
int i, j;
i = strlen(s1);
j = 0;
while(s1[i++] = s2[j++])
;
return s1;
}
int main(void)
{
char s1[20] = "good ";
char s2[] = "morning";
char a[20] = "How ";
char b[] = "are you";
mycat(s1,s2);
strcat(a, b);
printf("%s\n", s1);
printf("%s\n", a);
return 0;
}
输出结果为:
由上可知,strcat的函数体为:
char * strycat(char * s1, const char * s2)
{
int i, j;
i = strlen(s1);
j = 0;
while(s1[i++] = s2[j++])
;
return s1;
}
当然,有安全的函数,情况是这样的
char * strncpy(char * restrict dst, const char * restrict src, size_t n);
char * strncat(char * restrict s1, const char * restrict s2, size_t n);
这个n表示最多拷过去(连接)n个字符,如果有多余,或将多余的字符忽视
int strncmp(const char * s1, const char * s2, size_t n);
对于strcmp来说,n的存在是为了判断是比较几个字符,假如我们想看一下某个字符串的前几位是不是和另一个字符串相同,就可以用
n来控制字符个数
字符串搜索函数
char * strchr(const char * s, int c);//从左往右找
char * strrchr(const char * s, int c);//从右往左找
返回NULL表示没有找到
举个例子
例10:
# include <stdio.h>
# include <string.h>
int main(int argc, char const *argv[])
{
char a[] = "good";
char * p = strchr(a, 'o');
printf("%s\n", p);
return 0;
}
输出结果为:
需要注意的是,在good这个单词中,o有两个,我们这个程序查到的是第一个,那么,如何查到第二个o呢?只需在上个程序的第八行添加这样的语句:p = strchr(p+1, 'o'); 即可
如果想把查到的后面的字符串复制到另一个字符串里边,可以这样做
例11:
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
int main(int argc, char const *argv[])
{
char a[] = "good";
char * p = strchr(a, 'o');
char * t = (char *)malloc(strlen(p)+1);
strcpy(t, p);
printf("%s\n", t);
free(t);
// p = strchr(p+1, 'o');
// printf("%s\n", p);
return 0;
}
输出结果同例10
这个是输出了所查到的字符后面的字符串,那如何输出所查到字符的前边的字符串呢?可以这样
例12:
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
int main(int argc, char const *argv[])
{
char a[] = "good";
char * p = strchr(a, 'o');
char c = *p;
*p = '\0';
char * t = (char *)malloc(strlen(a)+1);
strcpy(t, a);
printf("%s\n", t);
free(t);
return 0;
}
输出结果为:
【所有代码均在windows系统下C-Free5.0下运行通过】
(如有错误,敬请指正)