0-Preface
最近在学习CSAPP(深入理解计算机系统(第三版))的过程中深感自己C语言的基础有多薄弱,因此打算好好再系统的学习一遍C语言。
本教程学习内容基于之前在网上无意中发现的一本书《C Programming Tutorial》。在我看来,这本书和国内很多课堂上教的C语言书最大的不同是实验代码。不知为何,大学里教的C语言教程给我的就是在不断地做数学题,不断的做数学题。输出个等差数列,等比数列,求个水仙花数。。。没错编程很大的一部分作用是解决生活中实际的问题,但这些教程让我感觉到这门语言只能解决数学题。与计算机相关的内容寥寥无几。编程语言难道不应该是和计算机密切相关吗??
如果打算学好操作系统原理
、软件逆向
、二进制漏洞挖掘
的话,那么C语言就是基石,你一定要学的特别透彻才可以。
比较巧的是,当在学习的过程中,上网搜索不会的问题时,我发现这本书在菜鸟教程有中文翻译版:
C语言教程 | 菜鸟教程https://www.runoob.com/cprogramming/c-tutorial.html
创作不易,您的点赞
就是对我最大的支持。
真心地希望该系列教程可以帮助大家打好C语言的基础。
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 的一些示例:
在开始之前,需要确保你的电脑上有文本编辑器和C编译器。
文本编辑器比如:Notepad、Vim、VsCode等。
C编译器本书使用GNU C/C++。
Linux或Unix默认安装了该编译器、可通过以下命令进行查看:
mrx@Deepin:~/Desktop$ gcc -v
显示结果如下:
MaxOS安装GCC最简单的方式就是直接安装XCode开发环境。XCode可直接应用商店下载即可。
➜ ~ 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
Windows下安装GCC需要安装MinGW。具体安装步骤请参考:https://mingw.osdn.io/
一个C程序基本上由以下几部分组成:
下面是一个简单的输出"Hello World"的程序代码:
#include
int main(){
/* my first program in C */
printf("Hello World! \n");
return 0;
}
上面的程序各部分的含义:
第一行,#include
是一个预处理指令,它告诉C编译器在进行实际编译之前要包含文件"stdio.h"。
第二行,int main()
是程序开始执行的主函数。
第三行,/*...*/
是程序中的注释,在程序编译时该行会被编译器忽略。
第四行,printf(...)
,该程序中的另一个函数,其功能是在屏幕上输出指定的信息"Hello World!"。
第五行,终止main函数,并返回数值0。
保存上述代码到hello.c
中,通过如下命令编译程序。
$ gcc -o hello hello.c
运行程序:
$ ./hello
Hello World!
一个C程序由若干标记(token)组成,标记可以是关键字(keyword)、标识符(identifier)、常量(constant)、字符串文字(string literal)或符号(symbol)。
比如,以下C语句由5个标记组成。
printf("Hello World! \n");
5个标记拆分开是:
printf
(
"Hello World! \n"
)
;
在C程序中,分号(Semicolons)是语句终止符。也就是说,每个单独语句都必须以分号结尾,它表示一个逻辑实体的结束。
例如,以下是两个不同的语句:
printf("Hello World! \n");
return 0;
注释(Comments)就行C程序中的帮助文本,编译器会忽略它们。
单行注释//
。如下所示:
// my first program in C
多行注释,以/*
开头并且以*/
结尾。如下所示:
/* my first program in C */
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
以下列表显示了C中的保留字。这些保留字不能用作常量或变量或任何其他标识符名称。
仅包含空格的行(可能带有注释)称为空行,C编译器会完全忽略它。
空格(Whitespace)是C语言中用来描述空格、制表符、换行符和注释的术语。空格将语句的一部分与另一部分分开,并使编译器能够识别语句中的一个元素(比如:int)在哪里结束,而下一个元素在哪里开始。因此,在以下语句中,int 和 age 之间必须至少有一个空白字符(通常是空格),以便编译器能够区分它们。
int age;
另一方面,在下面的声明中:fruit
和=
之间,或者=
和apples
之间不需要空格字符,尽管如果希望出于可读性目的,可以随意包含一些空格字符。
fruit = apples + oranges; // get the total fruit
在C语言中,数据类型(Data Types)是指用于声明不同类型的变量或函数的扩展系统。变量的类型决定了它在存储中占用了多少空间以及如何解释存储的位模式。
C中的数据类型
可以分类如下:
Types | Description |
---|---|
Basic Types(基本类型) | 它们都是算数类型。包含两种类型:整数类型(integer types)和浮点类型(floating-point types) |
Enumerated types(枚举类型) | 它们都是算数类型。它们用于定义在整个程序中只能分配某些离散整数值的变量。 |
The type void(void类型) | 类型说明符void。表示没有可用的值。 |
Derived Types(派生类型) | 它们包括:指针类型(Pointer types)、数组类型(Array types)、结构类型(Structure types)、联合类型(Union types)和函数类型(Function types)。 |
数组类型和结构类型统称为聚合类型。函数类型指的是函数的返回值类型。本节仅介绍Basic Types(基本类型)
和The type void(void类型)
,其它类型后续再讲解。
下表列出了标准整数类型(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)
可以得到对象或存储类型的存储字节大小。
以下是在任何机器上获取上述表中类型大小的示例:
#include
#include
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
下表详细介绍了标准浮点类型(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
中定义了允许我们在程序中使用这些值和有关实数二进制表示的其他详细信息的宏。以下示例将打印浮点类型及其范围值占用的存储空间:
#include
#include
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
void类型指定没有可用的值。它通常用于以下三种情况:
Type | Description |
---|---|
Function returns as void(函数返回值为void) | C中有很多不返回值的函数,没有返回值的函数的返回类型为void。 例如:void exit(int status); |
Function arguments as void(函数参数为void) | C中有很多不接受任何参数的函数,没有任何参数的函数可以接受void类型。 例如:int rand(void); |
Pointers to void(指针指向为void) | void *类型的指针可以表示对象的地址,但不表示其类型。 例如:void *malloc(size_t size); 这会返回指向void的指针,该指针可以转换为任何数据类型。 |
此时如果无法理解 void 类型,我们还会在接下来的章节中介绍这些内容。
汇总后的C数据类型如下: