C语言编程经典实践:100个精选习题完全解析
C语言是一种广泛使用的计算机编程语言,以其强大的功能、灵活性和高效率而闻名。它广泛应用于操作系统、嵌入式开发、系统软件以及应用程序开发中。作为IT专业人员,掌握C语言能帮助你更好地理解和优化系统层面的代码。函数是组织代码、实现代码复用的重要方式。在C语言中,一个函数由返回类型、函数名、参数列表和函数体四部分组成。// 函数定义// 函数调用return 0;在这个例子中,add函数被定义为接收两个
简介:《C语言经典习题100例》是一本详尽的C语言学习资料,适合初学者巩固基础和提升编程技能。本书包括了从基础语法到高级特性的100个例题,涵盖了变量声明、运算符、控制流程、函数、数组、指针、字符串处理、结构体与联合体、位操作、预处理器以及文件操作等关键知识点。通过解决这些精选习题,学习者可以深入理解C语言的核心概念,并通过实际编程提升问题解决能力。 
1. C语言编程基础和数据类型
1.1 C语言概述
C语言是一种广泛使用的计算机编程语言,以其强大的功能、灵活性和高效率而闻名。它广泛应用于操作系统、嵌入式开发、系统软件以及应用程序开发中。作为IT专业人员,掌握C语言能帮助你更好地理解和优化系统层面的代码。
1.2 基本数据类型
C语言提供了几种基本的数据类型,包括整型(int)、浮点型(float和double)、字符型(char)以及布尔型(使用int的特例)。理解这些基本数据类型的特性和内存分配,是编写有效C程序的基础。
int main() {
int integerVar = 10; // 整型变量
float floatVar = 3.14f; // 浮点型变量
char charVar = 'A'; // 字符型变量
double doubleVar = 6.28; // 双精度浮点型变量
return 0;
}
1.3 数据类型的转换与运算
在C语言中,不同类型的数据可以进行运算,但有时需要显式地进行类型转换,以防止数据溢出或精度丢失。例如,将一个整型值除以浮点型值时,整型值将自动转换为浮点型。
int main() {
float result;
int intVar = 5;
float floatVar = 2.0;
result = intVar / floatVar; // 这里intVar自动转换为float
return 0;
}
在编写C语言程序时,选择合适的数据类型并进行必要的类型转换,可以优化程序的性能并确保数据处理的准确性。接下来的章节将逐步深入探讨C语言的控制流程、函数编程等高级主题。
2. C语言控制流程和函数编程
2.1 控制流程的实现与应用
2.1.1 条件语句的编写与实践
在C语言中,条件语句是控制程序执行流程的基础,其中最常用的两种条件语句是 if 语句和 switch 语句。 if 语句可以让我们根据不同的条件执行不同的代码块,而 switch 语句则是处理多个固定值的条件判断。
#include <stdio.h>
int main() {
int value = 2;
if (value > 0) {
printf("Value is positive.\n");
} else if (value == 0) {
printf("Value is zero.\n");
} else {
printf("Value is negative.\n");
}
switch (value) {
case 1:
printf("Value is one.\n");
break;
case 2:
printf("Value is two.\n");
break;
default:
printf("Value is not one or two.\n");
}
return 0;
}
在上述代码中,我们首先使用 if 语句检查变量 value 是否大于0、等于0或者小于0。然后通过 switch 语句检查 value 的具体值。这种结构在实际编程中非常常见,尤其适用于需要基于多个固定选项来执行特定任务的场景。
2.1.2 循环结构的深入探讨和案例分析
循环是让程序能够重复执行某些任务直到满足特定条件的控制结构。C语言提供了三种基本的循环结构: while 、 do-while 和 for 循环。
#include <stdio.h>
int main() {
int i;
int sum = 0;
// while 循环
i = 0;
while (i < 10) {
sum += i;
i++;
}
printf("Sum using while: %d\n", sum);
// do-while 循环
sum = 0;
i = 0;
do {
sum += i;
i++;
} while (i < 10);
printf("Sum using do-while: %d\n", sum);
// for 循环
sum = 0;
for (i = 0; i < 10; i++) {
sum += i;
}
printf("Sum using for: %d\n", sum);
return 0;
}
在这个案例中,我们使用了三种循环结构来计算从0到9的整数之和。 while 循环是直到条件不再满足时停止; do-while 循环至少执行一次,即使条件初始时就是假的; for 循环则是将初始化、条件判断和迭代过程整合在一起,常用于循环次数已知的情况。通过这些循环,可以灵活地控制程序的执行流程。
2.1.3 跳转语句的逻辑控制与运用
跳转语句允许程序跳过部分代码或者直接跳出循环。C语言中最常用的跳转语句包括 break 、 continue 和 goto 。
#include <stdio.h>
int main() {
int i;
// 使用 break 跳出循环
for (i = 0; i < 10; i++) {
if (i == 5) {
break;
}
printf("%d\n", i);
}
// 使用 continue 跳过当前循环迭代
for (i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue;
}
printf("%d\n", i);
}
// 使用 goto 进行任意跳转 (不推荐使用)
goto jump_here;
printf("This will not be printed.\n");
jump_here:
printf("Jumping to this point.\n");
return 0;
}
在这个代码段中,我们使用了 break 来跳出 for 循环,一旦变量 i 的值达到了5,循环就会终止。使用 continue 则是跳过当次循环中 continue 之后的语句,继续下一次循环。尽管 goto 语句可以提供强大的跳转能力,但由于其潜在的副作用(比如代码难以理解),在现代C语言编程中通常不推荐使用。
2.2 函数定义及调用
2.2.1 函数的基本概念与定义方式
函数是组织代码、实现代码复用的重要方式。在C语言中,一个函数由返回类型、函数名、参数列表和函数体四部分组成。
#include <stdio.h>
// 函数定义
int add(int a, int b) {
return a + b;
}
int main() {
// 函数调用
int sum = add(5, 3);
printf("Sum is: %d\n", sum);
return 0;
}
在这个例子中, add 函数被定义为接收两个整数参数并返回它们的和。 main 函数中通过调用 add 并传入相应的参数,实现了函数的执行。
2.2.2 函数调用机制与参数传递技巧
函数调用机制涉及到参数的传递方式,包括值传递和引用传递两种基本方式。在C语言中,默认情况下使用值传递,即将实参的副本传递给函数的形参。
#include <stdio.h>
void increment(int *ptr) {
(*ptr)++;
}
int main() {
int value = 10;
increment(&value);
printf("Value after increment: %d\n", value);
return 0;
}
在这个例子中, increment 函数通过指针接收了一个变量的地址。通过指针传递的方式,函数内部的修改会影响到原始变量,这是因为传递的是变量的引用而不是它的值。
2.3 函数高级特性
2.3.1 函数指针的使用和理解
函数指针允许我们通过指针来调用函数,这在实现回调函数、动态分派等高级功能时非常有用。
#include <stdio.h>
// 函数声明
void printHello() {
printf("Hello, World!\n");
}
int main() {
// 函数指针声明
void (*funcPtr)() = printHello;
// 使用函数指针调用函数
funcPtr();
return 0;
}
在这里, funcPtr 是一个函数指针,它指向了 printHello 函数。通过 funcPtr() 的调用,我们可以间接执行 printHello 函数。
2.3.2 递归函数的原理和编程实例
递归函数是一种直接或者间接调用自身的函数,它通常包含两个基本要素:基本情况和递归情况。
#include <stdio.h>
// 递归函数计算阶乘
int factorial(int n) {
if (n <= 1) {
return 1; // 基本情况
} else {
return n * factorial(n - 1); // 递归情况
}
}
int main() {
int number = 5;
int result = factorial(number);
printf("Factorial of %d is %d\n", number, result);
return 0;
}
在这个例子中, factorial 函数是一个递归函数,它不断将问题规模缩小并最终达到基本情况 n <= 1 。通过递归,函数能够处理像阶乘这样的问题。
请注意: 本章节详细介绍了C语言控制流程中的条件语句、循环结构和跳转语句的基本概念、实现方式和编程实践。同时,深入探讨了函数的定义、调用机制和高级特性,包括函数指针的使用和递归函数的原理。每个话题都通过具体的代码示例和逻辑分析进行了详细解释。
3. C语言数组与指针操作
数组和指针是C语言中极为重要的概念,它们在程序设计中扮演着不可或缺的角色。数组提供了一种组织数据的方式,而指针则提供了对内存地址直接操作的能力。本章将深入探讨数组和指针的使用,并将展示它们的高级技巧。
3.1 数组的深入学习
3.1.1 数组的声明、初始化和使用
数组是一种数据结构,能够存储一系列相同类型的数据。在C语言中,数组的声明需要指定数组名、类型和元素数量。数组一旦声明,其大小就固定了,无法动态改变。
int numbers[10]; // 声明一个整型数组,包含10个元素
初始化数组时,可以在声明时直接为元素赋值。
int numbers[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
若未完全初始化,则未指定的元素会自动设为0。
数组的使用很简单,通过索引可以直接访问或修改元素。
int first = numbers[0]; // 访问第一个元素
numbers[1] = 10; // 修改第二个元素的值
数组元素通过整数索引访问,索引从0开始。在C语言中,数组名实际上是一个指向数组首元素的指针,因此可以使用指针运算来访问数组元素。
3.1.2 多维数组的操作和应用
多维数组是数组的扩展,用于存储多维数据。例如,二维数组可以看作是表格,而三维数组可以看作是空间中的立方体。
int matrix[3][4]; // 声明一个3行4列的整型二维数组
二维数组的初始化如下:
int matrix[3][4] = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11}
};
访问多维数组元素的方式是通过多个索引完成的。
int value = matrix[2][3]; // 访问第三行第四列的元素
数组的大小必须在编译时确定,因此在声明时就需要知道其维度。数组在内存中是线性存储的,这意味着多维数组在内存中的布局其实是一维的。
3.2 指针操作的原理与技巧
3.2.1 指针的基础知识与操作
指针是一个变量,其值为另一个变量的地址。指针的声明需要指定指针的类型,因为编译器需要知道指针指向的地址所存储数据的类型,以确保正确的内存操作。
int *p; // 声明一个指向整数的指针
int number = 5;
p = &number; // 指针p指向变量number的地址
指针可以进行加减运算,用于访问连续内存位置的数据。此外,指针还可以与数组结合使用,实现更为复杂的数据操作。
3.2.2 指针与数组的结合使用
数组名在大多数情况下被解释为数组首元素的地址,即指向数组首元素的指针。因此,数组与指针在很多情况下可以互换使用。
int numbers[5] = {1, 2, 3, 4, 5};
int *p = numbers; // 指针p指向数组numbers的首元素
通过指针访问数组元素的代码如下:
for(int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // 使用指针运算访问数组元素
}
3.2.3 指针与函数的高级应用
指针可以作为函数参数,允许函数直接修改调用者的变量。这种技术被称为“按引用传递”。通过指针,函数能够接收变量的地址,并在函数内部操作这些地址指向的数据。
void increment(int *value) {
(*value)++; // 通过指针修改传入变量的值
}
int number = 10;
increment(&number); // 传入number的地址
函数中的指针参数使得函数能够执行更强大的操作,例如动态内存分配或复杂数据结构的修改。指针与函数结合使用是C语言灵活编程的体现。
表格、mermaid格式流程图以及代码块的展示
在本章节中,我们已经看到了数组和指针的基本操作。接下来,我们用一个表格来对比数组和指针的异同。
| 特性 | 数组 | 指针 | | --- | --- | --- | | 存储内容 | 一系列相同类型的数据 | 变量的地址 | | 访问方式 | 通过索引 | 通过解引用或指针运算 | | 声明 | 数据类型 数组名[大小]; | 数据类型 *指针名; | | 操作限制 | 大小固定,无法动态改变 | 大小动态,灵活操作 |
mermaid格式流程图可以用来表示指针如何在内存中进行操作。以下是内存中指针变量和指针所指向内容的关系图:
flowchart LR
A[指针变量p] -->|存储地址| B[变量number的地址]
B -->|访问| C[变量number]
代码块展示了如何使用指针操作数组元素。这段代码通过指针循环遍历数组,并打印每个元素的值。
int numbers[5] = {10, 20, 30, 40, 50};
int *p = numbers;
for(int i = 0; i < 5; i++) {
printf("%d ", *(p + i));
}
指针和数组结合使用的代码逻辑是对地址进行偏移运算,以此来访问连续内存中的多个元素。
通过本章节的介绍,我们可以看出数组和指针在C语言中的基础性和灵活性。数组为数据组织提供了结构化的方式,而指针则提供了对内存更细致的控制。两者结合使用时,能够在程序中实现更为高级和复杂的功能。
4. C语言字符串处理和高级数据结构
4.1 字符串处理的实践技巧
4.1.1 字符串常量与变量的操作
字符串是C语言编程中的基础概念之一,它由一系列字符组成,以空字符('\0')结尾。在C语言中,字符串既可以是一个字符串常量(也称为字面量),也可以是一个字符数组。
字符串常量是直接写在程序中的,编译器会将其存储在程序的只读数据段中。例如:
const char *str1 = "Hello, World!";
上面的代码定义了一个指向字符常量的指针 str1 。这里, "Hello, World!" 是一个字符串常量,其内容被存储在程序的只读数据段中。
相对地,字符串变量通常通过字符数组实现。使用字符数组存储字符串时,可以使用 strncpy 、 strcpy 等标准库函数来操作。例如:
char str2[100] = ""; // 定义一个最大长度为100的字符数组
strcpy(str2, "C programming"); // 使用strcpy函数复制字符串
在操作字符串时需要注意的是,确保目标缓冲区有足够的空间来存储复制的字符串,以防止缓冲区溢出。
4.1.2 字符串的比较与格式化方法
C语言提供了标准库函数来比较字符串和格式化字符串。
字符串比较
比较字符串可以使用 strcmp 函数,该函数比较两个字符串,并根据比较结果返回一个整数。如果两个字符串相等,返回值为0;如果第一个不匹配的字符在第一个字符串中的ASCII码值小于第二个字符串中的ASCII码值,返回负值;反之则返回正值。
char str1[] = "Hello";
char str2[] = "World";
int result = strcmp(str1, str2); // result 将会是负数
字符串格式化
格式化字符串可以使用 sprintf 函数,它将格式化的数据写入字符串。类似 printf 函数, sprintf 也使用格式说明符来格式化各种数据类型到字符串中。
char buffer[50];
sprintf(buffer, "String: %s, Number: %d", "Test", 12345);
// buffer 现在包含了 "String: Test, Number: 12345"
4.2 结构体与联合体的使用
4.2.1 结构体的定义、访问和应用
结构体( struct )是C语言中复合数据类型之一,它允许将不同类型的数据项组合在一起。结构体被广泛用于处理复杂的数据。
结构体的定义
结构体的定义以关键字 struct 开始,后跟结构体名称,花括号内定义其成员。
struct Person {
char name[50];
int age;
float height;
};
结构体的访问
可以通过点操作符( . )访问结构体成员,例如:
struct Person person1;
strcpy(person1.name, "John Doe");
person1.age = 30;
person1.height = 6.0;
结构体也可以通过指针访问,使用箭头操作符( -> ):
struct Person *ptr;
ptr = &person1;
printf("%s, %d, %.2f\n", ptr->name, ptr->age, ptr->height);
结构体的应用
结构体在处理具有多个属性的实体时非常有用。例如,定义一个表示图书信息的结构体:
struct Book {
char title[100];
char author[50];
int year;
};
struct Book book1;
strcpy(book1.title, "The C Programming Language");
strcpy(book1.author, "Brian W. Kernighan and Dennis M. Ritchie");
book1.year = 1978;
4.2.2 联合体的概念与使用场景
联合体( union )是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。联合体和结构体类似,但所有成员共享同一个起始地址,因此联合体的总大小是其最大成员的大小。
联合体的定义
定义联合体的方式类似于结构体,但是使用 union 关键字。
union Data {
int i;
float f;
char str[20];
};
联合体的使用
由于所有成员都共享相同的内存位置,修改一个成员会改变整个联合体的内容。
union Data data;
data.i = 10;
printf("Data.i: %d\n", data.i);
data.f = 10.0;
printf("Data.f: %f\n", data.f);
在上述代码中,尽管 i 和 f 是不同的数据类型,它们存储在 data 的相同位置。因此,改变 f 的值也会影响到 i 的值。
联合体经常用于节省内存或描述多种数据类型占用同一内存位置的情况,例如,可以用来定义一个可以包含不同类型数据的“混合”数据类型。
通过这样的使用,我们可以灵活地处理不同类型的数据,而不需要为每种类型分配额外的内存空间。
5. C语言位操作与预处理技巧
位操作和预处理是C语言中高级而强大的特性,它们能够帮助程序员在系统底层编程时实现高效率的代码,同时也为代码的模块化和可重用性提供了强大的支持。本章将深入探讨位操作的原理、技巧以及预处理指令的使用,帮助读者更好地掌握这些高级特性。
5.1 位操作的深入理解
位操作是直接在二进制位层面上进行的操作,它允许程序员直接访问和修改变量的位,使得操作更加精细和高效。位操作在底层硬件控制、数据加密、图像处理等领域有着广泛的应用。
5.1.1 位运算符的使用和规则
C语言提供了六种位运算符:按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)和右移(>>)。我们通过具体的代码示例来解释它们的使用和规则。
int a = 60; // 二进制表示为 0011 1100
int b = 13; // 二进制表示为 0000 1101
int c;
c = a & b; // 按位与操作,结果为 0000 1100,即十进制的 12
c = a | b; // 按位或操作,结果为 0011 1101,即十进制的 61
c = a ^ b; // 按位异或操作,结果为 0011 0001,即十进制的 49
c = ~a; // 按位取反操作,结果为 1100 0011,即十进制的 -61(补码表示)
c = a << 2; // 左移操作,结果为 1111 0000,即十进制的 240
c = a >> 2; // 右移操作,结果为 0000 1111,即十进制的 15
5.1.2 位字段的声明和应用
位字段是一种特殊的数据结构,它允许在一个声明中定义多个位变量,从而可以更有效地使用内存。位字段通常在结构体中声明,并且有特定的语法格式。
struct {
unsigned int is_valid : 1;
unsigned int priority : 3;
unsigned int data : 4;
} flags;
flags.is_valid = 1; // 设置位字段为1
flags.priority = 7; // 设置位字段为111(十进制的7)
flags.data = 10; // 设置位字段为1010(十进制的10)
位字段的大小在声明时需要明确指定,以便编译器能够正确地在内存中分配空间。位字段的使用可以极大地提高存储效率,特别是在硬件控制和资源受限的嵌入式系统开发中非常有用。
5.2 预处理指令的掌握
预处理是C语言编译过程中的第一步,它在编译器对代码进行实际编译之前运行。预处理指令以 # 开头,例如 #define , #include 等,它们为程序员提供了处理源代码的能力。
5.2.1 宏定义的编写和注意事项
宏定义是预处理指令中最常见的用法之一,它允许程序员定义一些常量或者函数式的宏,在整个程序中任何地方都可以使用,且编译器在编译前都会进行替换。
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))
printf("Pi value: %f\n", PI);
int max_value = MAX(10, 20);
宏定义有一些需要注意的地方: - 宏参数没有类型检查,这可能导致意外的行为。 - 宏函数的参数如果没有用括号包围,可能会产生优先级错误。 - 宏展开可能导致代码量膨胀,影响程序大小。
5.2.2 预处理指令在编译过程中的作用
预处理指令在实际编译之前对源代码进行处理,它包括宏定义、文件包含、条件编译等操作。预处理指令可以在不同的环境中快速更改程序的行为,例如调试时临时禁用某些代码块。
// 文件包含
#include <stdio.h>
#include "myheader.h"
// 条件编译
#ifdef DEBUG
printf("Debug mode\n");
#else
printf("Release mode\n");
#endif
预处理指令通常用于: - 引入标准库或者第三方库的头文件。 - 临时修改程序行为,比如添加日志输出、禁用某些功能。 - 为跨平台开发提供代码的可配置性。
总结起来,本章深入探讨了位操作的原理与技巧以及预处理指令的掌握。通过本章的学习,读者应该能够更好地利用位操作进行底层的内存优化和数据处理,同时,通过预处理指令可以提高代码的模块化和可配置性。掌握这些高级特性将使程序员在进行系统级编程时更加灵活和高效。
6. C语言文件操作和综合应用
6.1 文件操作的全面掌握
文件操作是C语言中较为高级的一个话题,它允许程序员直接与计算机的存储设备进行交互,进行数据的持久化存储。理解并掌握文件操作的各个细节,对于构建复杂的应用程序至关重要。
6.1.1 文件的打开、关闭、读写操作
在C语言中,标准库提供了对文件操作的支持,其中 stdio.h 头文件定义了一系列文件操作相关的函数。这些函数允许我们执行基本的文件操作,如打开文件、读取文件、写入文件和关闭文件。
#include <stdio.h>
int main() {
FILE *fp;
char ch;
// 打开文件用于读取,如果文件不存在则报错
fp = fopen("example.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 读取文件中的字符直到文件尾部
while ((ch = fgetc(fp)) != EOF) {
putchar(ch); // 输出字符到控制台
}
// 关闭文件
fclose(fp);
return 0;
}
在上面的示例中,我们使用 fopen 函数打开一个名为 "example.txt" 的文件用于读取,若文件成功打开,则文件指针 fp 将指向该文件。通过 fgetc 函数我们逐个读取文件中的字符,并使用 putchar 函数输出到控制台。最后,我们用 fclose 函数关闭文件,释放与之相关的资源。
6.1.2 文件指针的定位与错误处理
在文件操作中,文件指针的位置是非常关键的。它决定了下一次读取或写入操作的开始位置。 stdio.h 中的 fseek 、 ftell 和 rewind 函数可以用来修改文件指针的位置,并获取当前位置的信息。
#include <stdio.h>
int main() {
FILE *fp;
int c;
fp = fopen("example.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 跳转到文件末尾
fseek(fp, 0, SEEK_END);
// 获取当前文件指针位置
c = ftell(fp);
// 显示当前位置
printf("Position from start is: %d\n", c);
// 返回文件开头
rewind(fp);
// 关闭文件
fclose(fp);
return 0;
}
以上代码演示了如何使用 fseek 函数移动文件指针到文件末尾,用 ftell 函数获取当前位置,最后使用 rewind 函数将文件指针重置到文件开头。
6.2 综合应用与习题解析
6.2.1 综合案例分析与编程技巧
为巩固和实践文件操作相关知识,我们可以考虑一个综合案例:创建一个文本文件,写入数据,然后读取并处理这些数据。
首先,创建一个文本文件并写入一些内容:
#include <stdio.h>
int main() {
FILE *fp;
const char *fileName = "data.txt";
fp = fopen(fileName, "w"); // 打开文件用于写入
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 写入字符串到文件
fprintf(fp, "Hello, world!\n");
// 写入数字到文件
int numbers[] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++) {
fprintf(fp, "%d ", numbers[i]);
}
fprintf(fp, "\n");
fclose(fp); // 关闭文件
return 0;
}
然后,读取并处理文件内容:
#include <stdio.h>
int main() {
FILE *fp;
char buffer[100];
int number;
const char *fileName = "data.txt";
fp = fopen(fileName, "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
// 逐行读取
while (fgets(buffer, sizeof(buffer), fp)) {
// 从字符串中解析整数
sscanf(buffer, "%*s %d", &number);
printf("Number read from file: %d\n", number);
}
fclose(fp); // 关闭文件
return 0;
}
6.2.2 经典习题的解题思路与代码实现
接下来我们解决一个经典的编程习题:将一个文本文件中的所有字符串提取出来,转换成小写,并写入到另一个新文件中。
这个问题的解决方案涉及到文件的读取、字符串处理以及字符转换等操作:
#include <stdio.h>
#include <ctype.h> // 为了使用tolower函数
int main() {
FILE *fileIn, *fileOut;
char ch;
const char *inFileName = "input.txt";
const char *outFileName = "output.txt";
// 打开输入文件
fileIn = fopen(inFileName, "r");
if (fileIn == NULL) {
perror("Error opening input file");
return -1;
}
// 打开输出文件
fileOut = fopen(outFileName, "w");
if (fileOut == NULL) {
perror("Error opening output file");
fclose(fileIn);
return -1;
}
// 读取字符直到文件尾部
while ((ch = fgetc(fileIn)) != EOF) {
fputc(tolower(ch), fileOut); // 转换成小写并写入输出文件
}
fclose(fileIn); // 关闭输入文件
fclose(fileOut); // 关闭输出文件
return 0;
}
在这个例程中,我们使用 fgetc 函数读取输入文件中的每一个字符,并用 tolower 函数将其转换为小写,然后使用 fputc 函数写入到输出文件中。这个过程持续到文件末尾。
以上就是对文件操作与综合应用的介绍。掌握这些技巧,结合前面章节的知识,将使你能够编写出更加完整和专业的C程序。
简介:《C语言经典习题100例》是一本详尽的C语言学习资料,适合初学者巩固基础和提升编程技能。本书包括了从基础语法到高级特性的100个例题,涵盖了变量声明、运算符、控制流程、函数、数组、指针、字符串处理、结构体与联合体、位操作、预处理器以及文件操作等关键知识点。通过解决这些精选习题,学习者可以深入理解C语言的核心概念,并通过实际编程提升问题解决能力。
更多推荐

所有评论(0)