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

简介: 我的第一门语言就是C,但是学艺不精,中途跑去学了C#和Java后,感觉到了C的重要性,毕竟是最接近底层的语言,又跑回来学C。毕竟前两门的控制语句,变量什么的都是类似的,回到C后只需要学习一些特定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类型。


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


例如:

// 定义一个包含姓名和成绩两个字段的学生信息
struct student {
    char* name;
    float score;
};
// 利用 typedef 声明一个新类型 StudentInfo 来表示学生信息
typdef struct student StudentInfo;


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


3.消除代码重复:有些场景下,同一个程序中需要多次使用相同的数据类型定义,这时可以使用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];


2.定义带参数的宏:可以使用#define来定义带参数的宏,方便在代码中进行简单替换。


例子:

#define SQUARE(x) ((x) * (x))
int main() {
   int a = 5;
   int b = SQUARE(a); // b = ((a) * (a)) = 25
   return 0;
}


3.定义条件编译:可以使用#define来定义条件编译指令,根据条件来选择编译哪些代码。这样可以写出能够适应不同操作系统或硬件平台的程序。


例子:

#define WINDOWS
#ifdef WINDOWS
    #include <windows.h>
#else
    #include <unistd.h>
#endif


4.定义函数或类型名称:可以使用#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)格式控制符


在 C 语言中,格式控制的功能由标准库函数 printf 和 scanf 提供。printf 可以输出指定格式的文本内容,而 scanf 则可以从输入流(通常是键盘)读取指定格式的数据。


以下是一些常用的格式控制符号:


%d 或 %i: 把整数类型的值插入到输出中;

%f 或 %F: 把浮点数类型的值插入到输出中;

%c: 把字符类型的值插入到输出中;

%s: 把字符串类型的值插入到输出中;

%o: 以八进制形式输出整数类型的值;

%x、%X:以十六进制形式输出整数类型的值;

%u: 输出无符号类型的十进制整数



特殊的格式控制符


%8s是一个 printf 函数的格式控制符,在字符串类型的输出中非常有用。这个格式控制符告诉 printf 函数把字符串占据 8 个字符的位置,并在左侧填充空格,以便更好地对齐输出。


具体来说,%8s的意思是输出一个长度为 8 个字符的字符串,如果字符串的实际长度小于 8,则在输出时在左侧填充空格,保证总共占据 8 个字符的位置(向右对齐)。


%8s呢?


母庸质疑,肯定是向左对齐啦!


例如,下面的代码使用了%8s格式控制符来输出一列水果名称:

#include <stdio.h>
int main() {
  printf("%-8s|%-8s|%-8s\n", "Apple", "Banana", "Peach");
  printf("%-8s|%-8s|%-8s\n", "Orange", "Watermelon", "Mango");
  return 0;
}


在这段代码中,“%-8s”告诉 printf 将每个字符串都输出为长度为 8 的字符串, - 符号表示左对齐。如果字符串的长度少于 8,则在其右侧填充空格,而如果字符串长度大于 8,则超出部分直接被截断。

Apple   |Banana  |Peach   
Orange  |Watermelon|Mango   


除开格式控制符,对于其他的输入输出函数也是有的,简要介绍两个:


2)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



四、文件读写


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


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


1)打开文件


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

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


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

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

85fa0c7e19e640f493498aca8550a0cd.png


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


“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);

目录
相关文章
|
6月前
|
存储 编译器 C语言
C语言难?深度剖析第一段C语言代码—带你快速入门C语言
C语言难?深度剖析第一段C语言代码—带你快速入门C语言
81 0
|
6月前
|
存储 编译器 C语言
C语言易错知识点总结1
C语言易错知识点总结1
49 0
|
6月前
|
存储 编译器 Linux
C语言易错知识点总结2
C语言易错知识点总结2
49 0
|
5月前
|
存储 缓存 C语言
C语言易错小贴士
C语言易错小贴士
|
6月前
|
存储 编译器 C语言
C语言易错知识点总结3
C语言易错知识点总结3
46 1
|
编译器 C语言
C语言杂碎知识 最后两个肯定你不知道
C语言杂碎知识 最后两个肯定你不知道
|
存储 编译器 C语言
C语言学习分享(第三次)------了解C语言-下
我们紧接着上一章了解C语言-上往下分享C语言的基础知识,在这之前我们先引入一个表格,叫ASCLL码表,它向我们展示了每个Ascll码值对应的字符,这里我们可以跳转到Ascll码表了解一下.我们可以看见Ascll码值从65到90是大写字母A到Z,Ascll码值97到122是小写字母a到z.大小写字母相差32个Ascll值,也就是相差一个空格字符(space).我们先了解一下,后期做题可能会用
|
存储 编译器 C语言
C语言学习分享(第二次)------了解C语言-上
这篇文章主要给大家分享基础的C语言知识,让我们对C语言有一个大概的了解,这篇文章会从循环和函数一直讲到指针和结构体,但是都是对于每个知识点的简单认识,不会详细的说明,就是告诉大家C语言有这个东西,在我们后面的学习(文章)中会给大家一一详解,这篇文章很长,知识点多,大家可以看目录按需阅读.现在正式开始我们C语言的学习.(分为两次讲解)
|
C语言
C语言刷题系列——2.输入输出练习
C语言刷题系列——2.输入输出练习
186 0
|
程序员 编译器 C语言
初识C语言(5)C语言一些基本常识
初识C语言(5)C语言一些基本常识
63 0