Linux C 编程(一)

1. 程序基础

1.1. 基本概念

计算机是由数字电路组成的运算机器,只能对数字进行运算,而其能够进行符号运算的原因,是因为符号在计算机内部也是用数字表示的

程序

用来告诉计算机应该如何完成一个计算任务(简言之,就是先做什么,再做什么)

指令

程序由一系列指令组成,指令是指示计算机进行某种运算的命令

语句

计算机指令的抽象表示(比如C、Java这种高级语言,不是使用的助记符或是机器码指令,而是封装成了更加易用的逻辑)

编写程序

编写程序的过程是将复杂的任务分解成子任务,再把子任务进一步分解,不断反复这样的分解,直到简单得可以使用指令完成

1.2. 程序调试

程序的编写是一个复杂的过程,其中时常陪伴着程序员的还有寻找 Bug 的过程,因为程序毕竟是人写的,人总会犯错,因此就需要我们查找并修改这些程序中的错误,也就是所谓 Bug

找到程序当中的这些 Bug 并且纠正的过程便是调试(Debug)

Bug的分类

  • 编译时错误

编译器只能翻译语法正确的程序,如果语法存在错误,编译器就会罢工,此时你需要根据编译器提供的线索,找到问题并加以改正

  • 运行时错误

编译器检查不出这种错误,并且会继续生成可执行文件,但是运行时可能会发生意想不到的错误或者崩溃

  • 逻辑错误和语义错误

这种错误是程序代码的逻辑存在问题,也是在编译期发现不了,但是这种错误会使程序执行错误的功能,比如说明明想要程序执行加法,却写了一个输出“Hello World”的程序

程序的调试是一个“破案”的过程,需要你根据线索进行假设、推理,最终找到问题所在,同时也需要经验的积累,并且会一直伴随程序员成长

1.3. 第一个程序

依照传统,一门编程语言的第一个程序通常都是输出“Hello World!”字符串,我们一起看下用 C 语言该如何写吧:

首先,创建一个普通的文本文件,修改后缀名为.c,表明这是一个 C 语言源代码文件

image-20230623001516951

接下来,编写以下代码:

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

int main(void) {
printf("Hello World\n");
return 0;
}

这便是一个输出“Hello World”字符串的简单程序

1
gcc hello.c

在终端调用gcc命令进行源代码文件的编译,得到可执行文件 a.out

1
./a.out 

执行a.out文件,得到运行结果

image-20230623002639020

#include <stdio.h>用来引入头文件,目的是提供可以调用的函数,比如说下面的printf(),而main()也是一个函数,作为程序的入口,程序从mian()开始执行,{}包裹的部分便是main()的内容,printf()将其中使用""包裹的字符串输出到终端控制台,最后return语句表示程序的终结,返回一个0,表示程序正常结束

2. 常量、变量和表达式

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

/*
* Hello World程序
*/
int main(void) {
// 输出Hello World字符串
printf("Hello World\n");
return 0;
}

使用/*开头并且使用*/结尾跨越多行的内容是程序的注释,其会被编译器忽视,仅仅用于对程序的功能进行说明

使用/**/包裹的是多行注释,期间可以有多行内容,但是不能够进行嵌套,否则会导致注释失效

除了多行注释,还可以使用以//开头的单行注释,这种注释的范围是从//直至当前的行尾

2.1. 常量

常量是程序中最基本的元素,C 语言当中有各种类型的常量,像是字符常量、整数常量、枚举常量等

其中注意,字符常量需要通过''包裹,比如'{'就是一个字符常量,通常都是单个字符,但是如果表示的是转义字符,可能出现\n这种,但其实质还是一个字符

1
printf("字符%c\n整数%d\n浮点数%f\n", '{', 56, 6.78);
image-20230625215444219

以上printf语句输出字符、整数、浮点数常量,其中printf()的第一个参数称为格式化字符串,也就是"字符%c\n整数%d\n浮点数%f\n",它的作用是告诉后面几个常量将要以何种格式“对号入座”

%后面分别添加cdf表示字符型、整型、浮点型的转换说明,转换说明在字符串中占个位置,即占位符运行时会替换为后面的常量,与编译时处理的转义字符有所不同

2.2. 变量

变量在任何编程语言当中都是极其重要的存在

从本质上来说,变量是计算机存储器里的一块命名空间,可以在里面存储一个值,存储的值是可以随时变的

这就好比租房子,你就是那个值,你不住了,别人住,那么值就发生了变化,但是存储空间还是那一块

既然常量有不同的类型,与之对应的,变量自然也会有不同的类型

更深入地讲,变量的类型决定了它所占用存储空间的大小

1
2
3
4
5
int a;  // 整型
double b; // 双精度浮点型
char c; // 字符型
long d; // 长整型
printf("%lu\n%lu\n%lu\n%lu\n", sizeof(a), sizeof(b), sizeof(c), sizeof(d));
image-20230625232323147

C 语言当中的声明可以分为变量声明函数声明类型声明三种

如果变量或者函数声明的同时要求编译器为其分配存储空间,那么也可以称为定义,因此定义是声明的一种

为变量起名是有一定的规则需要遵守的,这也就是所谓标识符规则

在 C 语言中,规定必须以字母下划线开头,后面可以跟上若干个字母、数字、下划线,但是不能掺杂有其他字符,比如:_123、__abc、s1都是合法的标识符

除此以外,还需要注意,像 C 语言当中的关键字或者是保留字是不能够作为标识符的

C语言关键字参考(https://www.runoob.com/cprogramming/c-basic-syntax.html)

一般呢,还要尽可能避免使用以下划线开头的标识符,通常下划线开头的标识符只要不与 C 语言关键字冲突就是合法的,然而,往往有一些下划线开头的标识符会被编译器用于扩展

2.3. 赋值

定义了变量之后,我们往往需要把值存到它们所表示的存储空间里,这便是一个赋值过程

变量一定要先声明,后使用,编译器需要先看到变量声明

1
2
3
int d;   // 声明
d = 0; // 赋值
d += 1; // 使用--自增

当然变量的定义和赋值也可以一步完成,这被称为变量的初始化

1
2
int d = 0;
d += 1;

变量的最小存储单位是字节,在 C 语言中char类型占1字节,其他的类型所占用的字节数在不同平台上有不同的规定

2.4. 表达式

变量和常量都可以参与各种运算,比如:加减乘除,而参与这些运算的常量或者变量就被称为操作数

将常量、变量通过运算组合在一起,可以形成表达式,比如:x + y、a * b - 2

任何表达式都有值和类型两个基本属性

其实这是由于表达式都会通过运算得到结果,而结果会是一个确定的值,因而自然会有值和类型

在 C 语言当中,整数的除法既不是Floor也不是Ceiling,无论正负,一律去掉小数部分

2.5. 字符类型和字符串编码

字符常量或字符型变量也可以当作整数参与运算

这是因为在计算机的内部,这些字符其实也是使用数字表示的,每个字符在计算机内部有一个整数与之对应,而这就是字符编码

目前最常用的是ASCII码(American Standard Code for Information Interchange)

ASCII码的取值范围是0~127,其中包含一些不可见字符和空白字符

‘0’的ASCII码是48,’\0’的ASCII码是0