《C程序设计语言》中首次提到EOF是在下面这段代码:
#include <stdio.h>
/* copy input to output; 1st version */
main()
{
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
}
这段代码作用从stdin获取一个字符输出到stdout。
一般看到这里会有疑惑:这里变量c为何要定义为int型而不是char型?为何while里的判断条件是“c != EOF”而不是“c != '\0'”?
书中原话是这么说的:
这里需要解决如何区分文件中有效数据与输入结束符的问题。C语言采取的解决方法是:在没有输入时,getchar函数将返回一个特殊值,这个特殊值与任何实际字符都不同。这个值称为EOF(end of file,文件结束)。我们在声明变量c的时候,必须让它大到足以存放getchar函数返回的任何值。这里之所以不把c声明称char类型,是因为它必须足够大,除了能存储任何可能的字符外还要能存储文件结束符EOF。因此,我们将c声明成int类型。
EOF定义在头文件<stdio.h>中,是个整型数,其具体是什么并不重要,只要它与任何char类型的值都不相同即可。这里使用符号常亮,可以确保程序不需要依赖于其对应的任何特定的值。
以上解释得很清楚了,如果将c定义成char,则不能获取到char的取值范围之外的值,使用 “char c; c = getchar();”可能会发生意料之外的数据转换(类似int a = -1; unsigned int b = a;)。
EOF不与任何实际字符相同,所有的字符都能在ascii表里128个字符里找到,acsii码表对应的十进制数从0-128,而十进制数0对应的字符就是空字符'\0'(null),EOF不存在这128个字符里,即不属于字符,而上述代码需要判断的是获取到的不是字符时结束while,空字符'\0'虽然无法从键盘输入,但也属于字符,所以此处用“c != EOF”而不是“c != '\0'”。如果需要判断是否空字符或者判断字符串是否结束则用“c != '\0'”。
EOF的值
那EOF等于多少呢,通过代码测试printf("EOF = %d\n", EOF);可以得知EOF的值为-1。
空字符'\0'
其值为0,用以标记字符串的结束,占一个字节(空字符也是字符)。
定义一个字符串:
char str[6] = {"abcdef"};
printf("str = %s strlen(str) = %d c=%d\n", str, strlen(str), *(str + strlen(str)));
*(str+6) = 'g';
printf("str = %s strlen(str) = %d c=%d\n", str, strlen(str), *(str + strlen(str)));
str[4] = '\0';
printf("str = %s strlen(str) = %d c=%d\n", str, strlen(str), *(str + strlen(str)));
输出结果:
str = abcdef strlen(str) = 6 c=0
str = abcdefg strlen(str) = 7 c=0
str = abcd strlen(str) = 4 c=0
strlen以‘\0’作为字符串结束标志,即使‘\0’后面仍有字符,也不会再统计。即空字符不是字符串的一部分,所以字符串的长度并不包含空字符。
printf使用%s格式输出,同strlen一样也是一直到结束符'\0'就终止,如果字符数组里包含的字符不含'\0',则会继续往下查找,当然这就是数组越界了,容易引起未知错误,实际项目中不可这样使用。
关于<string.h>下的库函数strcpy、strcmp、strstr、strcat关于'\0'处理的简单介绍:
以char *strcpy(char *dst, char const *src)为例,若源字符串src的长度比字符数组dst的长度要长,这样src的所有字符仍然会被复制到dst,同样这里也属于数组越界。可用如下代码测试:
char src[10] = {"ABCDEFGHIK"};
char dst[5] = {0};
strcpy(dst, src);
printf("src = %s\n dst = %s\n", src, dst);
输出:
src = ABCDEFGHIK
dst = ABCDEFGHIK
所以复制的内容需要小于dst的长度,在最后手动赋值'\0';
在dst的长度大于等于src的情况下,strcpy会将src的第一个字符一直到遇到结束符为止的所有字符copy到dst,用如下代码测试:
int i = 0;
char src[10] = {"ABCDEFGHIK"};
char dst[10] = {0};
src[6] = '\0';
src[9] = '\0';
strcpy(dst, src);
printf("src = %s\n dst = %s\n", src, dst);
for(i = 0; i<10; i++)
printf("src[%d]=%d\t", i, src[i]);
printf("\n");
for(i = 0; i<10; i++)
printf("dst[%d]=%d\t", i, dst[i]);
printf("\n");
输出结果:
src = ABCDEF
dst = ABCDEF
src[0]=65 src[1]=66 src[2]=67 src[3]=68 src[4]=69 src[5]=70 src[6]=0 src[7]=72 src[8]=73 src[9]=0
dst[0]=65 dst[1]=66 dst[2]=67 dst[3]=68 dst[4]=69 dst[5]=70 dst[6]=0 dst[7]=0 dst[8]=0 dst[9]=0
可以看到src[6]即为结束符,虽然后面仍然有有效字符,但是不会复制。