第02章_变量与进制

本章专题脉络

第2章_变量与进制

1、关键字(keyword)

定义:被C语言赋予了特殊含义,用做专门用途的字符串(或单词)。

特点:全部关键字都是小写字母

举例:HelloWorld案例中,出现的关键字有 intreturn等,这些单词已经被C语言定义好了。

传统的C语言(ANSI C)有32个关键字。如下:

类型 具体关键字
控制语句关键字(12 个) break, case, continue, default, do, else, for, goto, if, return, switch, while
数据类型关键字(12 个) char, enum, double, long, float, int, short, signed, struct, unsigned, union, void
存储类型关键字(4 个) auto, extern, register, static
其他关键字(4 个) const, sizeof, typedef, volatile

后续,1999年,C99标准增加了5个关键字:inlinerestrict_Bool_Complex_Imaginary

2011年,C11标准又增加了7个关键字:_Alignas_Alignof_Atomic_Static_assert_Noreturn_Thread_local_Generic

说明:

1、ANSI C、C99和C11,它们之间差别并不大,在大多数情况下,它们都是和谐共处的。

2、不需要死记硬背,学到哪里记到哪里即可。

2、标识符(Identifier)

C语言中变量、函数、数组名、结构体等要素命名时使用的字符序列,称为标识符。

技巧:凡是自己可以起名字的地方都叫标识符。

标识符的命名规则(必须遵守的硬性规定

  • 只能由26个英文字母大小写,0-9 或 _ 组成
  • 数字不可以开头
  • 不可以是关键字,但可以包含关键字
  • C99和C11允许使用更长的标识符名,但是编译器只识别前63个字符。(会忽略超出的字符)
  • 不允许有空格。
  • 严格区分大小写字母。比如:Hello、hello是不同的标识符。

标识符的命名建议(建议遵守的软性要求

  • 在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。如:sum,name,max,year,total 等。

  • 不要出现仅靠大小写区分不同的标识符。如:name、Name 容易混淆

  • 尽量避免名字中出现数字编号,如value1、value2等,除非逻辑上需要编号。

  • 习惯上,所有宏定义、枚举常数、常量(只读变量)全用大写字母命名,用下划线分隔单词。

    比如: const double TAX_RATE = 0.08; //TAX_RATE 只读变量

  • 系统内部使用了一些下划线开头的标识符(比如两个下划线开头的变量名、一个下划线 + 大写英文字母开头的变量名)。比如,C99 标准添加的类型 _Bool。为防止冲突,建议用户尽量避免使用下划线开头的标识符。

  • 下划线通常用于连接一个比较长的变量名。如:max_classes_per_student。

  • 变量名、函数名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz (驼峰法,小驼峰)。比如:short stuAge = 20; tankShotGame

举例:合法的标识符:

1
a、BOOK1、_sun、MAX_SIZE、Mouse、student23、Football、FOOTBALL、max、_add、num_1、sum_of_numbers

举例:非法的标识符:

1
$zj、3sum、ab#cd、23student、Foot-baii、s.com、b&c、j**p、book-1、tax rate、don't

【武汉科技大学2019研】

以下均是合法变量名的是( )。
A.#name total
B.node  value_max
C._var  long
D.stu-code a+b

【答案】B

【解析】C语言中变量名只能包含数字,字母和下划线,且只能以字母和下划线开始。A项含非法字符#,错误;C中long为关键字,变量不能以关键字命名;D中含非法字符-和+。

【四川大学2017研】以下不合法的用户标识符是(  )。
A.J2_KEY
B.Double
C.4d
D._8_

【答案】C

【解析】标识符只能包含数字,字母,下划线,且不能以数字开头,选项C错误。

练习

下列定义变量的语句中错误的是(  )。
A.double int_;
B.float US$;
C.char For;
D.int _int;

【答案】B【解析】标识符由字母、数字、下划线组成。$是非法字符,不能出现在标识符中。答案选择B选项。

3、变量(variable)

3.1 为什么需要变量

image-20220513235020527

一花一世界,如果把一个程序看做一个世界或一个社会的话,那么变量就是程序世界的花花草草、万事万物。即,变量是程序中不可或缺的组成单位,最基本的存储单元

image-20220513235828042

3.2 初识变量

  • 变量的概念:

    • 内存中的一个存储区域,该区域的数据可以在同一类型范围内不断变化。

    • 通过变量名,可以访问这块内存区域,获取里面存储的值。

    • 变量的构成包含三个要素:数据类型变量名存储的值

    • C语言中变量声明的格式:数据类型 变量名 = 变量值

      image-20230620171948703

  • 变量的作用:用于在内存中保存数据。

  • 使用变量注意:

    • C语言中每个变量必须先声明,后使用。
    • 不同的数据类型,占用的空间大小不一样。
    • 一旦声明,变量的类型就不能在运行时修改。

3.3 变量的声明与赋值

步骤1:变量的声明

格式:

1
数据类型  变量名;  //声明变量的语句必须以分号结尾

举例1:

1
int width;

举例2:

1
2
3
4
5
int width,height;

// 等同于
int width;
int height;

步骤2:变量的赋值

变量声明时,就为它分配内存空间,但是不会清除内存里面原来的值。这导致声明变量以后,变量会是一个随机的值。所以,变量一定要赋值以后才能使用。

1
2
int age; //变量的声明
age = 18; //变量的赋值

变量的声明和赋值,也可以写在一行。

1
int age = 18;

多个相同类型变量的赋值,可以写在同一行。

1
int a = 1, b = 2;
1
2
3
int a, b;
a = 1;
b = (a = 2 * a);
1
2
int a, b, c, x, y;
a = b = c = x = y = 10; //连续赋值

注意:声明变量以后,不用忘记初始化赋值!定义变量时编译器并不一定清空了这块内存,它的值可能是无效的数据,运行程序,会异常退出。

3.4 变量的作用域(scope)

  • 变量的作用域:其定义所在的一对{ }内。

  • 变量只有在其作用域内才有效。出了作用域,变量不可以再被调用。

  • 同一个作用域内,不能定义重名的变量。

  • C 语言的变量作用域主要有两种:文件作用域(file scope)和块作用域(block scope)。

文件作用域(file scope)指的是,在源码文件顶层声明的变量,从声明的位置到文件结束都有效。

1
2
3
4
5
int x = 1;
int main() {
printf("%d\n", x);
return 0;
}

块作用域(block scope)指的是由大括号( {} )组成的代码块,它形成一个单独的作用域。凡是在块作用域里面声明的变量,只在当前代码块有效,代码块外部不可见。

1
2
3
4
5
6
7
8
9
10
11
int main() {
int m = 10;
if (m == 10) {
int n = 20;
printf("%d %d\n", m, n); // 10 20
}
printf("%d\n", m); // 10
printf("%d\n", n); // 超出作用域,报错

return 0;
}

最常见的块作用域就是函数,函数内部声明的变量,对于函数外部是不可见的。 for 循环也是一个块作用域,循环变量只对循环体内部可见,外部是不可见的。

1
2
3
4
for (int i = 0; i < 10; i++){
printf("%d\n", i);
}
printf("%d\n", i); // 超出作用域,报错

3.5 变量按类型的分类

变量可以按数据类型来分,也可以按声明的位置来分(全局变量、局部变量)。本节主讲变量的不同类型。

C 语言中的变量按照数据类型分为:

image-20230810152811098

注意1:这里列举的是C语言的常用类型,后续C语言版本还有新增的类型。

注意2:空类型:void 表示空类型(无类型)。通常应用于函数的返回值类型、函数的参数、指针类型。

注意3:在C语言中,没有字符串类型,使用字符数组表示字符串。

4、基本数据类型

4.1 整数类型

4.1.1 类型说明

  • C语言规定了如下的几类整型:短整型(short)、整型(int)、长整型(long)、更长的整型(long long)

  • 每种类型都可以被 signed 和unsigned 修饰。其中,

    • 使用 signed 修饰,表示该类型的变量是带符号位的,有正负号,可以表示负值。默认是signed
    • 使用 unsigned 修饰,表示该类型的变量是不带符号位的,没有有正负号,只能表示零和正整数。
  • bit(位):计算机中的最小存储单位。

    byte(字节):计算机中基本存储单元。

    1byte = 8bit

类型 修饰符 占用空间 取值范围
short [int] signed 2个字节(=16位) -32768 ~ 32767 (-$2^{15}$ ~ $2^{15}$-1)
short [int] unsigned 2个字节(=16位) 0 ~ 65535 (0 ~ $2^{16}$-1)
int signed 通常4个字节 -2147483648 ~ 2147483647 (-$2^{31}$ ~ $2^{31}$-1)
int unsigned 通常4个字节 0 ~ 4294967295 (0 ~ $2^{32}$-1)
long [int] signed 4个或8个字节 4字节时:-2147483648 ~ 2147483647 (-$2^{31}$ ~ $2^{31}$-1)
long [int] unsigned 4个或8个字节 4字节时:-0 ~ 4294967295 (0 ~ $2^{32}$-1)

long long int是C99新增的:

类型 修饰符 占用空间 取值范围
long long [int] signed 8个字节(=64位) -9223372036854775808~ 9223372036854775807(-$2^{63}$ ~ $2^{63}$-1)
long long [int] unsigned 8个字节(=64位) 0 ~ 18446744073709551615(0 ~ $2^{64}$-1)

说明1:不同计算机的 int 类型的大小是不一样的。比较常见的是使用4个字节(32位)存储一个 int 类型的值,具体情况如下:

类型 16位编译器 32位编译器 64位编译器
short int 2字节 2字节 2字节
int 2字节 4字节 4字节
unsigned int 2字节 4字节 4字节
long 4字节 4字节 8字节
unsigned long 4字节 4字节 8字节
long long 8字节 8字节 8字节

说明2:C标准虽然没有具体规定各种类型数据所占用存储单元的长度,但几条铁定的原则(ANSI/ISO制订的):
① sizeof(short int) ≤ sizeof(int) ≤ sizeof(long int) ≤ sizeof(long long),具体由各编译系统自行决定的。其中,sizeof是测量类型或变量长度的运算符。

② short int至少应为2字节,long int至少应为4字节。

这样约定的好处就是使得C语言可以长久使用。现在的主流CPU是64位,可以预测不久的将来会推出128位甚至256位的CPU,但是在C语言刚刚出现的时候,CPU还是以8位和16位为主。如果那时候就将整型定死为8位或16位,那么现在我们肯定不会再学习C语言了。

说明3:

最常用的整型类型为:int类型。

整数型常量,默认为int类型。

4.1.2 举例

举例1:对于 int 类型,默认是带有正负号的。即 int 等同于 signed int 。一般情况下,关键字signed省略不写。

1
2
3
signed int m;  //声明了一个带符号的整数变量 m 
// 等同于
int m; //声明了一个带符号的整数变量 m

举例2:int 类型也可以不带正负号,只表示非负整数。这时就必须使用关键字 unsigned 声明变量。表数范围为:0~4294967295

1
unsigned int a;   //声明了一个不带符号的整数变量a,表数范围为:0~4294967295

unsigned int 里面的 int 可以省略,所以上面的变量声明也可以写成这样:

1
unsigned a;

举例3:

int 类型使用4个字节表示一个整数,对于小整数,这样做很浪费空间。另一方面,某些场合需要更大的整数,8个字节还不够。此时,可以使用short int (简写为 short )、long int (简写为 long )、long long int (简写为 long long )

1
2
3
signed short int a; 
signed long int b;
signed long long int c;

默认情况下, short 、 long 、 long long 都是带符号的(signed),即 signed 关键字可以省略。代码简写为:

1
2
3
short a; 
long b;
long long c;

它们也可以声明为不带符号(unsigned),使得能够表示的最大值扩大一倍。

1
2
3
unsigned short a;  //无符号短整型,表数范围:0~65535
unsigned long b; //无符号长整型,表数范围:0~4294967295
unsigned long long c; //无符号长整型,表数范围:0~18446744073709551615

4.1.3 关于后缀

编译器将一个整数字面量指定为 int 类型,但是如果希望将其指定为 long 类型,需要在该字面量末尾加上后缀 lL ,编译器会把这个字面量的类型指定为 long 。

1
long x = 123L; //或者写成 123l

如果希望字面量指定为long long类型,则后缀以llLL结尾。

1
long long y = 123LL;

如果希望指定为无符号整数 unsigned int ,可以使用后缀 uU

1
unsigned int x = 123U;

L 和 U 可以结合使用,表示 unsigned long 类型。 L 和 U 的大小写和组合顺序无所谓。

u 还可以与其他整数后缀结合,放在前面或后面都可以,比如 10UL 、 10ULL 和 10LLU 都是合法的。

1
2
unsigned long int      x = 1234UL;
unsigned long long int x = 1234ULL;

4.1.4 精确宽度类型(了解)

C 语言的整数类型(short、int、long)在不同计算机上,占用的字节宽度可能是不一样的,无法提前知道它们到底占用多少个字节。程序员有时控制准确的字节宽度,这样的话,代码可以有更好的可移植性,头文件 stdint.h 创造了一些新的类型别名。

**精确宽度类型(exact-width integer type)**:保证某个整数类型的宽度是确定的。

  • int8_t :8位有符号整数

  • int16_t :16位有符号整数

  • int32_t :32位有符号整数

  • int64_t :64位有符号整数

  • uint8_t :8位无符号整数

  • uint16_t :16位无符号整数

  • uint32_t :32位无符号整数

  • uint64_t :64位无符号整数

上面这些都是类型别名,编译器会指定它们指向的底层类型。比如,某个系统中,如果 int 类型为32位, int32_t 就会指向 int ;如果 long 类型为32位, int32_t 则会指向 long 。

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

int main() {
int32_t x32 = 45933945; //变量 x32 声明为 int32_t 类型,可以保证是32位的宽度。
printf("x32 = %d\n", x32);
return 0;
}

4.1.5 整型的极限值(了解)

有时候需要查看,当前系统不同整数类型的最大值和最小值,C 语言的头文件 limits.h 提供了相应的常量。比如:INT_MIN 代表 signed int 类型的最小值 -2147483648, INT_MAX 代表 signed int 类型的最大值 2147483647。

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

int main() {
printf("%d\n", INT_MIN ); // -2147483648
printf("%d\n", INT_MAX ); // 2147483647
return 0;
}

为了代码的可移植性,需要知道某种整数类型的极限值时,应该尽量使用这些常量。

  • SCHAR_MIN , SCHAR_MAX :signed char 的最小值和最大值。

  • SHRT_MIN , SHRT_MAX :short 的最小值和最大值。

  • INT_MIN , INT_MAX :int 的最小值和最大值。

  • LONG_MIN , LONG_MAX :long 的最小值和最大值。

  • LLONG_MIN , LLONG_MAX :long long 的最小值和最大值。

  • UCHAR_MAX :unsigned char 的最大值。

  • USHRT_MAX :unsigned short 的最大值。

  • UINT_MAX :unsigned int 的最大值。

  • ULONG_MAX :unsigned long 的最大值。

  • ULLONG_MAX :unsigned long long 的最大值。

4.2 浮点类型

4.2.1 类型说明

浮点型变量,也称为实型变量,用来存储小数数值的。因为32位浮点数提供的精度或者数值范围还不够,C 语言又提供了另外两种更大的浮点数类型。

在C语言中,浮点型变量分为三种:单精度浮点型(float)、双精度浮点型(double)、长双精度浮点型(long double)。

类型 占用空间 取值范围
float 4个字节 (=32位) $-1.410^{-45}$ ~ $-3.410^{+38}$,$1.410^{-45}$ ~ $3.410^{+38}$
double 8个字节 (=64位) $-4.910^{-324}$ ~ $-1.710^{+308}$,$4.910^{-324}$ ~ $1.710^{+308}$
long double 12个字节(=96位) 太大了…

其中,

类型 16位编译器 32位编译器 64位编译器
float 4字节 4字节 4字节
double 8字节 8字节 8字节

C语言的第3种浮点类型是long double,以满足比double类型更高的精度要求。不过,C只保证long double类型至少与double类型的精度相同。

浮点型变量不能使用signed或unsigned修饰符。

最常用的浮点类型为:double 类型,因为精度比float高。

浮点型常量,默认为 double 类型。

关于后缀:

对于浮点数,编译器默认指定为 double 类型,如果希望指定为float类型,需要在小数后面添加后缀 fF;如果希望指定为long double类型,需要在小数后面添加后缀 lL

1
2
3
float x       = 3.14f;
double x      = 3.14;
long double x = 3.14L;

4.2.2 举例

举例1:

1
2
3
float f = 123.4f;  //后面必须加上字母f
double d1 = 101.1; //后面可以省略字母d
double d2 = 299.4;  //后面可以加上字母d

举例2:

C 语言允许使用科学计数法表示浮点数,使用字母 e 来分隔小数部分和指数部分。注意,e 的前后,不能存在空格。

1
2
3
double x = 123.456e+3; // 123.456 x 10^3
// 等同于
double x = 123.456e3;

另外,科学计数法的小数部分如果是 0.x 或 x.0 的形式,那么 0 可以省略。

1
2
3
4
5
6
7
0.3E6
// 等同于
.3E6

3.0E6
// 等同于
3.E6

举例3:可以在常量的末尾加专用字符,强制指定常量的类型

1
2
float a = 3.14159f; //把此3.14159按单精度浮点常量处理
long double a = 1.23L; //把此1.23作为long double型处理

举例4:

有人用温度计测量出用华氏法表示的温度(如64°F),今要求把它转换为以摄氏法表示的温度(如17.8℃)。转换的公式为:$c = \frac{5}{9}(f - 32)$。

其中,f 代表华氏温度,c 代表摄氏温度。

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

int main() {
float f, c; //定义f和c分别表示华氏温度、摄氏温度
f = 64.0; //指定f的值
c = (5.0 / 9) * (f - 32); //利用公式计算c的值
printf("f=%f\nc=%f\n", f, c); //输出c的值
return 0;
}

4.2.3 存储规则(了解)

任何有小数点的数值,都会被编译器解释为浮点数。所谓“浮点数”就是使用 m * b^e 的形式,存储一个数值, m 是小数部分, b 是基数, e 是指数部分。

从十进制的角度:

1688554845769

从二进制的角度:

根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:$(-1)^S * M * 2^E$

其中:

  • $(-1)^s$表示符号位,当s=0,V为正数;当s=1,V为负数。

  • M表示有效数字,大于等于1,小于2。

  • $2^E$表示指数位。

举例来说:

十进制的5.0,写成二进制是 101.0 ,相当于 1.01×2^2 。即,按照上面V的格式,可以得出s=0,M=1.01,E=2。

十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。即,s=1,M=1.01,E=2。

IEEE 754规定:

对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。

1688626607436

对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

1688626635111

浮点数的存储方式,决定了浮点数精度控制在一定范围内。有效数字部分可能丢失,造成精度损失。

4.3 字符类型

C语言中,使用 char 关键字来表示字符型,用于存储一个单一字符

字符型变量赋值时,需要用一对英文半角格式的单引号('')把字符括起来。

每个字符变量,在16位、32位或64位编译器中都是占用 1 个字节(=8位)

表示方式1:最常见

1
char c = 'A'; //为一个char类型的变量赋值字符'A'

每个字符对应一个整数(由 ASCII 码确定),比如 A 对应整数 65 。

只要在字符类型的范围之内,整数与字符是可以互换的,都可以赋值给字符类型的变量。

表示方式2:ASCII 码值

1
2
3
char c = 66;
// 等同于
char c = 'B';

两个字符类型的变量可以进行数学运算。

1
2
3
char a = 'B'; // 等同于 char a = 66;
char b = 'C'; // 等同于 char b = 67;
printf("%d\n", a + b); // 输出133

常见的ASCII值与对应的字符如下:(ASCII数值范围为0-127)

image-20220513095907601

ASCII码:上个世纪60年代,美国制定了一套字符编码,对英语字符二进制位之间的关系,做了统一规定。这被称为ASCII码。ASCII码一共规定了127个字符的编码,比如空格“SPACE”是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0,也就是说,ASCII虽然用8位二进制编码表示字符,但是其有效位为7位。

举例1:字符′1′和整数1是不同的概念。(参看ASCII码表)

1
2
3
4
char c1 = 1;
char c2 = '1';
printf("c1 = %d\n",c1); // c1 = 1
printf("c2 = %d\n",c2); // c2 = 49

举例2:

1
2
char c='?'; //定义c为字符型变量并使初值为字符'?'。'?'的ASCII代码是63,系统把整数63赋给变量c。
printf("%d %c\n",c,c); //用"%d"格式输出十进制整数63,用"%c"格式输出字符'?'

signed 和 unsigned 修饰:

根据C90标准,C语言允许在关键字char前面使用signed或unsigned。

1
2
signed char c; // 范围为 -128 到 127
unsigned char c; // 范围为 0 到 255

注意,C 语言规定 char 类型默认是否带有正负号,由当前系统决定,这一点与 int 不同, int 等同于 signed int 。这就是说, char 不等同于signed char ,它有可能是 signed char(范围-128 到 127) ,也有可能是 unsigned char (范围0 到255)。不管是哪种,范围都正好都能覆盖 0 到 127 的 ASCII 字符范围。

表示方式3:使用转义字符

单引号本身也是一个字符,如果要表示这个字符常量,必须使用反斜杠转义。

1
char t = '\'';

char还可以用来表示转义字符。比如:

字符形式 含义
\n 换行符(光标移动到下行行首)
\t 水平制表符,光标移到下一个Tab位置
\' 单引号字符 ‘
\" 双引号字符 “
\\ 反斜杠字符 ’\’
\r 回车符,光标移到本行开头
\0 null 字符,代表没有内容。注意,这个值不等于数字0。
\b 退格键,光标回退一个字符,但不删除字符

4.4 布尔类型

C语言标准(C89)没有为布尔值单独设置一个类型,所以在判断真假时,使用整数 0 表示假,所有非0表示真。比如:

1
2
3
4
5
6
7
8
9
int main(){
int handsome = 1;

if (handsome) {
printf("我好帅!\n");
}

return 0;
}

上述做法不直观,可以借助于C语言的宏定义处理。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义布尔类型的宏
#define BOOL int //可以使用 typedef int BOOL; 替换
#define TRUE 1
#define FALSE 0


int main(){
BOOL handsome = TRUE;

if(handsome){
printf("好帅~");
}

return 0;
}

此外,C99 标准添加了类型 _Bool,表示布尔值,即逻辑值true和false。但是,这个类型的值其实只是整数类型的别名,还是使用 0 表示false, 1 表示true,其它非0的值都会被存储为1。所以_Bool类型实际上也是一种整数类型。

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

int main() {
_Bool isFlag = 1;
if (isFlag)
printf("你好毒~~\n");
return 0;
}

与此同时,C99还提供了一个头文件 stdbool.h,文件中定义了bool代表_Bool,并且定义了 true 代表 1 、 false 代表 0 。只要加载这个头文件,就可以使用 bool 定义布尔值类型,以及 false 和 true 表示真假。

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

int main() {
bool isFlag = true;
if (isFlag)
printf("你好毒~~\n");
return 0;
}

【武汉科技大学2019研】

以下选项中不属于C语言类型的是(  )。
A.short int
B.unsigned long int
C.char
D.bool

【答案】D

【解析】C语言中没有bool型,只有C++才有boolean型,也称bool。C语言中一般用“0”表示“假”,用“1”表示“真”。

【四川大学2017研】有4个圆塔,圆心分别为(2,2)、(-2,2)、(-2,-2)、(2,-2),圆半径为1。这4个塔的高度为10m,塔以外无建筑物。今输入任一点的坐标,求该点的建筑高度(塔外的高度为零)。

【答案】
N-S图如图1所示。

1691981968969

​ 图1 计算某点建筑高度的N-S流程图
程序如下:

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

int main() {

int h = 10;
float x1 = 2, y1 = 2, x2 = -2, y2 = -2, x3 = -2, y3 = -2, x4 = 2, y4 = -2;
float x, y; //表示随意选中的一个点的坐标
float d1, d2, d3, d4; //(x,y)这个点的坐标到各个圆心的距离
printf("请输入一个点(x,y):");
scanf("%f,%f", &x, &y);
d1 = (x - x1) * (x - x1) + (y - y1) * (y - y1); //求该点到各中心点距离
d2 = (x - x2) * (x - x2) + (y - y2) * (y - y2);
d3 = (x - x3) * (x - x3) + (y - y3) * (y - y3);
d4 = (x - x4) * (x - x4) + (y - y4) * (y - y4);
if (d1 > 1 && d2 > 1 && d3 > 1 && d4 > 1) //判断该点是否在塔外
h = 0;
printf("该点高度为%d\n", h);
return 0;
}

5、变量间的运算规则

在C语言编程中,经常需要对不同类型的数据进行运算,运算前需要先转换为同一类型,再运算。为了解决数据类型不一致的问题,需要对数据的类型进行转换。

5.1 隐式类型转换

情况1:窄类型自动转为宽类型

即,系统自动将字节宽度较小的类型转换为字节宽度较大的数据类型,它是由系统自动转换完成的。

image-20220523162200891

基本数据类型的转换规则如图所示:

image-20230824194145734

注意:最好避免无符号整数与有符号整数的混合运算。因为这时 C 语言会自动将 signed int 转为unsigned int ,可能不会得到预期的结果。

举例1:

  • 不同的整数类型混合运算时,宽度较小的类型会提升为宽度较大的类型。比如 short 转为 int ,int 转为 long 等。
  • 不同的浮点数类型混合运算时,宽度较小的类型转为宽度较大的类型,比如 float 转为double , double 转为 long double 。
1
float y = 12 * 2; //整数赋值给浮点数变量时,会自动转为浮点数。结果24.0
1
2
3
4
//char类型 与 int类型运算,会自动提升为 int 。
char c = 10;
int i = 10;
int j = c + i; //ok
1
2
3
short s1 = 10;
int num1 = s1; //ok
double num2 = s1; //ok
1
2
3
int i = 10;
double d1 = 12.3;
double d2 = i + d1; //系统自动将i的类型由int转换为double类型,故i+d1结果为double类型
1
2
double d;
d = 2 + 'A' + 1.5F;

举例2:

两个相同类型的整数运算时,或者单个整数的运算,一般来说,运算结果也属于同一类型。但是有例外,宽度小于 int 的类型,运算结果会自动提升为 int 。

1
2
3
char c1 = 10;
short s1 = 10;
int i1 = c1 + s1; //char类型和short类型的变量运算的结果默认为int类型
1
2
3
4
5
6
7
8
9
unsigned char a = 1;
unsigned char b = 255;
unsigned char c = 255;

if ((a - 5) < 0)
do_something();

if ((b + c) > 300)
do_something();

说明:表达式 a - 5 和 b + c 都会自动转为 int 类型,所以函数 do_something() 会执行两次。

情况2:宽类型赋值给窄类型

字节宽度较大的类型,赋值给字节宽度较小的变量时,会发生类型降级,自动转为后者的类型。这时可能会发生截值(truncation),系统会自动截去多余的数据位,导致精度损失。

这反映了C语言在检查类型匹配方面不太严格。最好不要养成这样的习惯。

举例1:

1
2
double pi = 3.14159;
int i = pi; // i 的值为 3

C编译器把浮点数转换成整数时,会直接丢弃(截断)小数部分,而不进行四舍五入。

举例2:

1
int x = 3.14; //浮点数赋予整数变量时,C 语言直接丢弃小数部分。结果 3
1
2
int cost = 12.99;         	 // double类型的值转为int类型,结果为:12
float pi = 3.1415926536; // double类型的值转为float类型,结果为:3.141593

举例3:

1
2
int i = 322;
char ch = i; // ch 的值是 66

图示:

image-20230626213244132 image-20230626213317271

举例4:

1
2
3
4
5
6
float f1 = 1.1f; //ok
double d2 = 4.58667435;
f1 = d2; // 出现精度损失 (double -> float )

printf("f1=%.8f", f1); // 期望: 4.58667435

由于存在精度限制,浮点数只是一个近似值,它的计算是不精确的。

举例5:

1
float a = 3.14159; //3.14159为双精度浮点常量,分配8个字节;a为float变量,分配4个字节

编译时系统会发出警告(warning: truncation from ′const double′ to′float′),提醒用户注意这种转换可能损失精度。

5.2 强制类型转换

隐式类型转换中的宽类型赋值给窄类型,编译器是会产生警告的,提示程序存在潜在的隐患。如果非常明确地希望转换数据类型,就需要用到强制(或显式)类型转换

形式: (类型名称)(变量、常量或表达式)

功能:将“变量、常量或表达式”的运算结果强制转换为“类型名称”所表示的数据类型。

注意:强制类型转换会导致精度损失。

举例:

1
2
3
double x = 12.3;
int y = 10;
int z = (int)x + y; //将变量x的值转换成int后,再与y相加

将浮点数转换为整数时,将舍弃浮点数的小数部分,只保留整数部分。

1
2
3
4
float f1,f2;
f1 = (int)1.2 + 3.4;
f2 = (int)(1.2 + 3.4);
printf("f1=%f,f2=%f",f1,f2);

输出结果:f1=4.4,f2=4.0。

举例2:

1
2
3
int i = 40000;
short s = (short)i;
printf("%d\n",s); //-25536

举例3:

1
long y = (long) 10 + 12; // (long) 将 10 显式转为 long 类型。这里的显示转换其实是不必要的,因为可以自动转换

5.3 运算的溢出问题

每一种数据类型都有数值范围,如果存放的数值超出了这个范围(小于最小值或大于最大值),需要更多的二进制位存储,就会发生溢出。大于最大值,叫做向上溢出(overflow);小于最小值,叫做向下溢出(underflow)

一般来说,编译器不会对溢出报错,会正常执行代码,但是会忽略多出来的二进制位,只保留剩下的位,这样往往会得到意想不到的结果。所以,应该避免溢出。

举例1:

1
2
3
unsigned char x = 255;
x = x + 1;
printf("%d\n", x); // 0

x 是 unsign char 类型,最大值是255 (二进制 11111111 ),加 1 后就发生了溢出, 256 (二进制 100000000 )的最高位 1 被丢弃,剩下的值就是 0 。

举例2:

1
2
3
4
5
unsigned int ui = UINT_MAX;  // 4,294,967,295
ui++;
printf("ui = %u\n", ui); // 0
ui--;
printf("ui = %u\n", ui); // 4,294,967,295

常量 UINT_MAX 是 unsigned int 类型的最大值。如果加 1 ,对于该类型就会溢出,从而得到 0 ;而 0 是该类型的最小值,再减 1 ,又会得到 UINT_MAX 。

溢出很容易被忽视,编译器又不会报错,所以必须非常小心。

6、常量

6.1 常量分类

程序运行时,其值不能改变的量,即为常量

C语言中的常量分为以下以下几种:

  • 字面常量
  • #define 定义的标识符常量
  • const 修饰的常变量
  • 枚举常量

举例:字面常量

1、2、12是整型常量,2.1、12.5、3.14是实型常量,’a’、 ‘b’、’c’是字符型常量。

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

int main(){
//字面常量
3.14;//字面常量
1000;//字面常量
}

6.2 多种方式定义常量

6.2.1 使用#define

这种方式是在文件开头用 #define 来定义常量,也叫作“宏定义”。所谓宏定义,就是用一个标识符来表示一个常量值,如果在后面的代码中出现了该标识符,那么编译时就全部替换成指定的常量值。即用宏体替换所有宏名,简称宏替换

定义格式:#define 符号常量名 常量值

  • 符号常量名,称为宏体,属于标识符,一般定义时用大写字母表示。
  • 常量值,称为宏名,可以是数值常量,也可以是字符常量。

习惯上,宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。

举例1:

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

#define ZERO 0 //#define的标识符常量

int main() {
printf("zero = %d\n", ZERO);
return 0;
}

跟#include一样,“#”开头的语句都是“预处理语句”,在编译之前,预处理器会查找程序中所有的“ZERO”,并把它替换成0,这个过程称为预编译处理。

然后将预处理的结果和源程序一起再进行通常的编译处理,以得到目标代码 (OBJ文件)。

举例2:

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

#define PI = 3.14 // 定义常量 PI,常量值 3.14。因为宏定义不是 C 语句,后面不能有分号

int main() {
//PI = 3.1415 可以吗? => 不可以
double area;
double r = 1.2;
area = PI * r * r;
printf("面积 : %.2f", area);
getchar();
return 0;
}

举例3:

1
2
3
4
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2

#define 对于考研数据结构来说没有什么贡献,我们只要认得它就行。

例如1,#define MAX_Size 50这句,即定义了常量MAX_Size(此时x = 50;等价于x = MAX_Size;)。

例如2,你要定义一个数组,如int A[MAX_Size];,加上一句注释“/*MAX_Size为已经定义的常量,其值为50*/”即可。

6.2.2 使用const限定符

C99中新的声明方式,这种方式跟定义一个变量是一样的,只需要在变量的数据类型前再加上一个const关键字,这被称为“限定符”。

格式:

1
const 数据类型 常量名 = 常量值;

举例:

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

int main(){
//const 修饰的常变量
const float PI = 3.14f;
//PI = 5.14;//是不能直接修改的!

return 0;
}

const修饰的对象一旦创建就不能改变,所以必须初始化。

跟使用 #define定义宏常量相比,const定义的常量有详细的数据类型,而且会在编译阶段进行安全检查,在运行时才完成替换,所以会更加安全和方便。

6.3.3 定义枚举常量

举例:

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

//使用enum定义枚举类
enum Sex{
//括号中的MALE,FEMALE,SECRET是枚举常量
MALE,
FEMALE,
SECRET
};

int main(){
//枚举常量
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET);
//注:枚举常量默认是从0开始,依次向下递增1的
return 0;
}

【北京航空航天大学2018研】若已知有如下宏定义

1
#define  CANBERRA(x,y)  ((x-y)/(x+y))

则以下表达式中,返回结果值最大的是(  )。
A.CANBERRA(3.0,2.0);
B.CANBERRA(4.0,1.0);
C.CANBERRA(1.0+2.0,0.0+2.0);
D.CANBERRA(1.0+2.0,1.0+1.0);

【答案】C

【解析】A项中为1.0/5.0,结果为0.2;B项中为3.0/5.0,结果为0.6;C项中的宏替换后为(1.0+2.0-0.0+2.0)/(1.0+2.0+0+2.0)=1.0;D项中宏替换后为(1.0+2.0-1.0+1.0)/(1.0+2.0+1.0+1.0)=0.6,因此最后答案为C。

【中央财经大学2018研】若有如下宏定义:

1
2
#define  N  2
#define y(n) ((N+1)*n)

则执行下列语句:z=4*(N+y(5));后的结果是(  )。
A.语句有错误
B.z值为68
C.z值为60
D.z值为180

【答案】B

【解析】y(5)=15,z=4*(N+y(5))=4*17=68,答案选B。

7、输入/输出函数

所谓输入输出是以计算机主机为主体而言的。

  • 输出:从计算机向外部输出设备(显示器、打印机)输出数据。
  • 输入:从输入设备(键盘、鼠标、扫描仪)向计算机输入数据。
image-20230824232336985

c语言本身没有提供专门的输入输出语句,所有的输入输出都是由调用标准库函数中的输入输出函数来实现的。

输入函数:scanf() 、 getchar()、gets():

  • scanf(),是格式输入函数,可接收任意类型的数据。
  • getchar(),是字符输入函数, 只能接收单个字符
  • gets(),是字符串输入函数。

输出函数:printf() 、 putchar()、puts():

  • printf(),是格式输出函数,可按指定的格式显示任意类型的数据。
  • putchar(),字符显示函数,只能显示单个字符
  • puts(),是字符串输出函数。

7.1 scanf()的使用

scanf()函数的作用:把从键盘上输入的数据根据找到的地址存入内存中,即给变量赋值。

格式: scanf("格式控制字符串",参数地址列表);

  • “格式控制字符串”:约定输入数据的类型和格式,参数的个数必须与变量地址的个数一致。
  • “参数地址列表”:以逗号 “, ”分隔的、输入数据变量地址序列。

举例:

1
scanf("%d%d%d",&a,&b,&c)

其中,&a,&b,&c中的&是寻址操作符,&a表示对象a在内存中的地址。

注意,

  • 如果scanf中%d是连着写的,如“%d%d%d”,在输入数据时,数据之间不可以用逗号分隔,只能用空白字符(空格或tab键或者回车键)分隔。即“2(空格)3(tab)4” 或 “2(tab)3(回车)4”等。
  • 如果是“%d,%d,%d”,则在输入数据时需要加“,”,如“2,3,4”。

举例1:计算圆的面积,其半径由用户指定

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

int main() {
float radius, area;
printf("请输入半径值: ");
scanf("%f", &radius); //输入半径
area = 3.14 * radius * radius;
printf("area=%f\n", area); //输出圆的面积

return 0;
}

注意:变量名之前要加上&运算符,表示取变量的地址,如“&a,&b”。否则将会出现错误。

举例2:输入一个整数,求其绝对值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int main() {

int num;
printf("输入一个整数:");

scanf("%d", &num);
int absNum = num;
if(absNum < 0)
absNum = -absNum;
printf("\n 整数:%d--->绝对值为:%d\n", num, absNum);

return 0;
}

举例3:输入多个变量的值,求乘积

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

int main() {
int a,b,c;
printf("请输入整数a,b:");
scanf("%d%d",&a,&b);
c=a*b;
printf("%d*%d=%d\n",a,b,c);

return 0;
}

【武汉科技大学2019研】若有声明语句:int x; char y[20]; double z;则正确的输入语句是( )。
A.scanf(”%d%c%le\n”,&x,&y,&z);
B.scanf(”%2d%s%lf”,&x,&y,&z);
C.scanf(”%d%s%lf”,&x,y,&z);
D.scanf(”%x%s%3.2f”,&x,y,&z);

【答案】C

【解析】y为一维数组名,指向数组首元素的地址,因此不需要再使用取地址运算符&,AB错误;D中%3.2f表示长度为3,小数为2位,但是小数点也占一位,因此D错误,答案选C。

7.2 getchar()与putchar()的使用

  • getchar():输入字符数据

    • 格式:getchar()
    • 功能:从键盘缓冲区读入一个字符
  • putchar():输出字符

    • 格式: putchar(ch),其中ch是一个字符变量
    • 功能:从标准输出设备输出一个字符

举例:

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

int main() {
char c = 0;
putchar('A'); //输出单个字符A
putchar(c); //输出变量c的ASCII对应字符
putchar('\n'); //执行换行效果,屏幕不显示
}
1
2
3
4
5
6
7
8
#include <stdio.h>

int main() {
char ch;
ch = getchar();
putchar(ch);
return 0;
}

7.3 gets()与puts()的使用(超纲)

puts():

在C语言中,puts() 是一个用于输出字符串的标准库函数,其原型定义在 <stdio.h> 头文件中。puts() 函数的作用是将一个以 null 字符(\0)结尾的字符串打印到标准输出(通常是控制台)上,并自动添加一个换行符。

1
2
3
4
5
6
7
8
9
10
11
12
int main() {

char str1[]={"China\nBeijing"};
char str2[] = "helloworld";

puts(str1);

puts(str2);

return 0;
}

注意,puts()函数只能用于输出字符串,而不能输出其他类型的数据。如果需要输出其他类型的数据,应使用 printf() 函数。

gets():

读取标准输入设备输入的字符串,直到遇到【Enter】键才结束。

1
2
char str[20];   //定义一个数组
gets(str); //获取输入的字符串,存放到字符数组中

举例:字符串的读写

1
2
3
4
5
6
7
8
9
10
int main() {

char str[15];
printf("enter your name:");
gets(str); //输入字符串至数组变量str
printf("your name is ");
puts(str); //输出字符串

return 0;
}

8、变量按声明位置的分类(后面讲)

变量按照声明的位置,可以分为:局部变量 和 全局变量。

  • 局部变量

    • 函数体内定义的变量,也称内部变量。局部变量只能在定义它的函数中使用。
  • 全局变量

    • 函数之外定义的变量称为外部变量,外部变量是全局变量(也称全程变量)。

    • 一个程序中,凡是在全局变量之后定义的函数,都可以使用在其之前定义的全局变量。

举例:

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

int global = 2023;//全局变量
int main(){
   int local = 2022;//局部变量

   //下面定义的global会不会报错?
   int global = 2024;//局部变量
   printf("global = %d\n", global);
   return 0;
}

当局部变量和全局变量同名的时候,局部变量优先使用。

9、常见的进制

9.1 二进制概述

计算机底层如何存储数据呢?

计算机世界中只有二进制,所以计算机中存储和运算的所有数据都要转为二进制。包括数字、字符、图片、声音、视频等。

010101

世界上有10种人 ,认识和不认识二进制的。

二进制的由来

二进制,是计算技术中广泛采用的一种数制,由德国数理哲学大师莱布尼茨于1679年发明。

二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”。

二进制的应用

二进制广泛应用于我们生活的方方面面。比如,广泛使用的摩尔斯电码(Morse Code),它由两种基本信号组成:短促的点信号“·”,读“”;保持一定时间的长信号“”,读“”。然后,组成了26个字母,从而拼写出相应的单词。

image-20220520105721126

我们偶尔会看到的:SOS,即为:

image-20220520110206899

9.2 进制的分类

  • 十进制(decimal)

    • 数字组成:0-9
    • 进位规则:满十进一
    • C 语言的整数默认都是十进制数
  • 二进制(binary)

    • 数字组成:0-1
    • 进位规则:满二进一,以0b0B开头
    1
    int x = 0b101010;
  • 八进制(octal):很少使用

    • 数字组成:0-7
    • 进位规则:满八进一,以数字0开头表示
    1
    2
    int a = 012; // 八进制,相当于十进制的10
    int b = 017; // 八进制,相当于十进制的15
  • 十六进制

    • 数字组成:0-9,a-f
    • 进位规则:满十六进一,以0x0X开头表示。此处的 a-f 不区分大小写
    1
    2
    int a = 0x1A2B; // 十六进制,相当于十进制的6699
    int b = 0X10; // 十六进制,相当于十进制的16

9.3 进制的换算举例

十进制 二进制 八进制 十六进制
0 0 0 0
1 1 1 1
2 10 2 2
3 11 3 3
4 100 4 4
5 101 5 5
6 110 6 6
7 111 7 7
8 1000 10 8
9 1001 11 9
10 1010 12 a或A
11 1011 13 b或B
12 1100 14 c或C
13 1101 15 d或D
14 1110 16 e或E
15 1111 17 f或F
16 10000 20 10

9.4 输出格式

不同的进制只是整数的书写方法不同,不会对整数的实际存储方式产生影响。不同进制可以混合使用,比如 10 + 015 + 0x20 是一个合法的表达式。

printf() 的进制相关占位符如下:

  • %d :十进制整数。

  • %o :八进制整数。

  • %x :十六进制整数。

  • %#o :显示前缀 0 的八进制整数。

  • %#x :显示前缀 0x 的十六进制整数。

  • %#X :显示前缀 0X 的十六进制整数。

1
2
3
4
5
6
7
int x = 100;
printf("dec = %d\n", x); // 100
printf("octal = %o\n", x); // 144
printf("hex = %x\n", x); // 64
printf("octal = %#o\n", x); // 0144
printf("hex = %#x\n", x); // 0x64
printf("hex = %#X\n", x); // 0X64

9.5 进制间的转换(了解)

9.5.1 二进制如何表示整数?

  • 计算机数据的存储使用二进制补码形式存储,并且最高位是符号位

    • 正数:最高位是0
    • 负数:最高位是1
  • 规定1:正数的补码与反码、原码一样,称为三码合一

  • 规定2:负数的补码与反码、原码不一样:

    • 负数的原码:把十进制转为二进制,然后最高位设置为1
    • 负数的反码:在原码的基础上,最高位不变,其余位取反(0变1,1变0)
    • 负数的补码:反码+1

9.5.2 二进制与十进制间的转换

1689209516368

二进制转十进制:权相加法

针对于一个字节的数据举例来说:

1689212021511

  • 例如:1个字节(8位)

    25 ==> 原码 0001 1001 ==> 反码 0001 1001 –>补码 0001 1001

    -25 ==>原码 1001 1001 ==> 反码1110 0110 ==>补码 1110 0111

1
2
3
4
5
6
7
8
整数:
正数:25 00000000 00000000 000000000 00011001(原码)
正数:25 00000000 00000000 000000000 00011001(反码)
正数:25 00000000 00000000 000000000 00011001(补码)

负数:-25 10000000 00000000 000000000 00011001(原码)
负数:-25 11111111 11111111 111111111 11100110(反码)
负数:-25 11111111 11111111 111111111 11100111(补码)

一个字节可以存储的整数范围是多少?

1
2
3
4
5
6
7
8
9
//1个字节:8位

0000 0001 ~ 0111 111 ==> 1~127

1000 0001 ~ 1111 1111 ==> -127 ~ -1

0000 0000 ==>0

1000 0000 ==> -128(特殊规定)=-127-1

十进制转二进制

十进制转二进制:除2取余的逆

1689214597038

9.5.3 二进制与八进制、十六进制间的转换

二进制转八进制

image-20230808151057231

二进制转十六进制

image-20230808151152468

八进制、十六进制转二进制

1689216401546

1689216597551

练习:以下叙述中错误的是(  )。
A.C程序在运行过程中所有计算都以十进制方式进行
B.C程序在运行过程中所有计算都以二进制方式进行
C.所有C程序都需要编译链接无误后才能运行
D.C程序中字符变量存放的是字符的ASCII值

【答案】A

【解析】C程序在运行过程中所有计算都以二进制方式进行。答案选择A选项。

【华南理工大学2018研】与十进制1100等值的十六进制数是(  )。
A.44A
B.44C
C.54A
D.54C

【答案】B

【解析】1100转换成二进制为0100 0100 1100,因此转换为十六进制为44C。

Windows:MinGW编译器的安装和配置

1、MinGW介绍

MinGW(Minimalist GNU on Windows)实际上是GCC在Windows平台上的移植版本,因此可以将源代码编译为可在 Windows 中运行的可执行程序。MinGW是,主要用于在Windows上本地编译C和C++代码。

目前适用于 Windows 平台、受欢迎的 GCC 移植版主要有 2 种,分别为 MinGW 和 Cygwin。
其中:

MinGW 侧重于服务 Windows 用户可以使用 GCC 编译环境,是真正的GCC移植,相比后者体积更小,使用更方便;
Cygwin 只是提供一个类unix的环境内部是原生的GCC,借助它不仅可以在 Windows 平台上使用 GCC 编译器,理论上可以运行 Linux 平台上所有的程序;

Cygwin 只是提供一个类unix的环境内部是原生的GCC。

如果读者仅需要在 Windows 平台上使用 GCC,可以使用 MinGW 或者 Cygwin;除此之外,如果还有更高的需求(例如运行 POSIX 应用程序),就只能选择安装 Cygwin。

2、下载与安装

2.1 下载

下载地址:https://sourceforge.net/projects/mingw/files/

点击“Download Latest Version”即可

1692674344122

2.2 安装

下载完成后,会得到一个名为 mingw-get-setup.exe 的安装包,双击打开它,可以看到如下的对话框:

1692674777924

直接点击“Install”,进入下面的对话框:

1692674793945

读者可根据自己操作系统的实际情况,自定义 MinGW 的安装位置(建议安装到非C盘的指定目录下),然后点击“continue”,进入下面的对话框:

1692674846199

​ 进入安装 MinGW 配置器的界面,耐心等待安装完成(显示 100%)即可。

1692675129933

安装完成之后,继续点击“continue”,进入下面的对话框,这是一个名为 “MinGW Installer Manager” 的软件,借助它,我们可以随时根据需要修改 GCC 编译器的配置。

1692675199055

常见的安装包介绍如下。

安装包名称 作用
mingw32-binutils 用于编译生成的 .o 文件的链接、汇编、生成静态库等
mingw32-gcc 核心的 C 编译器
mingw32-gcc-ada Ada 编译器
mingw32-gcc-fortran Fortran 编译器
mingw32-gcc-g++ C++ 编译器
mingw32-gcc-objc Objective-C 编译器
mingw32-libgcc C 编译器编译出来的程序的运行库

其中minw32-gcc-g++支持C++编译和minw32-gcc支持C编译。

1692693959356

为使 GCC 同时支持编译 C 语言和 C++,需勾选上图中标注的 2 项。选中其中一项,鼠标右键点击,选择“Mark for Installation”,如图所示。

1692675410414

标记完以后如图所示。

1692698300912

GCC 还支持其它编程语言,读者可借助此配置器,随时根据需要安装自己需要的编译环境。

勾选完成后,在菜单栏中选择 Installation -> Apply Changes

1692675512853

弹出如下对话框:

1692675528090

选择“Apply”。然后耐心等待,直至安装成功,即可关闭此界面。

1692675556189

3、配置:path环境变量

在安装完成的基础上,我们需要手动配置 PATH 环境变量。

1)依次 右击计算机(我的电脑) -> 属性 -> 高级系统设置 -> 环境变量,例如我将其安装到了C:\MinGW文件夹中,因此 PATH 环境变量的设置如下:

image-20230822181721360

image-20230822181849566

2)打开命令行窗口(通过在搜索栏中执行 cmd 指令即可),输入gcc -v指令,如果输出 GCC 编译器的具体信息,则表示安装成功,例如:

image-20230822182303099

通过上面的安装,我们就可以在当前 Windows 平台上编译、运行 C 或者 C++ 程序了。

因为 MinGW-w64 本来就是将 GCC 移植到 Windows 上的产物,所以操作方式和 GCC 一样,只是在 Linux 下命令是被键入到“终端”中,而 Windows 下则是被键入到“命令提示符”里。

C开发利器:CLion的使用

1. 认识CLion

1.1 JetBrains 公司介绍

CLion,是 JetBrains (https://www.jetbrains.com/)公司的产品,该公司成立于2000年,总部位于捷克的布拉格,致力于为开发者打造最高效智能的开发工具。

1692692710108

公司旗下的各种产品:

  • CLion:用于开发 C/C++
  • IDEA:用于开发 Java
  • PyCharm:用于开发 python
  • WebStorm:用于开发 JavaScript、HTML5、CSS3 等前端技术
  • PhpStorm:用于开发 PHP
  • RubyMine:用于开发 Ruby/Rails
  • AppCode:用于开发 Objective - C/Swift
  • DataGrip:用于开发数据库和 SQL
  • Rider:用于开发.NET
  • GoLand:用于开发 Go

1.2 CLion 介绍

Clion是一款专门开发C以及C++所设计的跨平台的集成开发环境(IDE)。它是以IntelliJ为基础设计的,包含了许多智能功能来提高开发人员的生产力。这种强大的IDE帮助开发人员在Linux、OS X和Windows上来开发C/C++,同时它还能使用智能编辑器来提高代码质量、自动代码重构并且深度整合Cmake编译系统,从而提高开发人员的工作效率。

1.3 Clion 的下载

1692685638894

官网提供的详细使用文档:
https://www.jetbrains.com.cn/clion/features/

2. 安装

2.1 安装前的准备

  • 64 位 Microsoft Windows 11、10、8
  • 最低 2 GB 可用 RAM,推荐 8 GB 系统总 RAM
  • 3.5 GB 硬盘空间,推荐 至少5G的SSD硬盘
  • 最低屏幕分辨率 1024x768,推荐1920×1080

2.2 安装过程

1、下载完安装包,双击直接安装 image-20240425223946881

2、欢迎安装

1692685818554

4、选择安装目录

1692685875043

选择安装目录,目录中要避免中文和空格。

1692685853085

5、创建桌面快捷图标等

1692686073729

确认是否与.c、.h、.cpp格式文件进行关联。这里建议不关联。

6、在【开始】菜单新建一个文件夹(这里需要确认文件夹的名称),来管理CLion的相关内容。

1692686114854

1692686134454

7、完成安装

1692686267537

8、双击打开

1692686314524

9、选择“Do not import settings”,点击“OK”按钮

1692686365691

10、如图所示,需要激活CLion

1692686434862

2.3 注册

  • 选择1:适用30天。在CLion版本中,需要先登录,才能开启试用。

    1692686513431
  • 选择2:付费购买旗舰版

    1692686556166 1692686590056
  • 选择3:(推荐)

    • 大家参照《02-软件\CLion-2022.1.3安装包\CLion注册文档\CLion注册文档.docx》操作即可。
    • 由于存在时效性,如果失效,大家可以自行搜索注册方式即可。

3. HelloWorld的实现

3.1 新建Project

选择”New Project”:

1692686679397

指定创建C可执行文件、工程目录,图中的“untitled1”需要修改为自己的工程名称。如下所示:

1692687949240

选择C可执行文件,修改工程名称为demo1

1692687854607

点击“Create”进行下一步,如图所示

1692688033686

此处选择编译器,默认MinGW即可,点击“OK”按钮,如图所示,默认创建了main.c文件。

1692688090496

3.2 运行

点击执行按钮,如下所示

1692688278731

4. 详细设置

image-20230823143234863

CLion的设置都在 File - Settings 中进行。

4.1 设置整体主题

1、选择主题

image-20230823143412251

2、设置菜单和窗口字体和大小

image-20230823143554720

4.2 设置编辑器主题样式

1、字体大小

1655136907073

更详细的字体与颜色如下:

image-20230823144141946

温馨提示:如果选择某个font字体,中文乱码,可以在fallback font(备选字体)中选择一个支持中文的字体。

2、注释的字体颜色

image-20220616121435182

  • Block comment:修改多行注释的字体颜色
  • Doc Comment –> Text:修改文档注释的字体颜色
  • Line comment:修改单行注释的字体颜色

4.3 代码智能提示功能

1655137649491

代码提示和补充功能有一个特性:区分大小写。 如果想不区分大小写的话,就把这个对勾去掉。建议去掉勾选

4.4 设置项目文件编码(一定要改)

image-20220615190832482

说明: Transparent native-to-ascii conversion主要用于转换ascii,显式原生内容。一般都要勾选。

4.5 设置控制台的字符编码

image-20230823144626692

5. 插件的使用(重要)

1、为何安装C/C++ Single File Execution插件?

前面已经创建了一个demo1工程,项目文件夹内存在一个代码文件,名为main.c。如果再创建一个C源文件,内部如果也包含main()函数,则会报错!因为默认C工程下只能有一个main()函数。如何解决此问题呢?

2、安装并测试

1)在 File - Settings - Plugins 中搜索 C/C++ Single File Execution 插件并安装

image-20230823145107293

2)在需要运行的代码中右键,点击 Add executable for single c/cpp file

1692774502830

3)此时可以在 Cmakelists.text 文件中看到多出的这一行代码,这就是插件帮我们完成的事情

1692774556495

4)右键项目文件夹,点击 Reload CMake Project 进行刷新

1692774575597

5)此时右上角标签处已经增加了我们的文件选项,选择需要的标签

1692774598633

6)点击小三角,或右键代码处点击 Run 选项,即可运行代码。

1692774678384

7)在该工程下创建main2.c文件,文件中的代码如下所示,执行上面相同的步骤。

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

int main() {
printf("Hello, World2!\n");
return 0;
}

可以发现一个工程中允许存在多个main方法了,而且可以独立允许。

6. 快捷键的使用

6.1 常用快捷键

第1组:通用型

说明 快捷键
复制代码-copy ctrl + c
粘贴-paste ctrl + v
剪切-cut ctrl + x
撤销-undo ctrl + z
反撤销-redo ctrl + shift + z
保存-save all ctrl + s
全选-select all ctrl + a

第2组:提高编写速度(上)

说明 快捷键
提示代码模板-insert live template ctrl+j
使用xx块环绕-surround with … ctrl+alt+t
调出生成getter/setter/构造器等结构-generate … alt+insert
自动生成返回值变量-introduce variable … ctrl+alt+v
复制指定行的代码-duplicate line or selection ctrl+d
删除指定行的代码-delete line ctrl+y
切换到下一行代码空位-start new line shift + enter
切换到上一行代码空位-start new line before current ctrl +alt+ enter
向上移动代码-move statement up ctrl+shift+↑
向下移动代码-move statement down ctrl+shift+↓
向上移动一行-move line up alt+shift+↑
向下移动一行-move line down alt+shift+↓

第3组:提高编写速度(下)

说明 快捷键
批量修改指定的变量名、方法名、类名等-rename shift+f6
抽取代码重构方法-extract method … ctrl+alt+m
选中的结构的大小写的切换-toggle case ctrl+shift+u

第4组:类结构、查找和查看源码

说明 快捷键
退回到前一个编辑的页面-back ctrl+alt+←
进入到下一个编辑的页面-forward ctrl+alt+→
打开的类文件之间切换-select previous/next tab alt+←/→
定位某行-go to line/column ctrl+g
回溯变量或方法的来源-go to implementation(s) ctrl+alt+b
折叠方法实现-collapse all ctrl+shift+ -
展开方法实现-expand all ctrl+shift+ +

第5组:查找、替换与关闭

说明 快捷键
查找指定的结构 ctlr+f
快速查找:选中的Word快速定位到下一个-find next ctrl+l
查找与替换-replace ctrl+r
直接定位到当前行的首位-move caret to line start home
直接定位到当前行的末位 -move caret to line end end
查询当前元素在当前文件中的引用,然后按 F3 可以选择 ctrl+f7
全项目搜索文本-find in path … ctrl+shift+f
关闭当前窗口-close ctrl+f4

第6组:调整格式

说明 快捷键
格式化代码-reformat code ctrl+alt+l
使用单行注释-comment with line comment ctrl + /
使用/取消多行注释-comment with block comment ctrl + shift + /
选中数行,整体往后移动-tab tab
选中数行,整体往前移动-prev tab shift + tab

第7组-Debug快捷键

说明 快捷键
单步调试(不进入函数内部)- step over F8
单步调试(进入函数内部)- step into F7
强制单步调试(进入函数内部) - force step into alt+shift+f7
选择要进入的函数 - smart step into shift + F7
跳出函数 - step out shift + F8
运行到断点 - run to cursor alt + F9
继续执行,进入下一个断点或执行完程序 - resume program F9
停止 - stop Ctrl+F2
查看断点 - view breakpoints Ctrl+Shift+F8
关闭 - close Ctrl+F4

6.2 查看快捷键

1、已知快捷键操作名,未知快捷键

1692752261713

2、已知快捷键,不知道对应的操作名

1692752357783

6.3 自定义快捷键

1692752523743

第01章_C语言入门

本章专题脉络

第1章_C语言入门

1、初识计算机语言

1.1 计算机语言是什么

  • 人类语言:是人与人之间用于沟通的一种方式。例如:中国人与中国人用普通话沟通。而中国人要和英国人交流,可以使用普通话或英语。

  • 计算机编程语言,就是人与计算机交流的方式。人们可以使用编程语言对计算机下达命令,让计算机完成人们需要的功能。

    • 计算机语言有很多种。如:C 、C++、Java、Go、JavaScript、Python,Scala等。

体会:语言 = 语法 + 逻辑

1.2 计算机语言简史

  • 第一代:机器语言(相当于人类的石器时代)

    • 1946年2月14日,世界上第一台计算机ENAC诞生,使用的是最原始的穿孔卡片。这种卡片上使用的是用二进制代码表示的语言,与人类语言差别极大,这种语言就称为机器语言。比如一段典型的机器码:

      1
      2
      3
      1.	0000,0000,000000010000 代表 LOAD A, 16
      2. 0000,0001,000000000001 代表 LOAD B, 1
      3. 0001,0001,000000010000 代表 STORE B, 16
    • 这种语言本质上是计算机能识别的唯一语言,人类很难理解。可以大胆想象”此时的程序员99.9%都是异类!

      image-20220309223406537

  • 第二代:汇编语言(相当于人类的青铜&铁器时代)

    • 使用英文缩写的助记符来表示基本的操作,这些助记符构成了汇编语言的基础。比如:LOADMOVE等,使人更容易使用。因此,汇编语言也称为符号语言

    • 优点:能编写高效率的程序。

    • 缺点:汇编语言是面向机器的,不同计算机会有不同的汇编语言,程序不易移植。

      1689152350231

    • 目前仍然应用于工业电子编程领域、软件的加密解密、计算机病毒分析等。

  • 第三代:高级语言(相当于人类的信息时代)

    • 高级语言,是一种接近于人们使用习惯的程序设计语言。它允许程序员使用接近日常英语的指令来编写程序,程序中的符号和算式也与日常用的数学式子差不多,接近于自然语言和数学语言,容易为人们掌握。比如:

      1689152910950

    • 高级语言独立于计算机硬件,有一定的通用性;计算机不能直接识别和执行用高级语言编写的程序,需要使用编译器或者解释器转换为机器语言才能被识别和执行。

      1689153225780

    • 使用普遍的高级语言有Fortran、ALGOL、Basic、COBOL、LISP、Pascal、PROLOG、C、C++、VC、VB、Delphi、Java等。

2、初识C语言

2.1 C语言的由来

C 语言最初是作为 Unix 系统的开发工具而发明的。

1969年,美国贝尔实验室的肯·汤普森(Ken Thompson)与丹尼斯·里奇(Dennis Ritchie)一起开发了Unix 操作系统。Unix 是用汇编语言写的,依赖于计算机硬件。为了程序的可读性可移植性,他们决定使用高级语言重写。但是,当时的高级语言无法满足他们的要求,汤普森就在 BCPL 语言的基础上发明了 B 语言

1972年,丹尼斯·里奇(Dennis Ritchie)在 B 语言的基础上重新设计了一种新语言,这种新语言取代了 B 语言,称为 C 语言

1973年,整个 Unix 系统都使用 C 语言重写

image-20230821142911092

此后,这种语言快速流传,广泛用于各种操作系统和系统软件的开发。如UNIX、MS-DOS、Microsoft Windows及Linux等。

image-20230808143350533

1988年,美国国家标准协会(ANSI)正式将 C语言标准化,标志着 C 语言开始稳定和规范化。

2.2 为什么要学习C语言

1、C语言具有可移植性好、跨平台的特点,用C编写的代码可以在不同的操作系统和硬件平台上编译和运行。

  • C 语言的原始设计目的,是将 Unix 系统移植到其他计算机架构,这使得它从一开始就非常注重可移植性。

2、C语言在许多领域应用广泛。掌握C语言可以让你有更多就业机会。

  • 操作系统:C 广泛用于开发操作系统,如 Unix、Linux 和 Windows。
  • 嵌入式系统:C 是一种用于开发嵌入式系统(如微控制器、微处理器和其他电子设备)的流行语言。
  • 系统软件:C用于开发设备驱动程序、编译器和汇编器等系统软件。
  • 网络:C 语言广泛用于开发网络应用程序,例如 Web 服务器、网络协议和网络驱动程序。
  • 数据库系统:C 用于开发数据库系统,例如 Oracle、MySQL 和 PostgreSQL。
  • 游戏:由于 C 能够处理低级硬件交互,因此经常用于开发计算机游戏。
  • 人工智能:C 用于开发人工智能和机器学习应用程序,例如神经网络和深度学习算法。
  • 科学应用:C 用于开发科学应用程序,例如仿真软件和数值分析工具。
  • 金融应用:C用于开发股票市场分析和交易系统等金融应用。

3、C 语言能够直接对硬件进行操作、管理内存、跟操作系统对话,这使得它是一种非常接近底层的语言,非常适合写需要跟硬件交互、有极高性能要求的程序

4、学习C语言有助于快速上手其他编程语言,比如C++(原先是C语言的一个扩展,在C语言的基础上嫁接了面向对象编程)、C#、Java、PHP、Javascript、Perl等。这些语言都继承或深受C语言的影响和启发。

5、C 语言长盛不衰。至今,依然是最广泛使用、最流行的编程语言之一。包括很多大学将C语言作为计算机教学的入门语言,拥有庞大而活跃的用户社区,这意味着有许多资源和库可供开发人员使用。

2.3 计算机语言排行榜

TIOBE (https://www.tiobe.com/tiobe-index/)是一个流行编程语言排行,每月更新。排名权重基于世界范围内 工程师数量,Google、Bing、Yahoo! 、Wikipedia、Amazon、Youtube和百度这些主流的搜索引擎,也将作为排名权重的参考指标。

image-20230821142412443

计算机走势图:

image-20230821142441945

2.4 网友一言话C

https://www.nowcoder.com/stack/209

image-20230819162927908

2.5 C语言的版本选择

随着微型计算机的日益普及,出现了许多C语言版本。

版本1:K&R C

K&R C 指的是 C 语言的原始版本。1978年,C 语言的发明者布莱恩·柯林(Brian Kernighan)和丹尼斯·里奇(Dennis Ritchie)合写了一本著名的教材《C 编程语言》(The C programming language)。

由于 C 语言还没有成文的语法标准,这本书就成了公认标准,以两位作者的姓氏首字母作为版本简称“K&R C”。

版本2:ANSI C(又称 C89 或 C90)

C 语言的原始版本非常简单,对很多情况的描述非常模糊,加上 C 语法依然在快速发展,要求将 C 语言标准化的呼声越来越高。

1989年,美国国家标准协会(ANSI)制定了一套 C 语言标准,并于次年被国际标准化组织(ISO)通过。它被称为“ANSI C”,也可以按照发布年份,称为“C89 或 C90”。

版本3:C99

C 语言标准的第一次大型修订,发生在1999年,增加了许多语言特性,比如双斜杠( // )的注释语法,可变长度数组、灵活的数组成员、复数、内联函数和指定的初始值设定项。这个版本称为 C99,是目前最流行的 C 版本

版本4:C11

2011年,标准化组织再一次对C 语言进行修订,增加了_Generic、static_assert 和原子类型限定符。这个版本称为C11。

需要强调的是,修订标准的原因不是因为原标准不能用,而是需要跟进新的技术。

版本5:C17

C11 标准在2017年进行了修补,但发布是在2018年。新版本只是解决了 C11 的一些缺陷,没有引入任何新功能。这个版本称为 C17。

版本6:C23

2023年预计发布,计划进一步增强安全性,消除实现定义的行为,引入模块化语言概念等新特性,使C语言在安全和可靠性方面有重大提高。

3、第一个C程序的编写->编译->运行

3.1 步骤1:HelloWorld的编写

C 语言的源代码文件,以后缀名 .c 结尾。下面是一个简单的 C 程序 Hello.c

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

int main()
{  
printf("hello,world!!\n");
  return 0;
}

3.2 步骤2:编译器的安装与配置

C 语言是一种编译型语言,源码都是文本文件,本身无法执行。必须通过编译器,生成二进制的可执行文件,才能执行。

目前,最常见的 C 语言编译器是自由软件基金会推出的 GCC 编译器,可以免费使用。Linux 和 Mac 系统可以直接安装 GCC,Windows 系统可以安装 MinGW

补充知识:MinGW和GCC的区别:

GCC是一个跨平台的编译器集合,可用于多种操作系统和处理器架构,包括Windows;而MinGW是GCC在Windows平台上的移植版本,主要用于在Windows上本地编译C和C++代码。

在Windows下,MinGW的详细安装和配置见《第01章附录:MinGW编译器的安装和配置.md》

3.3 步骤3:编译和运行

编译器将代码从文本翻译成二进制指令的过程,就称为编译阶段,又称为“编译时”(compile time),跟运行阶段(又称为“运行时”)相区分。

假设你已经安装好了 GCC 编译器,可以通过win+r打开cmd命令行,在Hello.c文件所在目录下执行下面的命令。

1
> gcc Hello.c

image-20230821203629981

上面命令使用 gcc 编译器,将源文件 Hello.c 编译成二进制代码。

运行这个命令以后,默认会在当前目录下生成一个编译产物文件 a.exe。执行该文件,就会在屏幕上输出 Hello World 。

1
2
> a.exe
hello,world!!

image-20230821203807040

GCC 的 -o 参数(output 的缩写)可以指定编译产物的文件名。

1
> gcc -o Hello Hello.c
image-20230821204057346

上面命令的 -o Hello 指定,编译得到的可执行文件名为 Hello.exe ,取代默认的 a.exe。执行该文件,也会得到同样的结果。

1
2
> Hello.exe
hello,world!!

image-20230821204306119

GCC 的 -std= 参数(standard 的缩写)还可以指定按照哪个 C 语言的标准进行编译。

1
> gcc -std=c99 Hello.c

上面命令指定按照 C99 标准进行编译。

4、IDE的使用

IDE(Integrated Development Environment,集成开发环境):相较于文本开发工具,IDE可以把代码编写,编译,执行,调试等多种功能综合到一起的开发工具。

4.1 开发工具介绍

方式1:本地安装的IDE工具

1. Code::Block

Code::Block是一个免费的跨平台IDE,它支持C、C++和Fortan程序的开发。Code::Block的最大特点是它支持通过插件的方式对IDE自身功能进行扩展,这使得Code::Block具有很强的灵活性,方便用户使用。

官网地址:https://www.codeblocks.org

2. Microsoft Visual C++ 2010

Visual C++ 2010,简称VC2010,是由微软开发的独立的、免费的 C/C++ 编译工具,与Visual Basic等并列,最后微软将它们整合在一起组成了Visual Studio。

Visual C++从发布起到现在已经有10个大版本了,这里介绍的Visual C++ 2010就是Visual C++ 10,简称VC10。上朔10多年发布的Visual C++ 6.0,被称为史上最经典的VC,现在有很多企业还在用它,大量的教材基于这个版本的VC来写的。但VC6比较弱,被淘汰是迟早的。

3. Microsoft Visual Studio

Visual Studio(简称 VS)是由微软公司发布的集成开发环境。它包括了整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开发环境(IDE)等。

Visual Studio 支持 C/C++、C#、F#、VB 等多种程序语言的开发和测试,可以用于生成Web应用程序,也可以生成桌面应用程序,功能十分强大。但下载和安装很可能耗时数小时,还可能会塞满磁盘。

Visual Studio 2019有三种版本:社区版(免费,不支持企业使用),专业版(第一年1199美元/ 799美元续订)和企业版(第一年5999美元/2569美元续订)。企业版拥有面向架构师的功能、高级调试和测试,这些功能是另两种SKU所没有的。

Visual Studio旨在成为世界上最好的IDE(集成开发环境),目前最新版本为 Visual Studio 2023。

这就好像Office 2007是由Word 2007、Excel 2007、Access 2007等等组成的一个道理。其中Visual C++就是Visual Studio的一个重要的组成部分。

官网地址:https://visualstudio.microsoft.com

4. CLion

CLion是一款由JetBrains推出的跨平台C/C++集成开发环境(IDE),它具有智能编辑器、CMake构建支持、调试器、单元测试、代码分析等功能,可以极大提高C/C++开发效率。

官网地址:https://www.jetbrains.com/clion

方式2:可在线使用的工具

CodingGround: https://tutorialspoint.com/compile_c_online.php

image-20230821153059391

OnlineGDB: https://onlinegdb.com/online_c_compiler

image-20230821153146970

Lightly:https://cde2f3ce.lightly.teamcode.com/

image-20230821153237900

4.2 CLion的下载与安装

详细见《第01章附录:C开发利器:CLion的使用.md》

4.3 CLion中HelloWorld的执行

1)选择”New Project”:

1692686679397

2)指定创建C可执行文件、工程目录,图中的“untitled1”需要修改为自己的工程名称。如下所示:

1692687949240

3)选择C可执行文件,修改工程名称为demo1

1692687854607

4)点击“Create”进行下一步,如图所示

1692688033686

5)此处选择编译器,默认MinGW即可,点击“OK”按钮,如图所示,默认创建了main.c文件。

1692688090496

6)点击执行按钮,如下所示

1692688278731

4.4 C 程序运行机制

过程1:编辑

编写C语言源程序代码,并以文件的形式存储到磁盘中。源程序文件以“.c”作为扩展名。

过程2:编译

将C语言源程序转换为目标程序(或目标文件)。如果程序没有错误,没有任何提示,就会生成一个扩展名为“.obj”的二进制文件。C语言中的每条可执行语句经过编译后最终都将被转换成二进制的机器指令。

过程3:链接/连接

将编译形成的目标文件“.obj”和库函数及其他目录文件连接/链接,形成统一的可执行的二进制文件“.exe”。

为什么需要链接库文件呢?

因为我们的C程序中会使用 C程序库的内容,比如<stdio.h> 、<stdlib.h> 中的函数printf()、system()等,这些函数不是程序员自己写的,而是C程序库中提供的,因此需要链接。链接后,生成的.exe 文件,比obj 文件大了很多。

过程4:运行

有了可执行的exe文件,我们可以在控制台下直接运行此exe文件。

image-20230810153413126

注意:

对修改后的xxx.c源文件需要重新编译、链接,生成新的exe文件后,再执行,才能生效。

练习:

计算机高级语言程序的运行方法有编译执行和解释执行两种,以下叙述中正确的是(  )。
A.C语言程序仅可以编译执行
B.C语言程序仅可以解释执行
C.C语言程序既可以编译执行,又可以解释执行
D.以上说法都不对

【答案】A

【解析】编译执行是指程序执行前需要一个专门的编译过程把程序编译成机器语言的文件,再次运行时不需要重新翻译,执行效率高;解释执行是指每个语句都是执行的时候才翻译,执行效率低。用C语言编写的程序必须经过编译器编译后,转换为二进制的机器指令来运行。

练习:

以下叙述中错误的是(  )。
A.C语言的可执行程序是由一系列机器指令构成的
B.用C语言编写的源程序不能直接在计算机上运行
C.通过编译得到的二进制目标程序需要链接才可以运行
D.在没有安装C语言集成开发环境的机器上不能运行C源程序生成的exe文件

【答案】D

【解析】A项正确,C语言的可执行程序是由一系列机器指令组成的;BC项正确,用C语言编写的源程序必须经过编译,生成二进制目标代码,再经过连接才能运行;D项错误,C语言经过编译链接后的二进制目标代码可以脱离C语言集成开发环境独立运行。答案选择D选项。

5、注 释(comment)

image-20220610113151774
  • 什么是注释?

    • 源文件中用于解释说明程序的文字就是注释。
    • 注释只是给人看的,程序执行时,编译器会忽略注释,注释对代码执行没有影响
  • 注释是一个程序员必须要具有的良好编程习惯。实际开发中,程序员可以先将自己的思想通过注释整理出来,再用代码去体现。

    程序员最讨厌两件事:

    • 一件是自己写代码被要求加注释

    • 另一件是接手别人代码,发现没有注释

  • 不加注释的危害

  • C语言中的注释类型:

    • 单行注释
    1
    // 单行注释
    • 多行注释(或块注释)
    1
    2
    3
    4
    5
    /*
     这是第一行注释
     这是第二行注释
     这是第三行注释
    */
    1
    /* 我是被注释的文字 */
  • 举例

    • 举例1
    1
    2
    3
    4
    5
    6
    7
    #include <stdio.h>              //这是编译预处理指令

    int main() //定义主函数
    {   //函数开始的标志
    printf("Hello World\n"); //输出所指定的一行信息
      return 0; //函数执行完毕时返回函数值0
    } //函数结束的标志
    • 举例2
    1
    int x = 1; // 这里定义了一个变量
    • 举例3
    1
    2
    3
    4
    5
    6
    printf("1"); 
    /*
    printf("2");
    printf("3");
    */
    printf("4");
    • 举例4
    1
    int open(char* s /* file name */, int mode);
  • 注意:

    1. 多行注释不能嵌套使用

      1
      2
      3
      4
      5
      6
      7
      /*
      我是注释信息1
      /*
      我是注释信息2
      */
      我是注释信息3
      */
    2. 双引号内使用注释,会被当做普通字符串看待,失去注释作用

      1
      printf("// hello /* atguigu */ ");
  • 一个段子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    A:嘿 //是什么意思啊?
    B:嘿.
    A:呃 我问你//是什么意思?
    B:问吧.
    A:我刚才不是问了么?
    B:啊?
    A:你再看看记录...
    B:看完了.
    A:......所以//是啥?
    B:所以什么?
    A:你存心耍我呢吧?
    B:没有啊 你想问什么?
    ……
    不断循环之后,A一气之下和B绝交,自己苦学程序。
    N年之后,A终于修成正果,回想起B,又把聊天记录翻出来看,这时,他突然发现B没有耍他……
    而他自己也不知道当年他问B的究竟是什么问题……

练习:

以下叙述中错误的是(  )。
A.C语言中的每条可执行语句和非执行语句最终都将被转换成二进制的机器指令
B.C程序经过编译、链接步骤之后才能形成一个真正可执行的二进制机器指令文件
C.用C语言编写的程序称为源程序,它以ASCII代码形式存放在一个文本文件中
D.C语言源程序经编译后生成后缀为.obj的目标程序

【答案】A

【解析】A项错误,注释语句不会被翻译成二进制的机器指令。C源程序经过C编译程序编译之后生成后缀为.obj的二进制文件(称为目标文件),然后由“链接程序”(Link)的软件把.obj文件与各种库函数连接起来生成一个后缀为.exe的可执行文件。答案选择A选项。

6、HelloWorld的剖析

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

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

6.1 规范的代码风格

正确的缩进和空白

  1. 使用一次tab操作,实现缩进,默认整体向右边移动。相反,使用shift+tab整体向左移

  2. 运算符两边习惯性各加一个空格。比如:2 + 4 * 5

  3. 可以使用代码格式化快捷键 ctrl+alt+L。(在CLion中使用)

代码块的风格

1)行尾风格

1
2
3
4
5
6
7
8
9
int main(){                                      
if(a > b) {
return a;
}
else{
return b;
}
  return 0;
}

2)次行风格

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
if(a > b)
{
return a;
}
else
{
return b;
}
  return 0;
}

正确的注释和注释风格:

1)如果注释一个函数,可以使用多行注释(或块注释)。

2)如果注释函数中的某一行语句,可以使用单行注释。

6.2 代码细节剖析

① main()

1
int main(){}
  • 每一个程序(或工程)可以定义很多函数(后面讲),但有且只有一个main()函数,作为程序执行的入口,在 main()函数结尾结束整个程序的运行。
  • 空括号(),表示 main 不接受任何参数。
  • 写在main之前的“int”称为关键字,代表数据类型是整型。它是main()的返回值类型。即在执行main()函数后会得到一个整型值(即函数值)。
  • C 语言约定: return 0,表示main()函数终止运行,且运行成功;如果返回其它非零整数,就表示运行失败。默认情况下,如果 main() 里面省略 return 0 这一行,编译器会自动加上,但是为了保持统一的代码风格,不建议省略。

② 函数体

  • 一对花括号{}定义了函数的主体,所有函数都必须以大括号开头和结尾,成对出现。

  • C 程序中的函数体指的是作为该函数一部分的语句。它可以是任何操作,比如搜索、排序、打印等。

  • 每一个执行语句后面都会有一个英文分号“;”作为语句结束的标志。

  • 一行内可写几条语句,一条语句也可写在几行上。

③ printf()

printf()函数是产生格式化输出的函数,作用是将参数文本输出到屏幕。它名字里面的 f 代表 format (格式化),表示可以指定输出文本的格式。

1
printf("Hello World"); //将字符串输出到控制台,行尾不换行

为了让光标移到下一行的开头,可以在输出文本的结尾,添加一个换行符 \n 。

1
printf("Hello World\n");

④ 标准库、头文件

printf() 是在标准库的头文件 stdio.h 中定义的。要想在程序中使用这个函数,必须在源文件头部引入这个头文件。即:

1
#include <stdio.h>

何为标准库?

程序需要用到的功能,不一定需要自己编写,C 语言可能已经自带了。程序员只要去调用这些自带的功能就可以了。C 语言自带的所有这些功能,统称为“标准库”(standard library),包含C 内置函数、常量和头文件。

因为它们是写入标准的,到底包括哪些功能,应该怎么使用,都是规定好的,我们直接调用即可。

何为头文件?

不同的功能定义在不同的文件里,这些文件统称为“头文件”(header file)。如果系统自带某一个功能,就一定会自带描述这个功能的头文件,比如 printf() 的头文件就是系统自带的 stdio.h 。头文件的后缀通常是 .h

预处理命令:#include命令

如果要使用某个功能,就必须先加载其对应的头文件,加载使用的是 #include 命令,声明在各文件模块的开头。C语言中以 # 号开头的命令称为预处理命令。顾名思义,在编译器对当前C程序进行编译前执行预处理操作。

格式:

1
#include <头文件名>

举例:

1
#include <stdio.h>   

注意,加载头文件的 #include 语句不需要分号结尾

对比写法:

1
2
3
#include <stdio.h>//编译系统在系统头文件所在目录搜索

#include "stdio.h"//编译系统首先在当前的源文件目录中查找 stdio.h,找不到的话,再转向系统头文件所在目录搜索。

stdio.h是系统提供的一个文件名,stdio是standard input & output的缩写。

结论:

  • 引用系统头文件,两种形式都会可以,#include <> 效率高。
  • 引用用户头文件,只能使用 #include ""

常用的C头文件

  • stdio.h——定义核心输入和输出函数
    • printf()、scanf()、getchar()、putchar()
  • stdlib.h——定义数值转换函数、伪随机网络生成器和内存分配
  • string.h——定义字符串处理函数
  • stdint.h——定义精确宽度的整数类型
  • math.h——定义常用的数学函数
    • sin()、sqrt()
  • stddef.h——定义了几个有用的类型和宏

练习:

以下叙述中正确的是( )。
A.C程序中的注释只能出现在程序的开始位置和语句的后面
B.C程序书写格式严格,要求一行内只能写一个语句
C.C程序书写格式自由,一个语句可以写在多行上
D.用C语言编写的程序只能放在一个程序文件中

【答案】C

【解析】C程序的注释可以出现在C程序的任何位置,注释符号:“//”或“//”,选项A错误。C程序中,一行内可写多个语句,每条语句用分号“;”结束,选项B错误,选项C正确。用C语言编写的程序可以放在多个程序文件中,用#include命令行实现文件包含功能,选项D错误。答案选择C选项。

【中央财经大学2018研】以下叙述错误的是( )。
A.在程序中凡是以“#”开始的语句行都是预处理命令行
B.预处理命令行的最后不能以分号表示结束
C.#include MAX是合法的宏定义命令行
D.C程序对预处理命令行的处理是在程序执行的过程中进行的

【答案】D

【解析】在C语言中,凡是以“#”开头的行都称为编译预处理命令行,为了区别C语句,后面是不加分号的。编译预处理是在编译程序对C源程序进行编译前执行的,而不是在程序执行过程中进行的。

注意:

学习编程最容易犯的错是语法错误。C语言要求必须按照语法规则编写代码。如果你的程序违反了语法规则,例如:忘记了分号、大括号、引号 或者拼错了单词等,C编译器都会报语法错误。尝试着去看懂编译器会报告的错误信息

7、关于输出

7.1 printf()标准格式

1
printf(格式控制字符串,输出列表);

image-20230822164051224

其中,

  • "格式控制字符串"是用双引号括起来的一个字符串。包括:

    • 普通字符:普通字符即需要在输出时原样输出的字符。
    • 占位符:由“%”和格式字符组成。这个位置可以用其它值代入。
  • "输出列表"是程序需要输出的一些数据,可以是常量、变量或表达式。用于替换占位符的位置。

注意:printf() 参数与占位符是一一对应关系。如果参数个数少于对应的占位符, printf() 可能会输出内存中的任意值。

7.2 占位符

占位符的第一个字符是 % ,第二个字符表示占位符的类型。

printf() 的占位符有许多种类,与 C 语言的数据类型相对应。

下面按照字母顺序,列出占位符如下,方便查阅(红色为常用的):

%a :浮点数(仅C99有效)
%A :浮点数(仅C99有效)
%c :char型数据
%d :十进制整数(int)
%e :使用科学计数法的浮点数,指数部分的 e 为小写
%E :使用科学计数法的浮点数,指数部分的 E 为大写
%i :整数,基本等同于 %d
%f :浮点数(float)
%g :6个有效数字的浮点数。整数部分一旦超过6位,就会自动转为科学计数法,指数部分的 e 为小写
%G :等同于 %g ,唯一的区别是指数部分的 E 为大写
%hd :十进制 short int 类型
%ho :八进制 short int 类型
%hx :十六进制 short int 类型
%hu :unsigned short int 类型
%ld :十进制整数(long)
%lo :八进制 long int 类型
%lx :十六进制 long int 类型
%lu :unsigned long int 类型
%lld :十进制 long long int 类型
%llo :八进制 long long int 类型
%llx :十六进制 long long int 类型
%llu :unsigned long long int 类型
%le :科学计数法表示的 long double 类型浮点数
%lf :十进制浮点数(double)
%n :已输出的字符串数量。该占位符本身不输出,只将值存储在指定变量之中
%o :八进制整数
%p :指针
%s :字符串
%u :十进制无符号整数(unsigned int)
%x :十六进制整数
%zd : size_t 类型
%% :输出一个百分号

7.3 举例

举例1:%d

1
2
int num = 10;
printf("count is %d\n",num); //输出:count is 10
1
printf("There are %i students\n", 5);   //输出:There are 5 students

举例2:%lf 和 %f

1
2
3
4
5
6
7
8
9
10
11
12
float f = 3.1415926535f;    // 单精度浮点数
double d = 3.1415926535; // 双精度浮点数

// 使用 %f 输出单精度浮点数
printf("Float: %f\n", f); //Float: 3.141593
// 使用 %lf 输出双精度浮点数
printf("Double: %lf\n", d); //Double: 3.141593

// 使用 %f 输出单精度浮点数
printf("Float: %.8f\n", f); //Float: 3.14159274
// 使用 %lf 输出双精度浮点数
printf("Double: %.8lf\n", d); //Double: 3.14159265

举例3:%c

1
2
char level = 'A';
printf("this score level is:%c\n",level); //输出:this score level is:A

举例4:%s

1
printf("%s是我最喜欢的冷门歌手.\n","孙燕姿"); //输出:孙燕姿是我最喜欢的冷门歌手.

举例5:多个占位符

输出文本里面可以使用多个占位符。

1
printf("%s有%d部手机\n", "老板", 2); //输出:老板有2部手机

7.4 输出格式

printf() 可以定制占位符的输出格式。

格式1:限定宽度

printf() 允许限定占位符的最小宽度。

1
printf("%5d\n", 123); // 输出为 "  123" 

说明:%5d 表示这个占位符的宽度至少为5位。如果不满5位,对应的值的前面会添加空格。

输出的值默认是右对齐,即输出内容前面会有空格;如果希望改成左对齐,在输出内容后面添加空格,可以在占位符的 % 的后面插入一个 - 号。

1
printf("%-5d\n", 123); // 输出为 "123  "

对于小数,这个限定符会限制所有数字的最小显示宽度

1
printf("%12f\n", 123.45); // 输出 "  123.450000"

%12f 表示输出的浮点数最少要占据12位。由于小数的默认显示精度是小数点后6位,所以123.45 输出结果的头部会添加2个空格。

格式2:总是显示正负号

默认情况下, printf() 不对正数显示 + 号,只对负数显示 - 号。如果想让正数也输出 + 号,可以在占位符的 % 后面加一个 + 。

1
2
printf("%+d\n", 11); // 输出 +11
printf("%+d\n", -11); // 输出 -11

格式3:限定小数位数

输出小数时,有时希望限定小数的位数。举例来说,希望小数点后面只保留两位,占位符可以写成 %.2f 。

1
printf("Number is %.2f\n", 0.8); // 输出 Number is 0.80

这种写法可以与限定宽度占位符,结合使用。

1
printf("%6.2f\n", 0.8); // 输出为 "  0.80"

说明:%6.2f 表示输出字符串最小宽度为6,小数位数为2。整体长度不足 6 位时,右对齐显示。

最小宽度和小数位数这两个限定值,都可以用 * 代替,通过 printf() 的参数传入。

1
2
3
printf("%*.*f\n", 6, 2, 0.8);
//等同于
printf("%6.2f\n", 0.8);

【华南理工大学2018研】十六进制形式输出整数的格式说明符是( )。

A.%u
B.%ld
C.%x
D.%o

【答案】C

【解析】A表示输出的是无符号整型;B表示输出的是有符号长整型;D表示输出的是八进制。

8、练习

练习1:开发一个 ILoveC.c 程序,可以输出 “某某 is studying c!”

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

int main(){
//printf("谷小妹 is studying C!\n");
//printf("%s is studying C!\n","谷小妹");
return 0;
}

练习2:控制台打印:5 + 3 = 8

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

int main() {
printf("%d + %d = %d\n", 5, 3, (5 + 3));
return 0;
} //函数结束

附录:C/C++ Single File Execution插件的安装

1、为何安装C/C++ Single File Execution插件?

前面已经创建了一个demo1工程,项目文件夹内存在一个代码文件,名为main.c。如果再创建一个C源文件,内部如果也包含main()函数,则会报错!因为默认C工程下只能有一个main()函数。如何解决此问题呢?

2、安装并测试

1)在 File - Settings - Plugins 中搜索 C/C++ Single File Execution 插件并安装

image-20230823145107293

2)在需要运行的代码中右键,点击 Add executable for single c/cpp file

1692774502830

3)此时可以在 Cmakelists.text 文件中看到多出的这一行代码,这就是插件帮我们完成的事情

1692774556495

4)右键项目文件夹,点击 Reload CMake Project 进行刷新

1692774575597

5)此时右上角标签处已经增加了我们的文件选项,选择需要的标签

1692774598633

6)点击小三角,或右键代码处点击 Run 选项,即可运行代码。

1692774678384

7)在该工程下创建main2.c文件,文件中的代码如下所示,执行上面相同的步骤。

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

int main() {
printf("Hello, World2!\n");
return 0;
}

可以发现一个工程中允许存在多个main方法了,而且可以独立允许。

About C++

  • C++融合3种不同的编程方式:
    • C++继承了C语言高效,简洁,快速和可移植性的传统
    • C++面向对象的特性带来了全新的编程方式,这种方式是为应付复杂程度不断提高的现代编程任务而设计的
    • C++模板特性提供了另一种全新的编程方法——泛型编程

Read More