C 语言教程 在线

1539C 输入 & 输出

Windows、Unix、Mac不同操作系统的换行问题 回车符\r和换行符\n

一、概念:

换行符‘\n’回车符‘\r’

(1)换行符就是另起一行  --- '\n' 10 换行(newline)

(2)回车符就是回到一行的开头 --- '\r' 13 回车(return)

所以我们平时编写文件的回车符应该确切来说叫做回车换行符  

CR: 回车(Carriage Return) \rLF: 换行(Line Feed) \n

二、应用:

(1)在微软的MS-DOS和Windows中,使用“回车CR('\r')”和“换行LF('\n')”两个字符作为换行符;

(2)Windows系统里面,每行结尾是 回车+换行(CR+LF),即“\r\n”;

(3)Unix系统里,每行结尾只有 换行LF,即“\n”;

(4)Mac系统里,每行结尾是 回车CR 即'\r'。

Mac OS 9 以及之前的系统的换行符是 CR,从 Mac OS X (后来改名为“OS X”)开始的换行符是 LF即‘\n',和Unix/Linux统一了。

三、影响:

(1)一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;

(2)而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。

(3)Linux保存的文件在windows上用记事本看的话会出现黑点。

四、可以相互转换

在linux下,命令unix2dos 是把linux文件格式转换成windows文件格式,命令dos2unix 是把windows格式转换成linux文件格式。

在不同平台间使用FTP软件传送文件时, 在ascii文本模式传输模式下, 一些FTP客户端程序会自动对换行格式进行转换. 经过这种传输的文件字节数可能会发生变化.

 如果你不想ftp修改原文件, 可以使用bin模式(二进制模式)传输文本。

一个程序在windows上运行就生成CR/LF换行格式的文本文件,而在Linux上运行就生成LF格式换行的文本文件。

1538C 输入 & 输出

gets()与fgets()

gets()

gets函数原型:char*gets(char*buffer);//读取字符到数组:gets(str);str为数组名。

gets函数功能:从键盘上输入字符,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中。

读取的换行符被转换为null值,做为字符数组的最后一个字符,来结束字符串。

注意:gets函数由于没有指定输入字符大小,所以会无限读取,一旦输入的字符大于数组长度,就会发生内存越界,

从而造成程序崩溃或其他数据的错误。

fgets()

fgets函数原型:char *fgets(char *s, int n, FILE *stream);//我们平时可以这么使用:fgets(str, sizeof(str), stdin);

其中str为数组首地址,sizeof(str)为数组大小,stdin表示我们从键盘输入数据。

fgets函数功能:从文件指针stream中读取字符,存到以s为起始地址的空间里,直到读完N-1个字符,或者读完一行。

注意:调用fgets函数时,最多只能读入n-1个字符。读入结束后,系统将自动在最后加'\0',并以str作为函数值返回。

借用教程实例,我把char str[100] 改为 char str[5]

#include <stdio.h>

int main( )
{
    char str[5];

    printf( "Enter a value :");
    gets( str );

    printf( "\nYou entered: ");
    puts( str );
    return 0;
}

如果输入123(长度小于5)结果为:

Enter a value :123

You entered: 123

如果输入123456789(长度大于5)结果为:

Enter a value :123456789

You entered: 123456789

虽然正常显示了,但是系统提示程序崩溃了

如果不能正确使用gets()函数,带来的危害是很大的,就如上面我们看到的,输入字符串的长度大于缓冲区长度时,并没有截断,原样输出了读入的字符串,造成程序崩溃。

考虑到程序安全性和健壮性,建议用fgets()来代替gets()。如:

#include <stdio.h>

int main( )
{
    char str[5];

    printf( "Enter a value :");
    fgets( str,5,stdin );      //fgets()函数;

    printf( "\nYou entered: ");
    puts( str );
    return 0;
}

1537C 输入 & 输出

linux系统下需要这样编译:不支持 gets 与 puts, 需要用 fgets 和 fputs。

将以下代码放到 test.c 文件:

#include <stdio.h>

int main()
{
    char c[100];
    printf("Enter a value:");
    fgets( c,100,stdin );

    printf("\nyou entered:");
    fputs( c,stdout );

    return 0;

}

编译执行以上代码,输出结果为:

# gcc test.c 
# ./a.out 
Enter a value:facesho

you entered:facesho

1536C typedef

typedef 还有一个作用,就是为复杂的声明定义一个新的简单的别名。用在回调函数中特别好用:

1. 原声明:int *(*a[5])(int, char*);

在这里,变量名为 a,直接用一个新别名 pFun 替换 a 就可以了:

typedef int *(*pFun)(int, char*);

于是,原声明的最简化版:

pFun a[5];

2. 原声明:void (*b[10]) (void (*)());

这里,变量名为 b,先替换右边部分括号里的,pFunParam 为别名一:

typedef void (*pFunParam)();

再替换左边的变量 bpFunx 为别名二:

typedef void (*pFunx)(pFunParam);

于是,原声明的最简化版:

pFunx b[10];

其实,可以这样理解:

typedef int *(*pFun)(int, char*); 

typedef 定义的函数 pFun,为一个新的类型,所以这个新的类型可以像 int 一样定义变量,于是,pFun a[5]; 就定义了 int *(*a[5])(int, char*);

所以我们可以用来定义回调函数,特别好用。

另外,也要注意,typedef 在语法上是一个存储类的关键字(如 auto、extern、mutable、static、register 等一样),虽然它并不真正影响对象的存储特性,如:

typedef static int INT2; // 不可行

编译将失败,会提示“指定了一个以上的存储类”。

1535C typedef

typedef 为数组去别名:

typedef int A[6];

表示用 A 代替 int [6]

即:A a; 等于 int a[6];