C++高级参考手册04
- 格式:pdf
- 大小:641.55 KB
- 文档页数:26
版权说明:本资料内容摘录自《C程序设计语言(第二版)》K&R著 徐宝文 李志译 尤晋元审校 机械工业出版社出版 一书。
版权属原作者和出版社所有。
制作本资料为了我本人学习和参考,非商业用途。
建议读者阅读原书学习比较好,它更详细。
目录:A.12 预处理主要介绍预处理器的功能和预处理的过程。
A.12.1三字符序列主要介绍 ??=, ??(, ??<等三字符序列。
A.12.2 行连接主要介绍反斜杠\结束的指令行处理过程。
A.12.3 宏定义和扩展主要介绍#define 标识符 记号序列,#define 标识符(标识符表opt) 记号序列,#undef 标识符,还有#和##等一些东西,有一些例子。
A.12.4 文件包含主要介绍#include <文件名>和#include “文件名”指令。
A.12.5 条件编译介绍#if 常量表达式/#ifdef 标识符/#ifndef 标识符,#elif 常量表达式,#else,#endif语句。
A.12.6 行控制介绍#line指令。
A.12.7 错误信息生成介绍#error指令。
A.12.8 pragma介绍#pragma。
A.12.9 空指令介绍空指令#。
A.12.10 预定义名字介绍__LINE__, __FILE__, __DATE__, __TIME__, __STDC__等。
A.12 预处理 返回目录预处理器执行宏替换,条件编译以及包含指定的文件。
以#开头的命令行(“#”前可以有空格)就是预处理器处理的对象。
这些命令行的语法独立于语言的其它部分,他们可以出现在任何地方,其作用可以延续到所在编译单元的末尾(与作用域无关)。
行边界是有实际意义的;每一行都将单独进行分析(有关如何将行连接起来的详细信息参考A.12.2节)。
对预处理器而言,记号可以是任何语言记号,也可以是类似于#include指令(参见A.12.4节)中表示文件名的字符序列。
C语言标准参考手册C语言是一种广泛应用于系统软件开发和嵌入式系统领域的高级编程语言。
为了确保C语言的标准化和统一性,C语言标准参考手册被广泛采纳和使用。
在本篇文章中,将向您介绍C语言标准参考手册的结构和内容,并探讨其在C语言开发中的重要性。
一、引言C语言标准参考手册是由国际标准化组织(ISO)和美国国家标准学会(ANSI)联合发布的C语言的标准文档。
其目的是为了促进不同平台上的代码可移植性,并确保C语言在不同系统中的一致性。
二、手册结构C语言标准参考手册主要由以下几个部分组成:1. 标准库说明:该部分介绍了C语言标准库中提供的函数、宏等各种功能,并详细描述了各种库函数的使用方法和参数。
2. 语言特性说明:该部分介绍了C语言的语法、语义、数据类型、控制流以及各种运算符等基本语言特性。
3. 标准宏定义:该部分列举了C语言标准中定义的各种宏,并解释了它们的作用和用法。
4. 标准兼容性:该部分描述了C语言标准在不同编译器中的兼容性问题,以及应遵循的最佳实践和常见的编译错误。
5. 编译指令和预处理器:该部分介绍了C语言中的编译指令、预处理器以及它们的功能和使用方法。
三、内容详解1. 标准库说明:C语言标准库包括标准输入输出库(stdio.h)、数学库(math.h)、字符串处理库(string.h)等。
标准库中的函数提供了丰富的功能,比如输入输出操作、数学计算、字符串处理、内存管理等。
2. 语言特性说明:C语言具有丰富的语言特性,包括基本的数据类型(整型、浮点型、字符型等)、运算符、变量和常量的定义、控制流程(条件语句、循环语句)以及函数定义等。
标准参考手册详细描述了这些特性的语法和语义。
3. 标准宏定义:C语言标准定义了一些宏,这些宏可以用于条件编译和编译器相关的操作。
例如,宏__FILE__和__LINE__可以用于在源代码中插入当前文件名和行号,便于调试和错误定位。
4. 标准兼容性:C语言的标准参考手册对于编译器的实现提供了指导性的要求。
c语言标准库参考手册The C programming language is often praised for its simplicity and efficiency. It is widely used in various industries for developing software applications, operating systems, and embedded systems. One of the key features of C is its rich set of standard library functions that provide ready-to-use solutions for common programming tasks. The C standard library reference manual serves as a comprehensive guide to these functions, detailing their usage, parameters, return values, and potential pitfalls.C 语言经常因其简单和高效而受到赞扬。
它被广泛用于各行各业,用于开发软件应用程序,操作系统和嵌入式系统。
C 的一个关键特点是其丰富的标准库函数集,提供了常见编程任务的即用解决方案。
C 标准库参考手册作为这些函数的全面指南,详细介绍了它们的用法,参数,返回值和潜在陷阱。
For beginner programmers, the C standard library reference manualis an invaluable resource. It helps them understand the fundamental building blocks of the C language and how to leverage the standard library functions to write efficient and robust code. By studying the reference manual, beginners can gain a deeper insight into the innerworkings of C and learn best practices for utilizing its features effectively.对于初学者来说,C 标准库参考手册是一本宝贵的资源。
目录目录 (1)第1章C语言编程 (4)练习1.1 (4)练习1.2 (5)练习1.3 (5)第2章编程初步 (6)习题2.1 (6)习题2.2 (7)习题2.3 (9)习题2.4 (10)第3章条件判断 (12)习题3.1 (12)习题3.2 (14)习题3.3 (19)习题3.4 (21)第4章循环 (24)习题4.1 (24)习题4.2 (26)习题4.4 (27)习题4.5 (29)第5章数组 (31)习题5.1 (31)习题5.2 (33)习题5.3 (35)习题5.4 (36)习题5.5 (39)第6章字符串和文本的应用 (41)习题6.1 (41)习题6.2 (50)习题6.3 (53)习题6.4 (53)第7章指针 (57)习题7.1 (57)习题7.2 (59)习题7.3 (61)习题7.4 (63)习题8.1 (65)习题8.2 (67)习题8.3 (69)习题8.4 (73)第9章函数再探 (79)习题9.1 (79)习题9.2 (80)习题9.3 (83)习题9.4 (85)第10章基本输入输出操作 (87)习题10.1 (87)习题10.2 (89)习题10.3 (91)习题10.4 (92)第11章结构化数据 (95)习题11.1 (95)习题11.2 (99)习题11.3 (103)习题11.5 (114)第12章处理文件 (119)习题12.1 (120)习题12.2 (121)习题12.3 (125)习题12.4 (127)第13章支持功能 (132)习题13.1 (133)习题13.2 (133)习题13.3 (135)《C语言入门经典(第4版)》课后练习参考答案第1章C语言编程练习1.1 编写一个程序,用两个printf()语句别离输出自己的名字和地址。
练习1.2将上一个练习修改成所有的输出只用一个printf()语句。
练习1.3编写一个程序,输出下列文本,格式如下所示:"It's freezing in here," he said coldly.第2章编程初步习题2.1 编写一个程序,提示用户用英寸输入一个距离,然后将该距离值输出为码、英尺和英寸的形式。
C语言参考手册语言ASCII动态内存管理mallocfreerealloc日期和时间工具time字符串库转换成数值格式atoistrtol字符串操作strcpystrcat字符串检验strlenstrcmpstrchrstrstr字符数组操作memsetmemcpy算法qsortbsearch数值rand输入/输出支持文件访问fopenfclose直接输入/输出freadfwrite无格式输入/输出fgetcfputcfgetsfputs有格式输入/输出fscanffprintf文件位置ftellfseekrewindC语言参考手册语言ASCII动态内存管理malloc头文件<stdlib.h>函数原型void*malloc(size_t size);函数说明分配size字节的未初始化内存.为避免内存泄漏,分配的内存需要释放.参数size - 要分配的字节数.返回值成功 - 指向新分配内存的指针.失败 - 空指针.free头文件<stdlib.h>函数原型void free(void*ptr);函数说明解分配之前由malloc()分配的内存.参数ptr - 指向要解分配的内存的指针.返回值(无).示例#include <stdlib.h>int main(void){int*p=malloc(sizeof(int) *4);free(p);}realloc头文件<stdlib.h>函数原型void*realloc(void*ptr, size_t new_size);函数说明重新分配new_size字节大小的内存区域.重新分配按以下二者之一执行:可能的话,扩张或收缩ptr所指向的已存在内存.内容在新旧大小中的较小者范围内保持不变. 若扩张范围,则数组新增部分的内容是未定义的.分配一个大小为new_size字节的新内存块,并复制大小等于新旧大小中较小者的内存区域,然后释放旧内存块.若无足够内存,则不释放旧内存块,并返回空指针.若ptr 是NULL,则行为与调用malloc(new_size)相同.参数ptr - 指向需要重新分配的内存区域的指针.new_size - 数组的新大小.返回值成功 - 指向新分配内存的指针. 原指针ptr被非法化.失败 - 空指针. 原指针ptr保持有效.示例#include <stdio.h>#include <stdlib.h>int main(void){int*pa=malloc(10*sizeof(int));if (pa){printf("%zu bytes allocated. Storing ints: ", 10*sizeof(int));for (int n=0; n<10; n++)printf("%d ", pa[n] =n);}int*pb=realloc(pa, 100*sizeof(int));if (pb){printf("\n%zu bytes allocated, first 10 ints are: ", 100*sizeof(int));for (int n=0; n<10; n++)printf("%d ", pb[n]);printf("\n");free(pb);}elsefree(pa);}输出40 bytes allocated. Storing ints: 0 1 2 3 4 5 6 7 8 9400 bytes allocated, first 10 ints are: 0 1 2 3 4 5 6 7 8 9日期和时间工具time头文件<time.h>函数原型time_t time(time_t*arg);函数说明返回编码成time_t 对象的当前日历时间,并将其存储于arg 指向的time_t对象(除非arg为空指针).参数arg - 指向将存储时间的time_t对象的指针,或空指针.返回值成功 - 编码成time_t对象的当前日历时间.失败 - (time_t)(-1).示例int main(void){time_t result=time(NULL);if(result!=-1)printf("%s(%ju seconds since the Epoch)\n",asctime(gmtime(&result)),(size_t)result);}输出Thu Mar 8 05:40:46 2018(1520487646 seconds since the Epoch)字符串库转换成数值格式atoi头文件<stdlib.h>函数原型int atoi(const char*str);函数说明将字符串转换成整数值.从首个非空白符开始,接收尽可能多的字符以组成合法的整数表示,并将其转换为整数值.若转换的值溢出,则返回值未定义.参数str - 指向字符串的指针.返回值成功 - 对应str内容的整数值.失败 - 0.示例int main(void){printf("%d\n", atoi(" -123junk"));printf("%d\n", atoi("0"));printf("%d\n", atoi("junk")); // 没有可进行的转换printf("%d\n", atoi("2147483648")); // UB: 在int范围之外}输出-123-2147483648strtol头文件<stdlib.h>函数原型long strtol(const char*str, char**str_end, int base);函数说明将字符串转换成整数值.从首个非空白符开始,取尽可能多的字符组成对应进制整数的字符串表示,并将它们转换成一个整数值.若被转换的值溢出,则发生值域错误并返回LONG_MAX 、LONG_MIN 、LLONG_MAX 或LLONG_MIN.参数str - 指向字符串的指针.str_end - 指向指向字符指针的指针.base - 字符串对应整数的进制.返回值成功 - 对应str内容的整数值.失败 - 0.示例int main(void){printf("\"1010\" in bin --> %ld\n", strtol("1010", NULL, 2));printf("\"12\" in oct --> %ld\n", strtol("12", NULL, 8));printf("\"A\" in hex --> %ld\n", strtol("A", NULL, 16));printf("\"junk\" in base36 --> %ld\n", strtol("junk", NULL, 36));}输出"1010" in bin --> 10"12" in oct --> 10"A" in hex --> 10"junk" in base36 --> 926192字符串操作strcpy头文件<string.h>函数原型char*strcpy(char*dst, const char*src);函数说明复制src所指向的字符串(包含'\0'),到首元素为dst所指的字符数组.若dst 不是指向字符数组的指针或src不是指向字符串的指针则行为未定义.若dst数组长度不足则行为未定义.若字符串覆盖则行为未定义.参数dst - 指向要写入的字符数组的指针.src - 指向要复制的字符串的指针.返回值dst的副本.示例int main(void){char*src="Take the test.";char dst[strlen(src) +1]; // +1以适应'\0'strcpy(dst, src);// src[0] = 'M'; // UBdst[0] ='M'; // OKprintf("src = %s\ndst = %s\n", src,dst);}输出src = Take the test.dst = Make the test.strcat头文件<string.h>函数原型char*strcat(char*dst, const char*src);函数说明将src 所指向的字符串的副本附加到dst 所指向的字符串的结尾.若目标数组相对于src 和dst的内容以及'\0'不够大,则行为未定义.若dst或src不是指向字符串的指针,则行为未定义.若字符串重叠,则行为未定义.参数dst - 指向要附加的字符串的指针.src - 指向要复制的字符串的指针.返回值dst的副本.示例int main(void){char dst[50] ="Hello";char src[50] ="World!";strcat(dst, src);strcat(dst, " ...");strcat(dst, " Goodbye World!");puts(dst);}输出Hello World! ... Goodbye World!字符串检验strlen头文件<string.h>函数原型size_t strlen(const char*str);函数说明返回给定字符串的长度.若str不是指向字符串的指针则行为未定义.参数str - 指向要检测的字符串的指针.返回值字符串str的长度.示例#include <stdio.h>#include <string.h>int main(void){const char str[] ="How many characters does this string contain?";printf("without null character: %zu\n", strlen(str));printf("with null character: %zu\n", sizeof str);}输出without null character: 45with null character: 46strcmp头文件<string.h>函数原型int strcmp(const char*lhs, const char*rhs);函数说明以字典序比较两个字符串.若lhs 或rhs不是指向字符串的指针,则行为未定义.参数lhs - 指向要比较的字符串的指针.rhs - 指向要比较的字符串的指针.返回值负值 - lhs 字典序中先出现于rhs.正值 - lhs 字典序中后出现于rhs.零 - lhs 与rhs相等.示例#include <stdio.h>#include <string.h>void demo(const char*lhs, const char*rhs){int rc=strcmp(lhs, rhs);if (rc==0)printf("[%s] equals [%s]\n", lhs,rhs);else if (rc<0)printf("[%s] precedes [%s]\n", lhs,rhs);else if (rc>0)printf("[%s] follows [%s]\n", lhs,rhs);}int main(void){const char*string="Hello World!";demo(string, "Hello!");demo(string, "Hello");demo(string, "Hello there");demo("Hello, everybody!"+12, "Hello,somebody!"+11);}输出[Hello World!] precedes [Hello!][Hello World!] follows [Hello][Hello World!] precedes [Hello there][body!] equals [body!]strchr头文件<string.h>函数原型char*strchr(const char*str, int ch);函数说明寻找ch 在str所指向的字符串中的首次出现位置.若str不是指向字符串的指针,则行为未定义.参数str - 指向字符串的指针.ch - 要搜索的字符.返回值指向str中找到的字符的指针,若未找到该字符则为空指针.示例#include <stdio.h>#include <string.h>int main(void){char target='T';const char*str="Try not. Do, or do not. There is no try.";const char*result=str;while((result=strchr(result, target)) !=NULL){printf("Found '%c' starting at '%s'\n", target, result);++result;}}输出Found 'T' starting at 'Try not. Do, ordo not. There is no try.'Found 'T' starting at 'There is no try.'strstr头文件<string.h>函数原型char*strstr(const char*str, const char*substr);函数说明查找substr 所指的字符串在str所指的字符串中的首次出现的位置.若str 或substr不是指向字符串的指针,则行为未定义.参数str - 指向要检验的字符串的指针.substr - 指向要查找的字符串的指针.返回值指向str中找到的子串首字符的指针.若找不到该子串则为NULL.若substr 指向空字符串,则返回str.示例#include <stdio.h>#include <string.h>void find_str(const char*str, const char*substr){char*pos=strstr(str, substr);if (pos)printf("found '%s' in '%s' at %ld\n", substr, str, pos-str);elseprintf("'%s' was not found in '%s'\n", substr, str);}int main(void){char*str="one twothree";find_str(str, "two");find_str(str, "");find_str(str, "nine");find_str(str, "n");}输出found 'two' in 'one two three' at 4found '' in 'one two three' at 0'nine' was not found in 'one two three'found 'n' in 'one two three' at 1字符数组操作memset头文件<string.h>函数原型void*memset(void*dst, int ch, size_t count);函数说明复制值ch 到dst 所指向对象的首count个字节.若出现dst数组结尾后的访问则行为未定义.若dst为空指针则行为未定义.参数dst - 指向要填充的对象的指针.ch - 填充字节.count - 要填充的字节数.返回值dst的副本.示例#include <stdio.h>#include <string.h>int main(void){char str[] ="ghghghghghghghghghghgh";puts(str);memset(str, 'a', 5);puts(str);}输出ghghghghghghghghghghghaaaaahghghghghghghghghmemcpy头文件<string.h>函数原型void*memcpy(void*dst, const void*src, size_t count);函数说明从src 所指向的对象复制count 个字符到dst所指向的对象.若访问发生在dst数组结尾后则行为未定义.若dst 或src为空指针则行为未定义.若对象重叠,则行为未定义.参数dst - 指向要复制的对象的指针.src - 指向复制来源对象的指针.count - 复制的字节数.返回值dst的副本.示例#include <stdio.h>#include <string.h>int main(void){int arr[3] = {1, 2, 3};int tmp[3];char dst[4];char source[] ="once upon a midnight dreary...";memcpy(dst, source, sizeof dst);for (size_t n=0; n<sizeof dst; ++n)putchar(dst[n]);putchar('\n');memcpy(tmp, arr, 3*sizeof(int));for (size_t n=0; n<3; ++n)printf("%d", tmp[n]);putchar('\n');}输出once123算法qsort头文件<stdlib.h>函数原型void qsort(void*ptr, size_t count, size_t size,int (*comp)(const void*, const void*));函数说明对ptr所指向的数组以升序排序.数组包含count 个长度为size字节的元素.参数ptr - 指向待排序的数组的指针.count - 数组的元素数目.size - 数组每个元素的字节大小.comp - 比较函数.比较函数的签名应等价于如下形式:int comp(const void* a, const void* b);若首个参数小于第二个,则返回负整数值.若首个参数大于第二个,则返回正整数值.若两参数相等,则返回零.返回值(无).示例#include <stdio.h>#include <stdlib.h>#include <limits.h>int compare(const void*a, const void*b){int arg1= (const int) a;int arg2= (const int) b;if (arg1<arg2) return-1;if (arg1>arg2) return1;else return0;// return (arg1 > arg2) - (arg1 < arg2); // 可行的简写// return arg1 - arg2; // 错误的简写(若给出 INT_MIN 则会失败)}int main(void){int ints[] = {-2, 99, 0, -743, 2, INT_MIN, 4};int size=sizeof ints/sizeof*ints;qsort(ints, size, sizeof(int), compare);for (int i=0; i<size; i++)printf("%d\n", ints[i]);}输出-2147483648-743-22499bsearch头文件<stdlib.h>函数原型void*bsearch(const void*key, const void*ptr, size_t count, size_t size,int (*comp)(const void*, const void*));函数说明在ptr 所指向的数组中寻找等于key所指向的元素.数组包含count 个长度为size字节的元素.若数组元素没有初始有序,则行为未定义.参数Key - 指向要查找的元素的指针.ptr - 指向要检验的数组的指针.count - 数组的元素数目.size - 数组每个元素的字节数.comp - 比较函数.比较函数的签名应等价于如下形式:int comp(const void* a, const void* b);若首个参数小于第二个,则返回负整数值.若首个参数大于第二个,则返回正整数值.若两参数相等,则返回零.返回值指向与*key比较相等的指针,在找不到元素时返回空指针.示例#include <stdio.h>#include <stdlib.h>typedef struct{int nr;char const*value;} Data;Data dat[] = {{1, "Foo"}, {2, "Bar"}, {3, "Hello"}, {4, "World"}};int compare(void const*a, void const*b){const Data*const l=a;const Data*const r=b;if (l->nr<r->nr) return-1;else if (l->nr>r->nr) return1;else return0;// return (l->nr > r->nr) - (l->nr <r->nr); // 可行的简洁写法// return l->nr - r->nr; // 错误的简洁写法(若给出INT_MIN就会失败)}int main(void){Data key= { .nr=3 };const Data*res=bsearch(&key, dat, sizeof dat/sizeof dat[0],sizeof dat[0], compare);if (res)printf("No %d: %s\n",res->nr, res->value);elseprintf("No %d not found\n", key.nr);}输出No 3: Hello数值rand头文件<stdlib.h>函数原型int rand();函数说明返回[0, RAND_MAX]范围的随机整数值.参数(无) .返回值[0,RAND_MAX]范围的随机整数值.示例#include <stdio.h>#include <stdlib.h>#include <time.h>int main(void){// 以当前时间为随机数生成器的种子// 若在srand()的调用前使用rand(),则如同以srand(1)播种// srand()中的相同seed产生相同的值数列srand(time(NULL));int random=rand();printf("Random value on [0, %d]: %d\n", RAND_MAX, random);}输出Random value on [0, 2147483647]:1328633460输入/输出支持文件访问fopen头文件<stdio.h>函数原型FILE*fopen(const char*filename, const char*mode);函数说明打开filename所指示的文件,并返回指向关联到该文件的文件流的指针.参数filename - 关联到文件系统的文件名.mode - 访问模式.返回值成功 - 指向新文件流的指针.失败 - 空指针.fclose头文件函数原型函数说明关闭给定的文件流.参数stream - 需要关闭的文件流.返回值成功 - 0.失败 - EOF .示例<stdio .h >int fclose (FILE * stream );#include <stdio.h>#include <stdlib.h>int main(void){int c; // 注意:int,非char,要求处理EOFFILE*fp=fopen("test.txt", "r");if(!fp){perror("File opening failed");return EXIT_FAILURE;}while ((c=fgetc(fp)) !=EOF) // 标准C I/O读取文件循环putchar(c);if (ferror(fp))puts("I/O error when reading");else if (feof(fp))puts("End of file reached successfully");fclose(fp);}直接输入/输出fread头文件<stdio.h>函数原型size_t fread(void*buffer, size_t size, size_t count, FILE*stream);函数说明从给定输入流stream 读取至多count 个对象到数组buffer中.参数buffer - 指向要读取的数组中首个对象的指针.size - 每个对象的字节大小.count - 要读取的对象数.stream - 读取来源的输入文件流.返回值成功读取的对象数,若出现错误或文件尾条件,则可能小于count.若size 或count 为零,则fread返回零且不进行其他动作.fwrite头文件<stdio.h>函数原型size_t fwrite(const void*buffer, size_t size, size_t count, FILE*stream);函数说明写count 个来自给定数组buffer 的对象到输出流stream.参数buffer - 指向数组中要被写入的首个对象的指针.size - 每个对象的大小.count - 要被写入的对象数.stream - 指向输出流的指针.返回值成功写入的对象数,若错误发生则可能小于count.若size 或count 为零,则fwrite返回零并不进行其他行动.示例#include <stdio.h>#include <stdlib.h>enum { SIZE=5 };int main(void){double a[SIZE] = {1, 2, 3, 4, 5};double b[SIZE];int r1, r2;FILE*f1;FILE*f2;f1=fopen("file.bin", "wb");r1=fwrite(a, sizeof a[0], SIZE, f1);printf("wrote %d elements out of %d requested\n", r1, SIZE);fclose(f1);f2=fopen("file.bin", "rb");r2=fread(b, sizeof b[0], SIZE, f2);fclose(f2);printf("read back: ");for (int i=0; i<r2; i++)printf("%f ", b[i]);printf("\n");}输出wrote 5 elements out of 5 requestedread back: 1.000000 2.000000 3.0000004.000000 5.000000 无格式输入/输出fgetc头文件<stdio.h>函数原型int fgetc(FILE*stream);函数说明从给定的输入流读取下一个字符.参数stream - 读取字符的来源.返回值成功 - 获得的字符.失败 - EOF.fputc头文件<stdio.h>函数原型int fputc(int ch, FILE*stream);函数说明写入字符ch 到给定输出流stream.参数ch - 要被写入的字符.stream - 输出流.返回值成功 - 被写入的字符.失败 - EOF.示例#include <stdio.h>#include <stdlib.h>int main(void){int c;char ch;int ret_code=0;FILE*fp=fopen("test.txt", "w");for (ch='a'; ret_code!=EOF&&ch!='z'; ch++)ret_code=fputc(ch, fp);fclose(fp);fp=fopen("test.txt", "r");while ((c=fgetc(fp)) !=EOF)putchar(c);fclose(fp);putchar('\n');}输出abcdefghijklmnopqrstuvwxyfgets头文件<stdio.h>函数原型char*fgets(char*str, int count, FILE*stream);函数说明从给定文件流读取最多count - 1个字符并存储于str所指向的字符数组.若文件尾出现或发现换行符则终止分析,后一情况下str将包含一个换行符.若无错误发生,则紧随写入到str 的最后一个字符后写入'\0'.若count 小于1则行为未定义.参数str - 指向char数组元素的指针.count - 写入的最大字符数(典型的为str的长度) .stream - 读取数据来源的文件流.返回值成功 - 指向字符串的指针.失败 - NULL.fputs头文件<stdio.h>函数原型int fputs(const char*str, FILE*stream);函数说明写入字符串str 的每个字符到stream ,不写入'\0'.参数str - 要写入的空终止字符串.stream - 输出流.返回值成功 - 非负值.失败 - EOF.示例#include <stdio.h>#include <stdlib.h>int main(void){char buf[8];FILE*tmpf=tmpfile();fputs("Alan Turing\n", tmpf);fputs("John von Neumann\n", tmpf);fputs("Alonzo Church\n", tmpf);rewind(tmpf);while (fgets(buf, sizeof buf, tmpf) !=NULL)printf("\"%s\"\n", buf);if (feof(tmpf))puts("End of file reached");}输出"Alan Tu""ring""John vo""n Neuma""nn""Alonzo ""Church"End of file reached有格式输入/输出fscanf头文件<stdio.h>函数原型int fscanf(FILE*stream, const char*format, ...);函数说明从文件流stream读取数据.参数stream - 要读取的输入文件流.format - 格式字符串.返回值成功 - 赋值的接收参数的数量.失败 - EOF.fprintf头文件<stdio.h>函数原型int fprintf(FILE*stream, const char*format, ...);函数说明写结果到文件流stream.参数stream - 要写入的输出文件流.format - 格式字符串.返回值成功 - 传输到输出流的字符数.失败 - 负值.示例#include <stdio.h>#include <stdlib.h>int main(void){char buf[8];FILE*tmpf=tmpfile();fprintf(tmpf, "Alan Turing\n");fprintf(tmpf, "John von Neumann\n");fprintf(tmpf, "Alonzo Church\n");rewind(tmpf);while (fscanf(tmpf, "%s", buf) !=EOF)printf("\"%s\"\n", buf);if (feof(tmpf))puts("End of file reached");}输出"Alan""Turing""John""von""Neumann""Alonzo""Church"End of file reached文件位置ftell头文件<stdio.h>函数原型long ftell(FILE*stream);函数说明返回流stream的文件位置指示器.若流以二进制模式打开,则由此函数获得的值是从文件开始的字节数.若流以文本模式打开,则由此函数返回的值未指定,且仅作为fseek()的输入才有意义.参数stream - 要检验的文件流.返回值成功 - 文件位置指示器.失败 - EOF .fseek头文件<stdio.h>函数原型int fseek(FILE*stream, long offset, int origin);函数说明设置文件流stream的文件位置指示器.若stream 以二进制模式打开,则新位置为从origin 开始后的offset字节.若stream 以文本模式打开,则仅有的受支持的offset 值为零(origin值不受限制)或先前在同一文件流上对ftell()的调用的返回值(origin 值只能是SEEK_SET)。
c语言参考手册C语言参考手册C语言是一种高效、可移植、面向过程的编程语言,常常被用于操作系统、网络编程、驱动程序、嵌入式系统等方面的编程。
以下是C语言参考手册中的重要概念和函数列表:基本概念1. 数据类型C语言支持多种数据类型,包括整型、浮点型、字符型、指针型、枚举型等。
每种数据类型占用的内存大小可能不同。
2. 运算符C语言支持多种运算符,包括算术运算符、关系运算符、逻辑运算符、位运算符等。
它们用于完成各种数学运算、逻辑判断和位操作。
3. 控制结构C语言支持多种控制结构,包括分支结构(if、switch)、循环结构(for、while)、跳转语句(goto)等。
它们用于控制程序的执行流程。
重要函数列表1.stdio.h头文件stdio.h是一个标准输入输出库头文件,包括多种输入输出函数,例如printf、scanf、puts、gets等。
这些函数用于打印输出、读取输入等操作。
2.string.h头文件string.h是一个字符串处理头文件,包括多种字符串处理函数,例如strlen、strcat、strcmp、strcpy等。
这些函数用于处理字符串。
3.math.h头文件math.h是一个数学头文件,包括多种数学函数,例如sin、cos、tan、pow等。
这些函数用于数学运算。
4.time.h头文件time.h是一个时间头文件,包括多种时间函数,例如time、localtime、gmtime等。
这些函数用于获取时间和日期。
5.stdlib.h头文件stdlib.h是一个标准库头文件,包括多种库函数,例如malloc、free、exit等。
这些函数用于内存分配、释放和退出程序。
总结C语言是一种强大的编程语言,具有高效、可移植、面向过程等优点。
C语言参考手册包含了各种基本概念和函数列表,可以帮助开发人员更好地掌握C语言。
第4章初始化与清除第2章利用了一些分散的典型C语言库的构件,并把它们封装在一个s t r u c t中,从而在库的应用方面做了有意义的改进。
(从现在起,这个抽象数据类型称为类)。
这样不仅为库构件提供了单一一致的入口指针,也用类名隐藏了类内部的函数名。
在第3章中,我们介绍了存取控制(隐藏实现),这就为类的设计者提供了一种设立界线的途径,通过界线的设立来决定哪些是用户可以处理的,哪些是禁止的。
这意味着数据类型的内部机制对设计者来说是可控的和能自行处理的。
这样让用户也清楚哪些成员是他们能够使用并加以注意的。
封装和实现的隐藏大大地改善了库的使用。
它们提供的新的数据类型的概念在某些方面比从C中继承的嵌入式数据类型要好。
现在C ++编译器可以为这种新的数据类型提供类型检查,这样在使用这种数据类型时就确保了一定的安全性。
当然,说到安全性,C ++的编译器能比C编译器提供更多的功能。
在本章及以后的章节中,我们将看到许多C ++的另外一些性能。
它们可以让我们程序中的错误暴露无遗,有时甚至在我们编译这个程序之前,帮我们查出错误,但通常是编译器的警告和出错信息。
所以我们不久就会习惯:在第一次编译时总听不到编译器那意味着正确的提示音。
安全性包括初始化和清除两个方面。
在C语言中,如果程序员忘记了初始化或清除一个变量,就会导致一大段程序错误。
这在一个库中尤其如此,特别是当用户不知如何对一个s t r u c t 初始化,甚至不知道必须要初始化时。
(库中通常不包含初始化函数,所以用户不得不手工初始化s t r u c t)。
清除是一个特殊问题,因为C程序员一旦用过了一个变量后就把它忘记了,所以对一个库的s t r u c t来说,必要的清除工作往往被遗忘了。
在C ++中,初始化和清除的概念是简化类库使用的关键所在,并可以减少那些由于用户忘记这些操作而引起的许多细微错误。
本章就来讨论C ++的这些特征。
4.1 用构造函数确保初始化在s t a s h和s t a c k类中都曾调用i n i t i a l i z e()函数,这暗示无论用什么方法使用这些类的对象,在使用之前都应当调用这一函数。
很不幸的是,这要求用户必须正确地初始化。
而用户在专注于用那令人惊奇的库来解决他们的问题的时候,往往忽视了这些细节。
在C ++中,初始化实在太重要了,所以不能留给用户来完成。
类的设计者可以通过提供一个叫做构造函数的特殊函数来保证每个对象都正确的初始化。
如果一个类有构造函数,编译器在创建对象时就自动调用这一函数,这一切在用户使用他们的对象之前就已经完成了。
对用户来说,是否调用构造函数并不是可选的,它是由编译器在对象定义时完成的。
接下来的问题是这个函数叫什么名字。
这必须考虑两点,首先这个名字不能与类的其他成员函数冲突,其次,因为该函数是由编译器调用的,所以编译器必须总能知道调用哪个函数。
S t r o u s t r u p的方法似乎是最容易也是最符合逻辑的:构造函数的名字与类的名字一样。
这使得这样的函数在初始化时自动被调用。
下面是一个带构造函数的类的简单例子:现在当一个对象被定义时:这时就好像a 是一个整数一样:为这个对象分配内存。
但是当程序执行到a 的定义点时,构造函数自动被调用,因为编译器已悄悄地在a 的定义点处插入了一个X ::X ()的调用。
就像其他成员函数被调用一样。
传递到构造函数的第一个参数(隐含)是调用这一函数对象的地址。
像其他函数一样,我们也可以通过构造函数传递参数,指定对象该如何创建,设定对象初始值等等。
构造函数的参数保证对象的所有部分都被初始化成合适的值。
举例来说:如果类t r e e 有一个带整型参数的构造函数,用以指定树的高度,那么我们就必须这样来创建一个对象:tree t(12); // 12英尺高的树如果t r e e (i n t )是唯一的构造函数,编译器将不会用其他方法来创建一个对象(在下一章我们将看到多个构造函数以及调用它们的不同方法)。
关于构造函数,我们就全部介绍完了。
构造函数是一个有着特殊名字,由编译器自动为每个对象调用的函数,然而它解决了类的很多问题,并使得代码更容易阅读。
例如在上一个代码段中,对有些i n i t i a l i z e ()函数我们并没有看到显式的调用,这些函数从概念上说是与定义分开的。
在C ++中,定义和初始化是同一概念,不能只取其中之一。
构造函数和析构函数是两个非常特殊的函数:它们没有返回值。
这与返回值为v o i d 的函数显然不同。
后者虽然也不返回任何值,但我们还可以让它做点别的。
而构造函数和析构函数则不允许。
在程序中创建和消除一个对象的行为非常特殊,就像出生和死亡,而且总是由编译器来调用这些函数以确保它们被执行。
如果它们有返回值,要么编译器必须知道如何处理返回值,要么就只能由用户自己来显式地调用构造函数与析构函数,这样一来,安全性就被破坏了。
4.2 用析构函数确保清除作为一个C 程序员,我们可能经常想到初始化的重要性,但很少想到清除的重要性。
毕竟,清除一个整型变量时需要作什么?只需要忘记它。
然而,在一个库中,对于一个曾经用过的对象,仅仅“忘记它”是不安全的。
如果它修改了某些硬件参数,或者在屏幕上显示了一些字符,或在堆中分配了一些内存,那么将会发生什么呢? 如果我们只是“忘记它”,我们的对象就永远不会消失。
在C ++中,清除就像初始化一样重要。
通过析构函数来保证清除的执行。
析构函数的语法与构造函数一样,用类的名字作函数名。
然而析构函数前面加上一个~,以和构造函数区别。
另外,析构函数不带任何参数,因为析构不需任何选项。
下面是一个析构函数的声明:class Y {p u b l i c :~ Y ();} ;当对象超出它的定义范围时,编译器自动调用析构函数。
我们可以看到,在对象的定义点处构造函数被调用,但析构函数调用的唯一根据是包含该对象的右括号,即使用g o t o语句跳出这一程序块(为了与C 语言向后兼容,g o t o在C ++中仍然存在,当然也是为了方便)。
我们应该注意一些非本地的g o t o语句,它们用标准C语言库中的setjmp() 和l o n g j m p()函数,这些函数将不会引发析构函数的调用。
(这里作一点说明:有的编译器可能并不用这种方法来实现。
依赖那些不在说明书中的特征意味着这样的代码是不可移植的)。
下例说明了构造函数与析构函数的上述特征:58下面是上面程序的输出结果:我们可以看到析构函数在包括它的右括号处被调用。
4.3 清除定义块在C中,我们总要在一个程序块的左括号一开始就定义好所有的变量,这在程序设计语言中不算少见(P a s c a l中例外),其理由无非是因为“这是一个好的编程风格”。
在这点上,我有自己的看法。
我认为它总是给我带来不便。
作为一个程序员,每当我需要增加一个变量时我都得跳到块的开始,我发现如果变量定义紧靠着变量的使用处时,程序的可读性更强。
也许这些争论具有一定的普遍性。
在C ++中,是否一定要在块的开头就定义所有变量成了一个很突出的问题。
如果存在构造函数,那么当对象产生时它必须首先被调用,如果构造函数带有一个或者更多个初始化参数,我们怎么知道在块的开头定义这些初始化信息呢?在一般的编程情况下,我们做不到这点,因为C中没有私有成员的概念。
这样很容易将定义与初始化部分分开,然而C ++要保证在一个对象产生时,它同时被初始化。
这可以保证我们的系统中没有未初始化的对象。
C并不关心这些。
事实上,C要求我们在块的开头就定义所有变量,在我们还不知道一些必要的初始化信息时,就要求我们这样做是鼓励我们不初始化变量。
通常,在C ++中,在还不拥有构造函数的初始化信息时不能创建一个对象,所以不必在块的开头定义所有变量。
事实上,这种语言风格似乎鼓励我们把对象的定义放得离使用点尽可能近一点。
在C ++中,对一个对象适用的所有规则,对预定义类型也同样适用。
这意味着任何类的对象或者预定义类型都可以在块的任何地点定义。
这也意味着我们可以等到我们已经知道一个变量的必要信息时再去定义它,所以我们总是可以同时定义和初始化一个变量。
我们可以看到首先是b u f被定义,然后是一些语句,然后x 被定义并用一个函数调用对它初始化,然后y 和g 被定义。
在C 中这些变量都只能在块的一开始定义。
一般说来,应该在尽可能靠近变量的使用点定义变量,并在定义时就初始化(这是对预定义类型的一种建议,但在那里可以不做初始化)。
这是出于安全性的考虑,减少变量误用的可能性。
另外,程序的可读性也增强了,因为读者不需要跳到程序头去确定变量的类型。
4.3.1 for 循环在C ++中,我们将经常看到f o r 循环的计数器直接在f o r 表达式中定义:上述声明是一种重要的特殊情况,这可能使那些刚接触C ++的程序员感到迷惑不解。
变量i 和j 都是在f o r 表达式中直接定义的(在C 中我们不能这样做),然后他们就作为一个变量在f o r 循环中使用。
这给程序员带来很大的方便,因为从上下文中我们可以清楚地知道变量i 、j 的作用,所以不必再用诸如i _l o o p _c o u n t e r 之类的名字来定义一个变量,以表示这一变量的作用。
这里有一个变量生存期的问题,在以前这是由程序块的右大括号来确定的。
从编译器的角度来看这样是合理的,因为作为程序员,我们显然想让i 只在循环内部有效。
然而很不幸的是,如果我们用这种方法声明:(无论有没有大括号,)在同一程序块内,编译器将给出一个重复定义的错误,而新的标准C ++说明书上说,一个在f o r 循环的控制表达式中定义的循环计数器只在该循环内才有效,所以上面的声明是可行的。
(当然,并不是所有的编译器都支持这一点,我们可能会遇到一些老式风格的编译器。
)如果这种转变引起一些错误的话,编译器会指出,解决起来也很容易。
注意,第4下载那些局部变量会屏蔽这个封闭范围中的变量。
我发现一个在小范围内设计良好的指示器:如果我们的一个函数有好几页,也许我们正在试图让这个函数完成太多的工作。
用更多的细化的函数不仅有用,而且更容易发现错误。
4.3.2 空间分配现在,一个变量可以在某个程序范围内的任何地方定义,所以在这个变量的定义之前是无法对它分配内存空间的。
通常,编译器更可能像C 编译器一样,在一个程序块的开头就分配所有的内存。
这些对我们来说是无关紧要的,因为作为一个程序员,我们在变量定义之前总是无法得到存储空间。
即使存储空间在块的一开始就被分配,构造函数也仍然要到对象的定义时才会被调用,因为标识符只有到此时才有效。
编译器甚至会检查我们有没有把一个对象的定义放到一个条件块中,比如在s w i t c h 块中声明,或可能被g o t o 跳过的地方。