【C语言】关于我回头学的那些输入输出等(四)

前言

我的第一门语言就是C,但是学艺不精,中途跑去学了C#和Java后,感觉到了C的重要性,毕竟是最接近底层的语言,又跑回来学C。

毕竟前两门的控制语句,变量什么的都是类似的,回到C后只需要学习一些特定C的语法,比如宏,预编译指令等等,这些对我来说都是陌生的词汇。

所以边学边记录一下以前的知识。



一、typedef

typedef是数据类型定义的关键字,它用于给已有的数据类型取一个新的名称。typedef通常用于简化复杂的数据类型定义、增加程序的可读性和可维护性。

具体地说,使用typedef可以实现以下几个方面的功能:

  1. 简化复杂数据类型:对于一些比较复杂、难以阅读和理解的类型定义,我们可以利用typedef为其起一个更加清晰明了、易于理解的名称,提高代码的可读性。

例如:

// 使用typedef之前
struct Books
{
    
    
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} Book;//在java里,这个Book就相当于一个类的对象名
 
int main( )
{
    
    
   strcpy( Book.title, "C 教程");
   strcpy( Book.author, "Runoob"); 
   strcpy( Book.subject, "编程语言");
   Book.book_id = 12345;
 
   printf( "书标题 : %s\n", Book.title);
   printf( "书作者 : %s\n", Book.author);
   printf( "书类目 : %s\n", Book.subject);
   printf( "书 ID : %d\n", Book.book_id);
 
   return 0;
}


// 使用typedef之后-------------------------------
typedef struct Books
{
    
    
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} Book;//在java里,这个Book就相当于一个类
 
int main( )
{
    
    
   Book book;
 
   strcpy( book.title, "C 教程");
   strcpy( book.author, "Runoob"); 
   strcpy( book.subject, "编程语言");
   book.book_id = 12345;
 
   printf( "书标题 : %s\n", book.title);
   printf( "书作者 : %s\n", book.author);
   printf( "书类目 : %s\n", book.subject);
   printf( "书 ID : %d\n", book.book_id);
 
   return 0;
}

在这个例子中,我们用typedef将原本比较冗长的结构体定义简化为了一个更加容易理解的Book类型。

  1. 声明复合数据类型:当我们需要把多个基本数据类型组合起来形成一个新的、自定义的数据类型时,可以使用typedef来声明这种新型数据类型。

例如:

// 定义一个包含姓名和成绩两个字段的学生信息
struct student {
    
    
    char* name;
    float score;
};

// 利用 typedef 声明一个新类型 StudentInfo 来表示学生信息
typdef struct student StudentInfo;

在这个例子中,我们将原始结构体student封装成了一个新型StudentInfo数据类型,并可以按照此种类型声明变量。

  1. 消除代码重复:有些场景下,同一个程序中需要多次使用相同的数据类型定义,这时可以使用typedef来避免代码的重复。

例如:

// 定义一个返回两个整数之和的函数指针类型
int (*SumFuncPtr)(int a, int b);

// 此处再次使用 SumFuncPtr 类型时,就可以直接用 typedef 定义
typdef int (*SumFuncPtr)(int a, int b);

在这个例子中,我们把两次相同的函数指针类型定义合并为了一个typedef语句。

总之,在实际开发过程中,typedef能帮助我们更形象地表示某一类数据,提高可读性、可维护性和代码复用性。


二、#define

这里简要的概述一下#define,到后面预编译的时候会详解。

#define是C系语言中的一个预处理指令,主要用于给常量、表达式和函数等定义一个名字,这样可以提高程序的可读性和维护性。

使用#define定义的常量也被称为宏常量,使用方法类似于普通常量。

主要作用如下:

  1. 定义常量:可以使用#define来定义不变的值或符号,方便代码中多次使用。

例子:

#define MAX_NUM 100

int array[MAX_NUM];
  1. 定义带参数的宏:可以使用#define来定义带参数的宏,方便在代码中进行简单替换。

例子:

#define SQUARE(x) ((x) * (x))

int main() {
   int a = 5;
   int b = SQUARE(a); // b = ((a) * (a)) = 25
   return 0;
}
  1. 定义条件编译:可以使用#define来定义条件编译指令,根据条件来选择编译哪些代码。这样可以写出能够适应不同操作系统或硬件平台的程序。

例子:

#define WINDOWS

#ifdef WINDOWS
    #include <windows.h>
#else
    #include <unistd.h>
#endif
  1. 定义函数或类型名称:可以使用#define来为复杂表达式或类型声明定义一个简短的别名。这样可以提高代码可读性和易维护性。

例子:

typedef struct Student {
    int id;
    char name[20];
} Stu;

#define MAX_NUM 100

Stu student_array[MAX_NUM];

三、输入&输出

大家肯定有过printf() 用于格式化输出到屏幕的经历,这个就是输出。

C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。

scanf() 函数用于从标准输入(键盘)读取并格式化, printf() 函数发送格式化输出到标准输出(屏幕)。

%f 格式化输入并输出浮点型数据

#include <stdio.h>
int main()
{
    
    
    float f;
    printf("Enter a number: ");
    // %f 匹配浮点型数据
    scanf("%f",&f);
    printf("Value = %f", f);
    return 0;
}

对于其他的输入输出函数也是有的,简要介绍两个:

1)getchar() & putchar() 函数

int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。

int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符。

#include <stdio.h>
 
int main( )
{
    
    
   int c;
 
   printf( "Enter a value :");
   c = getchar( );
 
   printf( "\nYou entered: ");
   putchar( c );
   printf( "\n");
   return 0;
}

输入runoob,结果如下:

$./a.out
Enter a value :runoob
You entered: r

2)gets() & puts() 函数

它会等待您输入一些文本,当您输入一个文本并按下回车键时,程序会继续并读取一整行直到该行结束。

#include <stdio.h>
 
int main( )
{
    
    
   char str[100];
 
   printf( "Enter a value :");
   gets( str );
 
   printf( "\nYou entered: ");
   puts( str );
   return 0;
}

输入runoob,结果如下:

$./a.out
Enter a value :runoob
You entered: runoob


四、文件读写

一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节。

C 语言不仅提供了访问顶层的函数,也提供了底层(OS)调用来处理存储设备上的文件。

1)打开文件

fopen()函数来创建一个新的文件或者打开一个已有的文件,这个调用会初始化类型 FILE 的一个对象, FILE 包含了所有用来控制流的必要的信息。

下面是这个函数调用的原型:

FILE *fopen( const char *filename, const char *mode );

在这里,filename 是字符串,用来命名文件,访问模式 mode 的值可以是下列值中的一个:

在这里插入图片描述

如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:

“rb”, “wb”, “ab”, “rb+”, “r+b”, “wb+”, “w+b”, “ab+”, “a+b”

2)关闭文件

int fclose( FILE *fp );

如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。EOF 是一个定义在头文件 stdio.h 中的常量。

这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。

3)写入文件

1.fputc

int fputc( int c, FILE *fp );

函数fputc()把整数 c 的值写到 fp 所指向的**输出流(文件)**中。如果写入成功,它会返回写入的字符,如果发生错误,则会返回 EOF。

2.fprintf

int fprintf(FILE *fp,const char *format, …)

函数把一个字符串写入到文件中。

3.综合案例

#include <stdio.h>
 
int main()
{
    
    
   FILE *fp = NULL;
 
   fp = fopen("/tmp/test.txt", "w+");
   fprintf(fp, "This is testing for fprintf...\n");
   fputs("This is testing for fputs...\n", fp);
   fclose(fp);
}

当上面的代码被编译和执行时,它会在 /tmp 目录中创建一个新的文件 test.txt,并使用两个不同的函数写入两行。接下来让我们来读取这个文件。

注意:

  • 请确保您有可用的 tmp 目录,如果不存在该目录,则需要在您的计算机上先创建该目录。

  • /tmp 一般是 Linux 系统上的临时目录,如果你在 Windows 系统上运行,则需要修改为本地环境中已存在的目录,例如: C:\tmp、D:\tmp等。

4)读取文件

1.fgetc

int fgetc( FILE * fp );

fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF。

2.fgets

char *fgets( char *buf, int n, FILE *fp );

函数 fgets()fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个null 字符来终止字符串。

如果这个函数在读取最后一个字符之前,就遇到换行 '\n' 或文件末尾,则返回读取到的字符,包括换行符。

3.fscanf

int fscanf(FILE *fp, const char *format, …)

可以指定读取多少字符。

遇到空格和换行时,停止读取。

```c
#include <stdio.h>
 
int main()
{
    
    
   FILE *fp = NULL;
 
   fp = fopen("/tmp/test.txt", "w+");
   fprintf(fp, "This is testing for fprintf...\n");
   fputs("This is testing for fputs...\n", fp);
   fclose(fp);
}


```c
#include <stdio.h>
 
int main()
{
   FILE *fp = NULL;
   char buff[255];
 
   fp = fopen("/tmp/test.txt", "r");
   fscanf(fp, "%s", buff);
   printf("1: %s\n", buff );
 
   fgets(buff, 255, (FILE*)fp);
   printf("2: %s\n", buff );
   
   fgets(buff, 255, (FILE*)fp);
   printf("3: %s\n", buff );
   fclose(fp);
 
}

1: This
2: is testing for fprintf…
3: This is testing for fputs…

首先,fscanf() 方法只读取了 This,因为它在后边遇到了一个空格。其次,调用 fgets() 读取剩余的部分,直到行尾。最后,调用 fgets() 完整地读取第二行。

5)二进制 I/O 函数

这两个函数都是用于存储块的读写 - 通常是数组或结构体。

size_t fread(void *ptr, size_t size_of_elements, 
             size_t number_of_elements, FILE *a_file);
              
size_t fwrite(const void *ptr, size_t size_of_elements, 
             size_t number_of_elements, FILE *a_file);

猜你喜欢

转载自blog.csdn.net/qFAFAF/article/details/129876518