一.数据的输入与输出:
字符数据的输入与输出:
字符数据的输入与输出在C语言中主要通过以下函数实现:
-
字符输入函数:
getchar():从标准输入(通常是键盘)读取一个字符并返回。如果发生错误或到达文件末尾,则返回EOF。
(EOF在C语言中表示文件结束的标志符号,通常被定义为-1)
例如:
char c;
c = getchar(); // 读取一个字符
函数声明和返回值
getchar函数的声明如下:
int getchar(void)
该函数返回一个int类型的值,实际上是读取字符的ASCII码值。如果读取到文件末尾或发生错误,则返回EOF。
使用方法
只获取一次字符:
#include<stdio.h> int main() { int ch = getchar(); putchar(ch); return 0; }
循环获取字符:
#include<stdio.h> int main() { int ch = 0; while ((ch = getchar()) != EOF) { putchar(ch); } return 0; }
这段代码会一直读取字符直到文件末尾。在Windows系统中,可以通过按Ctrl+Z然后回车来产生EOF。
缓冲区问题
在使用getchar时,需要注意缓冲区的问题。getchar会从缓冲区中读取字符,如果之前使用scanf读取输入,缓冲区中可能会留下一个换行符('\n'),这会影响后续的getchar读取。为了避免这种情况,可以在读取完数据后使用getchar()来清除缓冲区中的换行符。
scanf():用于格式化输入,可以读取一个字符。例如:
scanf("%c", &c); // 读取一个字符,注意有&!!
字符输出函数:
putchar():将一个字符写入标准输出(通常是屏幕)并返回该字符。如果发生错误,则返回EOF。例如:
putchar('A'); // 输出字符 'A'
函数原型和用法
putchar函数的原型为:
int putchar(int char);
该函数包含在头文件 <stdio.h> 中。其基本用法包括:
- 输出单个字符:当参数
char是一个被单引号引起来的字符时,putchar 输出该字符。例如:putchar('a');会输出字符 'a'。 - 输出ASCII码对应的字符:当参数
char是一个介于0~127之间的十进制整型数时,putchar 输出该ASCII码对应的字符。例如:putchar(65);会输出字符 'A'。 - 输出字符变量:当参数
char是一个用char定义的字符型变量时,putchar 输出该变量的字符。例如:char ch = 'a'; putchar(ch);会输出字符 'a'。
返回值
当输出正确时,putchar 返回输出的字符转换为的 unsigned int 值;当输出错误时,返回 EOF(文件结束符)。
printf():将格式化的字符串输出到标准输出。例如:
printf("Character: %c\n", 'A'); // 输出字符 'A'
字符输入与输出的使用场景和示例代码
- 使用getchar()和putchar()进行字符的输入与输出:
#include <stdio.h> int main() { char c; c = getchar(); // 读取一个字符 putchar(c); // 输出读取的字符 return 0; } - 使用scanf()和printf()进行格式化输入与输出:
#include <stdio.h> int main() { char c; printf("Enter a character: "); scanf("%c", &c); // 读取一个字符并存储在变量c中 printf("You entered: %c\n", c); // 输出读取的字符 return 0; }
两种输入输出方式的区别:
getchar()和putchar()专门用于单个字符的输入输出,简洁直接;
scanf()和printf()功能更强大,能处理多种类型数据,格式化输入输出,更灵活多变。
注意事项和常见问题处理技巧(再次强调)
- 缓冲区问题:在使用
scanf()时,如果之前有输入,可能会读取到之前的换行符或空格。可以使用getchar()来清空缓冲区。例如:getchar();来读取并丢弃缓冲区中的字符。
格式化输入与输出:
在C语言中,格式化输入与输出是非常重要的,因为它允许我们按照特定的格式来读取和显示数据。以下是一些关键要点:
-
标准输入输出函数:如
printf()和scanf()用于格式化输出和输入。 -
格式化字符串:由
%开始的指令组成,指定了如何显示或读取数据。 -
转换说明符:如
%d(整数)、%f(浮点数)、%s(字符串)等,用于指定输入输出的数据类型。 -
动态内存分配:对于字符串的动态处理非常重要,如使用
malloc()和calloc()函数为字符串分配内存,使用realloc()函数调整内存大小。(先作了解)
以下的例子,在加入了转换说明符的前提下,更加详细地展示了如何使用printf()和scanf():
#include <stdio.h>
int main() {
int num;
float fnum;
char str[20];
// 输入
printf("Enter an integer: ");
scanf("%d", &num);
printf("Enter a float: ");
scanf("%f", &fnum);
printf("Enter a string: ");
scanf("%s", str);
// 输出
printf("Integer: %d\n", num);
printf("Float: %.2f\n", fnum);
printf("String: %s\n", str);
return 0;
}
输入/输出流:
C语言中的输入/输出流主要包括标准输入流(stdin)、标准输出流(stdout)以及文件输入/输出流。
标准输入流(stdin)和标准输出流(stdout)
- 标准输入流(stdin):数据从键盘输入到程序,表示为
stdin。例如,用户通过键盘输入数据时,数据通过stdin进入程序。 - 标准输出流(stdout):数据从程序输出到屏幕,表示为
stdout。例如,程序输出结果时,通过stdout显示在屏幕上。
文件输入/输出流
- 文件输入流:数据从文件读取到程序,通常使用
fopen函数打开文件进行读取。 - 文件输出流:数据从程序输出到文件,通常使用
fopen函数以写入模式打开文件进行写入。
输入/输出函数及其用法
- getchar()和putchar():用于字符的输入和输出。
getchar()从标准输入流读取一个字符,putchar()向标准输出流写入一个字符。 - gets()和puts():用于字符串的输入和输出。
gets()从标准输入流读取一行字符串,puts()向标准输出流写入一个字符串。 - scanf()和printf():用于格式化输入和输出。
scanf()从标准输入流读取格式化输入,printf()向标准输出流写入格式化输出。
输入/输出流的错误处理和结束标志
- 错误处理:在使用输入函数如
scanf()时,如果输入不匹配或遇到文件结束标志EOF(通常值为-1),函数会返回一个错误代码。 - 结束标志:在标准输入中,可以通过按Ctrl+Z(Windows)或Ctrl+D(Unix/Linux)然后回车来发送文件结束标志EOF,表示输入结束。
实际应用场景和示例代码
- 读取用户输入:使用
scanf()或getchar()从键盘读取用户输入。 - 文件读写:使用
fopen()、fread()、fwrite()等函数进行文件的读写操作。 - 格式化输出:使用
printf()进行格式化输出,例如打印表格或报告。
程序设计:
在C语言中,程序设计主要包括以下几个要点:
首先我们来复习一下:
-
数据的类型和变量:C语言是一种强类型语言,每个变量在使用前都必须声明,其数据类型决定了变量可以存储的数据种类和大小。
-
int age; // 声明一个整型变量 age = 25; // 给变量赋值运算符和表达式:C语言提供了丰富的运算符,用于执行不同的数学和逻辑运算。
-
int sum = 5 + 3; // 加法运算 int diff = 10 - 4; // 减法运算其次我们来大致了解一下之后要学到的内容:(后面会详讲。)
-
控制结构:C语言提供了三种基本的控制结构:顺序、选择(分支)和循环。
if (age >= 18) { // 条件为真时执行的代码 printf("You are an adult.\n"); } else { // 条件为假时执行的代码 printf("You are not an adult.\n"); } for (int i = 0; i < 5; i++) { // 循环执行的代码 printf("Hello, World!\n"); }函数:C语言允许将程序逻辑封装在函数中,提高代码的模块化和复用性。
-
int add(int a, int b) { return a + b; } //这是一个非常简单的C语言函数,名为add,它接收两个整数类型的参数a和b,然后返回它们的和。 //下面是对这个函数的详细解释: // • int:这是函数返回类型的声明,表示该函数将返回一个整数类型的值。 // • add:这是函数的名称,你可以通过这个名字来调用这个函数。 // • (int a, int b):这是函数的参数列表,括号内定义了两个参数,a和b,它们都是整数类型(int)。这意味着当你调用这个函数时,需要传递两个整数给它。 // • 函数体 `{ return a + b; }`:这里包含了函数的执行代码。`return`语句用于返回函数的执行结果,这里是`a`和`b`的和。当函数执行到这个`return`语句时,它会计算`a + b`的值,并将这个值返回给函数的调用者。 //所以,如果你在其他地方调用这个函数,比如: // int result = add(3, 4); //那么result的值将会是7,因为3 + 4等于7。 //这个函数非常基础,但它展示了函数定义、参数传递、返回值等编程中的基本概念。数组和指针:数组是C语言中处理和操作集合数据的主要方式,指针提供了直接访问内存地址的能力。(后面会详讲)
-
int numbers[5] = {1, 2, 3, 4, 5}; // 声明并初始化一个数组 int *p = &numbers[0]; // 声明一个指针并将其初始化为数组第一个元素的地址字符串和字符处理:C语言通常使用字符数组来处理字符串。
char name[50]; strcpy(name, "John "); // 使用库函数复制字符串指针和内存管理:C语言提供了对内存的低级控制,但也增加了管理内存泄漏和错误操作内存的风险。(仅了解)
int *p = (int *)malloc(sizeof(int) * 5); // 动态分配内存 free(p); // 释放分配的内存结构和联合:C语言中的结构和联合允许将多个相关的变量组合在一起。
-
struct Person { char name[50]; // 定义一个字符数组,用于存储人的名字,最大长度为49个字符(因为最后一个字符需要存储字符串结束符'\0') int age; // 定义一个整数,用于存储人的年龄 }; struct Person person = {"John ", 30}; //这里定义了一个名为Person的结构体,它包含两个成员: // 1. name:一个字符数组,长度为50,用于存储一个人的名字。需要注意的是,由于C语言字符串需要以空字符'\0'结尾,因此实际能够存储的最大字符数是49。 // 2. age:一个整数,用于存储一个人的年龄。 //结构体实例化 // struct Person person = {"John ", 30}; //这里创建了一个Person类型的变量person,并在声明的同时对其进行了初始化。初始化的方式是提供一个初始化列表,列表中的值按照结构体成员的顺序进行赋值。 // • "John ":这是name成员的初始值,注意字符串末尾有一个空格字符。这意味着person.name将包含字符串"John "(包括末尾的空格)。 // • 30:这是age成员的初始值,表示person的年龄是30岁。 //注意事项 // 1. 字符串末尾的空字符:由于name是一个字符数组,它存储的是C语言风格的字符串,因此末尾需要有一个空字符'\0'来表示字符串的结束。在这个例子中,由于我们显式地提供了一个字符串字面量"John "作为初始化值,编译器会自动在字符串末尾添加空字符。 // 2. 初始化列表的顺序:在C语言中,初始化列表中的值必须按照结构体成员在定义中出现的顺序进行赋值。如果成员的类型或数量不匹配,或者初始化列表中的值多于或少于结构体的成员数,编译器将报错。 // 3. 结构体的使用:在定义了结构体之后,我们可以创建该结构体的变量(如上面的person),并通过这些变量来访问或修改结构体的成员。例如,我们可以使用person.name来访问或修改person的名字,使用person.age来访问或修改person的年龄。文件处理:C语言提供了标准库函数来进行文件的读写操作。
FILE *fp = fopen("myfile.txt", "w"); // 打开文件 fprintf(fp, "Hello, World!\n"); // 写入文件 fclose(fp); // 关闭文件异常处理:C语言没有内置的异常处理机制,但可以通过返回错误码或使用goto语句来实现。
-
int result = divide(10, 0); if (result == ERROR_CODE) { // 处理错误 }
二.宏与预定义:
宏定义:
在C语言中,宏定义是非常常见且有用的一种技术。它可以用来定义常量、进行参数替换以及实现一些复杂的文本处理。
#define用于定义宏,可以用来定义常量、函数、表达式等。宏定义的格式为
#define 宏名 宏体
-
定义常量
#define PI 3.14159
这种方式定义的宏在预处理阶段就会被替换成对应的值,相当于一个全局的常量。
-
文本替换
#define MIN(a, b) ((a) < (b) ? (a) : (b)) //这个宏接受两个参数a和b,通过三目运算符(?:)比较这两个参数, //如果a小于b,则返回a;否则返回b。宏定义中的参数用括号括起来是一种常见的做法,目的是为了防止参数中的某些操作会先于宏参数的括号被执行。
-
PS:三目运算符也叫条件运算符、三元运算符,是由一个问号和一个冒号组成。
-
( ? : ),是 C语言中惟一的一个三目运算符,由条件运算符可以构成条件表达式,它的格式为:
-
表达式1 ? 表达式2 : 表达式3
-
先执行表达式1,若表达式1的结果为真,那么执行表达式2,而这个整体的运算式的结果是表达式2的结果;若表达式1的结果为假,执行表达式3,运算式的结果是表达式3的果。
-
条件编译(新手仅了解)
#define DEBUG #ifdef DEBUG //#ifdef可以用它区隔一些与特定头文件、程序库和其他文件版本有关的代码。可翻译为:如果宏定义了语句1则执行程序2。 #define PRINT_DEBUG(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define PRINT_DEBUG(fmt, ...) #endif宏定义还可以用来进行条件编译,只有在定义了DEBUG的情况下,PRINT_DEBUG才会进行文本替换,否则就不会进行任何操作。
-
宏函数
#define SQUARE(x) ((x) * (x))宏定义也可以用来定义函数,只不过这种函数没有参数类型声明和返回类型声明,所有的参数都是通过宏展开进行的,因此可能会有一些隐患,比如类型安全问题和运算优先级问题。
-
计算数组长度(数组后面会详细讲)
#define ARRAY_LEN(array) (sizeof((array)) / sizeof((array[0]))宏定义还可以用来计算数组的长度,这种方式在编译期间就可以确定数组的长度,不需要在运行时通过循环来计算。
-
循环展开(新手仅了解)
#define REPEAT(n, macro) \ REPEAT_ ## n(macro) #define REPEAT_1(macro) macro(1) #define REPEAT_2(macro) REPEAT_1(macro) macro(2) #define REPEAT_3(macro) REPEAT_2(macro) macro(3) ... REPEAT(10, PRINT_DEBUG)宏定义还可以用来实现循环展开,这种方式可以在编译期间就完成一些重复的工作,不需要在运行时通过循环来实现。
注意:宏定义虽然强大,但也有一些风险,比如无法进行类型检查,容易出错,而且在调试时可读性较差。因此,应当在确实需要时才使用宏定义,并尽量减少使用宏定义的复杂性。
文件包含:
文件包含是C语言预处理器的一个重要功能,它可以让程序员将一个文件的内容包含到另一个文件中。主要用于共享头文件中的声明和宏定义。
基本语法
文件包含使用 #include 预处理指令。
包含系统头文件:
#include <stdio.h>
尖括号 <> 表示文件是系统头文件,编译器会在系统的 include 目录下查找。
包含用户头文件:
#include “XXXXX.h"
双引号 "" 表示文件是用户头文件,编译器会在当前文件的目录或指定的 include 目录下查找。
文件包含的类型
直接包含:
直接包含文件的内容到当前位置。
#include "XXXXX.h"
间接包含:
通过包含另一个文件来间接包含文件。
// In "YYYYY.h"
#include "XXXXX.h"
// In "main.c"
#include "YYYYY.h"
// "XXXXX.h" is indirectly included
注意事项
文件包含是C语言重用和模块化代码的关键,使用时需确保头文件路径正确、避免宏冲突、注意包含顺序、区分系统与用户文件,并善用条件编译来控制不同环境下的编译,从而提高代码的组织和可维护性。
条件编译:
条件编译是指在编译过程中,根据特定的条件决定哪些代码被编译,哪些代码被忽略。这主要通过预处理器指令实现,如#ifdef、#ifndef、#if、#elif、#else和#endif。
- #ifdef:如果某个宏已被定义,则编译其后的代码块。
- #ifndef:如果某个宏未被定义,则编译其后的代码块。
- #if:后面跟一个常量表达式,如果表达式的值为非零,则编译其后的代码块。
- #elif:相当于“else if”,可以在多个条件之间进行选择。
- #else:如果前面的条件都不满足,则编译其后的代码块。
- #endif:用于结束一个条件编译块。
使用场景
- 控制调试语句:在程序中用条件编译将调试语句包裹起来,通过外部编译指令选项控制调试代码的启停状态,而不需修改源代码。
- 选择代码片段:在大型项目中,根据具体的情况选择不同的代码实现,例如在不同的硬件配置下使用不同的函数实现。
5050

被折叠的 条评论
为什么被折叠?



