1.为什么大家都说不要使用gets()?
跟fgets() 不同, gets() 不能被告知输入缓冲区的大小,因此不能避免缓冲区的溢出。标准库的fgets() 函数对gets() 作了很大的改进, 尽管它仍然不完善。
2. fgetops/fsetops 和ftell/fseek 之间有什么区别? fgetops()和fsetops() 到底有什么用处?
ftell() 和fseek() 用长整型表示文件内的偏移(位置), 因此,偏移量被限制在20亿(231 ¡ 1) 以内。而新的fgetpos() 和fsetpos()函数使用了一个特殊的类型定义fpos t 来表示偏移量。这个类型会适当选择, 因此, fgetpos() 和fsetpos可以表示任意大小的文件偏移。fgetpos() 和gsetpos() 也可以用来记录多字节流式文件的状态。
3. 一旦使用freopen() 之后, 怎样才能恢复原来的stdout (或stdin)?
没有什么好办法。如果你需要恢复回去,那么最好一开始就不要使用freopen()。可以使用你自己的可以随意赋值的输出(输入) 流变量, 而不要去动原来的输出(或输入)流。
有一种不可移植的办法, 可以在调用freopen() 之前保存流的信息,以便其后恢复原来的流。一种办法是使用系统相关的调用如dup(), dup2() 等。另一种办法是复制或查看FILE 结构的内容,但是这种方法完全没有可移植性而且很不可靠。
4. 怎样正确的读取二进制文件?我有时看到0x0a 和0x0d 混淆了,而且如果数据中包含0x1a的话, 我好像会提前遇到EOF。
读取二进制数据文件的时候你应该用“rb” 调用fopen(), 确保不会发生文本文件的解释。类似的,写二进制文件时, 使用“wb”。注意文本/二进制区别只是发生在文件打开时: 一旦文件打开之后, 在其上调用何种I/O函数无关紧要。
5.怎样把数字转为字符串(与atoi 相反)?有itoa() 函数吗?
用sprintf() 就可以了。不需担心用sprintf() 会小题大作,也不必担心会浪费运行时间或代码空间; 实践中它工作得挺好。
6. 怎样在C 程序中取得当前日期或时间?
只要使用函数time(), ctime(), localtime() 和/或strftime()就可以了。下面是个简单的例子:
#include
#include
int main()
{
time_t now;
time(&now);
printf("It’s %s", ctime(&now));
return 0;
}
用函数strftime() 可以控制输出的格式。
7. 我需要一个随机数生成器。
标准C 库函数就有一个: rand()。
8. 怎样获得在一定范围内的随机数?
直接的方法是
rand() % N
试图返回从0 到N ¡ 1 的数字。但这个方法不好,因为许多随机数发生器的低位比特并不随机。一个较好的方法是:
(int)((double)rand() / ((double)RAND_MAX + 1) *N)
如果你不希望使用浮点, 另一个方法是:
rand() / (RAND_MAX / N + 1)
两种方法都需要知道RAND MAX, 而且假设N 要远远小于RAND MAX。RAND MAX在ANSI 里#define 在<</I>stdlib.h>。顺便提一下, RAND MAX是个常数, 它告诉你C 库函数rand() 的固定范围。你不可以设RAND MAX 为其它的值, 也没有办法要求rand()返回其它范围的值。
如果你用的随机数发生器返回的是0 到1 的浮点值, 要取得范围在0 到N ¡1内的整数, 只要将随机数乘以N 就可以了。
9. 我不断得到库函数未定义错误, 但是我已经#inlude 了所有用到的头文件了。
通常, 头文件只包含外部说明。某些情况下, 特别是如果是非标准函数, 当你连接程序时,需要指定正确的函数库以得到函数的定义。#include 头文件并不能给出定义。
10. 虽然我在连接时明确地指定了正确的函数库, 我还是得到库函数未定义错误。
许多连接器只对对象文件和函数库进行一次扫描,同时从函数库中提取适合当前未定义函数的模块。所以函数库和对象文件(以及对象文件之间) 的连接顺序很重要; 通常,你希望最后搜索函数库。例如, 在Unix 系统中, 把-l 参数放在命令行的后部。
11. 做一些简单的三角函数运算, 也引用了#include<</I>math.h>, 可是一直得到编译错误“undefined: sin” (函数sin未定义)。
确定你真的连接了数学函数库(math library)。例如, 在Unix 或Linux 系统中,有一个存在了很久的bug, 你需要把参数-lm 加在编译或连接命令行的最后。
12. 怎样取整数?
最简单、直接的方法:
(int)(x + 0.5)
这个方法对于负数并不正常工作。可以使用一个类似的方法:
(int)(x < 0 ? x - 0.5 : x + 0.5)
13. 为什么当n 为long int, printf("%d", n);编译时没有匹配警告?我以为ANSI 函数原型可以防止这样的类型不匹配。
当一个函数用可变参数时,它的原型说明没有也不能提供可变参数的数目和类型。所以通常的参数匹配保护不适用于可变参数中的可变部分。编译器不能执行内含的转换或警告不匹配问题。
14. 怎样写一个有可变参数的函数?
下面是一个把任意个字符串连接起来的函数, 结果存在malloc 的内存中:
#include
#include
#include
char *vstrcat(const char *first, ...)
{
size_t len;
char *retbuf;
va_list argp;
char *p;
if(first == NULL)
return NULL;
len = strlen(first);
va_start(argp, first);
while((p = va_arg(argp, char *)) != NULL)
len += strlen(p);
va_end(argp);
retbuf = malloc(len + 1);
if(retbuf == NULL)
return NULL;
(void)strcpy(retbuf, first);
va_start(argp, first);
while((p = va_arg(argp, char *)) != NULL)
(void)strcat(retbuf, p);
va_end(argp);
return retbuf;
}
调用如下:
char *str = vstrcat("Hello, ", "world!", (char*)NULL);
注意最后一个参数的类型重置。注意调用者要释放返回的存储空间, 那是用malloc 分配的。
15. 怎样写类似printf() 的函数, 再把参数转传给printf()去完成大部分工作?
用vprintf(), vfprintf() 或vsprintf()。
下面是一个error() 函数, 它列印一个出错信息, 在信息前加入字符串“error: ”
和在信息后加入换行符:
#include
#include
void error(const char *fmt, ...)
{
va_list argp;
fprintf(stderr, "error: ");
va_start(argp, fmt);
vfprintf(stderr, fmt, argp);
va_end(argp);
fprintf(stderr, "\n");
}
16. 为什么编译器不让我定义一个没有固定参数项的可变参数函数?
标准C 要求用可变参数的函数至少有一个固定参数项, 这样你才可以使用vastart()。所以编译器不会接受下面定义的函数:
int f(...)
{
...
}
17. 我有个接受float 的可变参函数, 为什么va arg(argp, float)不工作?
“参数默认晋级” 规则适用于在可变参数中的可变动部分: 参数类型为float 的总是晋级(扩展)到double, char 和short int 晋级到int。所以va arg(arpg, float)是错误的用法。应该总是用vaarg(arpg, double)。同理, 要用va arg(argp, int) 来取得原来类型是char, short 或int的参数。基于相同理由, 传给va start() 的最后一个“固定” 参数项的类型不会被晋级。
18. va arg() 不能得到类型为函数指针的参数。
宏va arg()所用的类型重写不能很好地操作于象函数指针这类过度复杂的类型。但是如果你用typedef 定义一个函数指针类型,那就一切正常了。
19. 怎样实现一个可变参数函数, 它把参数再传给另一个可变参数函数?
通常来说, 你做不到。理想情况下, 你应该提供另一个版本的函数, 这个函数接受va list指针类型的参数。类似于vfprintf()。如果所有的参数必须完整的传给另一个函数, 或者你不能重写另一个函数为一个接受va list指针类型参数的函数, 这并没有一个可移植的解决方法。也许可以通过求助于机器的汇编语言来实现。