C语言学习教程(一)

C语言学习教程(一):本系列教程第0-5章。

0-Preface

本教程学习内容基于之前在网上无意中发现的一本书《C Programming Tutorial》。

如果打算学好操作系统原理软件逆向二进制漏洞挖掘的话,那么C语言就是基石,你一定要学的特别透彻才可以。

比较巧的是,当在学习的过程中,上网搜索不会的问题时,我发现这本书在菜鸟教程有中文翻译版:(https://www.runoob.com/cprogramming/c-tutorial.html)。本来我以为我可以完全按照中文版的教程来学习就行了,但是我发现中文教程中代码中的一些内容被更改了,然后导致理解起来就怪怪的,因此我决定对比着来学习,屏幕左侧是菜鸟教程中文版,右侧是英文原版,然后一点点的去整理。

Github项目地址:(https://github.com/Mr-Aur0ra/C_Study)

创作不易,您的点赞Star就是对我最大的支持。

可以通过知乎C语言学习教程Mr.Aur0ra’s Blog的在线方式学习。也可以下载该项目到本地,然后通过markdown编辑器(推荐Typora)打开学习。

真心地希望该系列教程可以帮助大家打好C语言的基础。

其他:

  • PDF英文原版可通过Github项目下载。
  • 文中第7、10、11章的几处插图使用了菜鸟教程的插图。
  • 请一定要结合《C Programming Tutorial》原文看文章中的出现的示例代码。文中出现的示例代码可能因为注释太简单就直接省略了。原文中这些简单注释还是有的。
  • 对原书中的部分插图和代码示例进行了优化

后续内容还在更新,全书共计30章。

1-C Language Overview

C 编程语言是一种通用的高级语言,最初由 Dennis M. Ritchie 开发,用于在贝尔实验室开发 UNIX 操作系统。 C 最初于 1972 年在 DEC PDP-11 计算机上实现。

1978 年,Brian Kernighan 和 Dennis Ritchie 制作了第一个公开可用的 C 描述,现在称为 K&R 标准。

UNIX 操作系统、C 编译器和基本上所有的 UNIX 应用程序都是用 C 编写的。由于各种原因,C 现在已成为一种广泛使用的专业语言。

C 最初用于系统开发工作,特别是构成操作系统的程序。 C 被采用为一种系统开发语言,因为它生成的代码运行速度几乎与用汇编语言编写的代码一样快。 使用 C 的一些示例:

  • 操作系统
  • 语言编译器
  • 汇编器
  • 文本编辑器
  • 打印后台处理程序
  • 网络驱动程序
  • 现代程序
  • 数据库
  • 语言翻译
  • 实用程序

2-C Environment Setup

在开始之前,需要确保你的电脑上有文本编辑器和C编译器。

文本编辑器比如:Notepad、Vim、VsCode等。

C编译器本书使用GNU C/C++。

(1)Linux下安装GNU C/C++

Linux或Unix默认安装了该编译器、可通过以下命令进行查看:

1
mrx@Deepin:~/Desktop$ gcc -v

显示结果如下:

image-20220905221613799

如果未安装,详细安装步骤请参考:http://gcc.gnu.org/install。

(2)MacOS下安装GNU C/C++

MaxOS安装GCC最简单的方式就是直接安装XCode开发环境。XCode可直接应用商店下载即可。

1
2
3
4
5
➜  ~ gcc -v
Apple clang version 13.1.6 (clang-1316.0.21.2.5)
Target: x86_64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

(3)Windows下安装GNU C/C++

Windows下安装GCC需要安装MinGW。具体安装步骤请参考:https://mingw.osdn.io。

3-C Program Structure

(1)C Hello World

一个C程序基本上由以下几部分组成:

  • 预处理指令
  • 函数
  • 变量
  • 语句和表达式
  • 注释

下面是一个简单的输出"Hello World"的程序代码:

1
2
3
4
5
6
7
#include <stdio.h>

int main(){
    /* my first program in C */
    printf("Hello World! \n");
    return 0;
}

上面的程序各部分的含义:

第一行,#include <stdio.h>是一个预处理指令,它告诉C编译器在进行实际编译之前要包含文件"stdio.h"。

第二行,int main()是程序开始执行的主函数。

第三行,/*...*/是程序中的注释,在程序编译时该行会被编译器忽略。

第四行,printf(...),该程序中的另一个函数,其功能是在屏幕上输出指定的信息"Hello World!"。

第五行,终止main函数,并返回数值0。

(2)编译和运行C程序

保存上述代码到hello.c中,通过如下命令编译程序。

1
$ gcc -o hello hello.c

运行程序:

1
2
$ ./hello
Hello World! 

4-C Basic Syntax

(1)C中的标记

一个C程序由若干标记(token)组成,标记可以是关键字(keyword)、标识符(identifier)、常量(constant)、字符串文字(string literal)或符号(symbol)。

比如,以下C语句由5个标记组成。

1
printf("Hello World! \n");

5个标记拆分开是:

1
2
3
4
5
printf
(
"Hello World! \n"
)
;

(2)分号

在C程序中,分号(Semicolons)是语句终止符。也就是说,每个单独语句都必须以分号结尾,它表示一个逻辑实体的结束。

例如,以下是两个不同的语句:

1
2
printf("Hello World! \n");
return 0;

(3)注释

注释(Comments)就行C程序中的帮助文本,编译器会忽略它们。

单行注释//。如下所示:

1
// my first program in C

多行注释,以/*开头并且以*/结尾。如下所示:

1
/* my first program in C */

(4)标识符

C标识符(Identifiers)用于标识变量,函数或者任何其他用户定义项的名称。C标识符以字母A到Z、或a到z、或下划线_开头,后面跟零个或多个字母,下划线和数字(0~9)。

C不允许在标识符(Identifiers)中使用标点符号,比如"@"、"$“和”%"。C是一种区分大小写的编程语言。因此,“Manpower"和"manpower"是C中两个不同的标识符。以下是一些可接受的标识符示例:

mohd       zara    abc   move_name  a_123
myname50   _temp   j     a23b9      retVal

(5)保留字

以下列表显示了C中的保留字。这些保留字不能用作常量或变量或任何其他标识符名称。

(6)C中的空白字符

仅包含空格的行(可能带有注释)称为空行,C编译器会完全忽略它。

空白字符是指在屏幕上显示为空白的字符,包括空格制表符换行符

空白字符将语句的一部分与另一部分分开,并使编译器能够识别语句中的一个元素(比如:int)在哪里结束,而下一个元素在哪里开始。因此,在以下语句中,int 和 age 之间必须至少有一个空白字符(通常是空格),以便编译器能够区分它们。

1
int age;

另一方面,在下面的声明中:fruit=之间,或者=apples之间不需要空格字符,尽管如果希望出于可读性目的,可以随意包含一些空格字符。

1
fruit = apples + oranges;   // get the total fruit

5-Data Types

在C语言中,数据类型(Data Types)是指用于声明不同类型的变量或函数的扩展系统。变量的类型决定了它在存储中占用了多少空间以及如何解释存储的位模式。

C中的数据类型可以分类如下:

S.N. Types and Description
1 Basic Types(基本类型):
它们都是算数类型。包含两种类型:整数类型(integer types)和浮点类型(floating-point types)
2 Enumerated types(枚举类型):
它们都是算数类型。它们用于定义在整个程序中只能分配某些离散整数值的变量。
3 The type void(void类型):
类型说明符void。表示没有可用的值。
4 Derived Types(派生类型):
它们包括:指针类型(Pointer types)、数组类型(Array types)、结构类型(Structure types)、联合类型(Union types)和函数类型(Function types)。

数组类型和结构类型统称为聚合类型。函数类型指的是函数的返回值类型。本节仅介绍Basic Types(基本类型)The type void(void类型),其它类型后续再讲解。

(1)基本类型(整数类型)

下表列出了标准整数类型(Integer Types)及其存储大小和取值范围的详细信息。

Type Storage size Value range
char 1 byte -128 to 127 或者 0 to 255
unsigned char 1 byte 0 to 255
signed char 1 byte -128 to 127
short 2 byte -32,768 to 32,767
unsigned short 2 byte 0 to 65,535
int 4 byte -32,768 to 32,767
unsigned int 4 byte 0 to 65,535
long 8 byte -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
unsigned long 8 byte 0 to 18,446,744,073,709,551,615

默认情况下,short、int和long都是有符号的。

因为各种存储类型的存储大小与系统位数有关,故以上的存储大小和取值范围仅为参考。如果需要在特定平台上获取某个数据类型或者变量的具体大小,可以使用sizeof运算符。表达式sizeof(type)可以得到对象或存储类型的存储字节大小。

以下是在任何机器上获取上述表中类型大小的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <limits.h>

int main(){
    printf("Storage size for char: %d \n", sizeof(char));
    printf("Storage size for unsigned char: %d \n", sizeof(unsigned char));
    printf("Storage size for signed char: %d \n", sizeof(signed char));
    printf("\n");

    printf("Storage size for short: %d \n", sizeof(short));
    printf("Storage size for unsigned short: %d \n", sizeof(unsigned short));
    printf("\n");

    printf("Storage size for int: %d \n", sizeof(int));
    printf("Storage size for unsigned int: %d \n", sizeof(unsigned int));
    printf("\n");

    printf("Storage size for long: %d \n", sizeof(long));
    printf("Storage size for unsigned long: %d \n", sizeof(unsigned long));
    return 0;
}

程序运行结果:

$ gcc -o get_size1 get_size1.c 
$ ./get_size1 

Storage size for char: 1 
Storage size for unsigned char: 1 
Storage size for signed char: 1 

Storage size for short: 2 
Storage size for unsigned short: 2 

Storage size for int: 4 
Storage size for unsigned int: 4 

Storage size for long: 8 
Storage size for unsigned long: 8 

(2)基本类型(浮点类型)

下表详细介绍了标准浮点类型(Floating-Point Types)及其存储大小和取值范围以及其精度的详细信息。

Type Storage size Value range Precision
float 4 byte 1.175494E-38 to 3.402823E+38 6位有效数字
double 8 byte 2.225074E-308 to 1.797693E+308 15位有效数字
long double 16 byte 1.797693E+308 to 1.797693E+308 18位有效数字

头文件float.h中定义了允许我们在程序中使用这些值和有关实数二进制表示的其他详细信息的宏。以下示例将打印浮点类型及其范围值占用的存储空间:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <float.h>

int main(){
    printf("Storage size for float: %d \n", sizeof(float));
    printf("Minimum float positive value: %E\n", FLT_MIN);
    printf("Maximum float positive value: %E\n", FLT_MAX);
    printf("Precision value: %d\n", FLT_DIG);
    printf("\n");

    printf("Storage size for double: %d \n", sizeof(double));
    printf("Minimum double positive value: %E\n", DBL_MIN);
    printf("Maximum double positive value: %E\n", DBL_MAX);
    printf("Precision value: %d\n", DBL_DIG);
    printf("\n");

    printf("Storage size for long double: %d \n", sizeof(long double));
    printf("Minimum long double positive value: %E\n", LDBL_MIN);
    printf("Maximum long double positive value: %E\n", LDBL_MAX);
    printf("Precision value: %d\n", LDBL_DIG);
    printf("\n");

    return 0;
}

程序运行结果:

$ gcc -o get_size2 get_size2.c
$ ./get_size2           

Storage size for float: 4 
Minimum float positive value: 1.175494E-38
Maximum float positive value: 3.402823E+38
Precision value: 6

Storage size for double: 8 
Minimum double positive value: 2.225074E-308
Maximum double positive value: 1.797693E+308
Precision value: 15

Storage size for long double: 16 
Minimum long double positive value: 1.797693E+308
Maximum long double positive value: 1.797693E+308
Precision value: 18

(3)void类型

void类型指定没有可用的值。它通常用于以下三种情况:

S.N. Type and Description
1 Function returns as void(函数返回值为void):
C中有很多不返回值的函数,没有返回值的函数的返回类型为void。
例如:void exit(int status);
2 Function arguments as void(函数参数为void):
C中有很多不接受任何参数的函数,没有任何参数的函数可以接受void类型。
例如:int rand(void);
3 Pointers to void(指针指向为void):
void *类型的指针可以表示对象的地址,但不表示其类型。
例如:void *malloc(size_t size); 这会返回指向void的指针,该指针可以转换为任何数据类型。

此时如果无法理解 void 类型,我们还会在接下来的章节中介绍这些内容。

汇总后的C数据类型如下:

image-20220906104022374

updatedupdated2024-03-112024-03-11