我所学的c语言

c语言结构

#include <stdio.h>

int main()
{
   /* 我的第一个 C 程序 */
   printf("Hello, World! \n");

   return 0;
}
  • 所有的 C 语言程序都需要包含 main() 函数。 代码从 main() 函数开始执行。

  • /**/ 用于注释说明。

  • printf() 用于格式化输出到屏幕。printf() 函数在 “stdio.h” 头文件中声明。

  • stdio.h 是一个头文件 (标准输入输出头文件) , #include 是一个预处理命令,用来引入头文件。 当编译器遇到 printf() 函数时,如果没有找到 stdio.h 头文件,会发生编译错误。

  • return 0; 语句用于表示退出程序。

    C 程序主要包括以下部分:

    • 预处理器指令

    • 函数

    • 变量

    • 语句 & 表达式

    • 注释

      编译 & 执行 C 程序

      步骤:

      1. 打开一个文本编辑器,添加上述代码。

      2. 保存文件为 hello.c

      3. 打开命令提示符,进入到保存文件所在的目录。

      4. 键入 gcc hello.c,输入回车,编译代码。

      5. 如果代码中没有错误,命令提示符会跳到下一行,并生成 a.out 可执行文件。

      6. 现在,键入 a.out 来执行程序。

      7. 您可以看到屏幕上显示 “Hello World”

c语言语法

C 程序由各种令牌组成,令牌可以是关键字、标识符、常量、字符串值,或者是一个符号。

在 C 程序中,分号是语句结束符。也就是说,每个语句必须以分号结束。它表明一个逻辑实体的结束。

c语言的注释方式:

//:单行注释

/**/:多行注释

标识符

C 标识符是用来标识变量、函数,或任何其他用户自定义项目的名称。一个标识符以字母 A-Z 或 a-z 或下划线 _ 开始,后跟零个或多个字母、下划线和数字(0-9)。

下表列出了 C 中的保留字。这些保留字不能作为常量名、变量名或其他标识符名称。

关键字 说明
auto 声明自动变量
break 跳出当前循环
case 开关语句分支
char 声明字符型变量或函数返回值类型
const 定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变
continue 结束当前循环,开始下一轮循环
default 开关语句中的”其它”分支
do 循环语句的循环体
double 声明双精度浮点型变量或函数返回值类型
else 条件语句否定分支(与 if 连用)
enum 声明枚举类型
extern 声明变量或函数是在其它文件或本文件的其他位置定义
float 声明浮点型变量或函数返回值类型
for 一种循环语句
goto 无条件跳转语句
if 条件语句
int 声明整型变量或函数
long 声明长整型变量或函数返回值类型
register 声明寄存器变量
return 子程序返回语句(可以带参数,也可不带参数)
short 声明短整型变量或函数
signed 声明有符号类型变量或函数
sizeof 计算数据类型或变量长度(即所占字节数)
static 声明静态变量
struct 声明结构体类型
switch 用于开关语句
typedef 用以给数据类型取别名
unsigned 声明无符号类型变量或函数
union 声明共用体类型
void 声明函数无返回值或无参数,声明无类型指针
volatile 说明变量在程序执行中可被隐含地改变
while 循环语句的循环条件

c语言中的空格

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

在 C 中,空格用于描述空白符、制表符、换行符和注释。空格分隔语句的各个部分,让编译器能识别语句中的某个元素(比如 int)在哪里结束,下一个元素在哪里开始。

c语言数据类型

变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。

C 中的类型可分为以下几种:

序号 类型与描述
1 基本类型: 它们是算术类型,包括两种类型:整数类型和浮点类型。
2 枚举类型: 它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量。
3 void 类型: 类型说明符 void 表明没有可用的值。
4 派生类型: 它们包括:指针类型、数组类型、结构类型、共用体类型和函数类型。

整数类型

下表列出了关于标准整数类型的存储大小和值范围的细节:

类型 存储大小 值范围
char 1 字节 -128 到 127 或 0 到 255
unsigned char 1 字节 0 到 255
signed char 1 字节 -128 到 127
int 2 或 4 字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int 2 或 4 字节 0 到 65,535 或 0 到 4,294,967,295
short 2 字节 -32,768 到 32,767
unsigned short 2 字节 0 到 65,535
long 4 字节 -2,147,483,648 到 2,147,483,647
unsigned long 4 字节 0 到 4,294,967,295
#include <stdio.h>
#include <limits.h>

int main()
{
  printf("int 存储大小 : %lu \n", sizeof(int));
   //表达式 sizeof(type) 得到对象或类型的存储字节大小。
   //%lu 为 32 位无符号整数
  return 0;
}

输出结果:int 存储大小 : 4
   

浮点类型

下表列出了关于标准浮点类型的存储大小、值范围和精度的细节:

类型 存储大小 值范围 精度
float 4 字节 1.2E-38 到 3.4E+38 6 位有效位
double 8 字节 2.3E-308 到 1.7E+308 15 位有效位
long double 16 字节 3.4E-4932 到 1.1E+4932 19 位有效位
#include <stdio.h>
#include <float.h>
//头文件 float.h 定义了宏,在程序中可以使用这些值和其他有关实数二进制表示的细节。
int main()
{
  printf("float 存储最大字节数 : %lu \n", sizeof(float));
  printf("float 最小值: %E\n", FLT_MIN );
  printf("float 最大值: %E\n", FLT_MAX );
  printf("精度值: %d\n", FLT_DIG );
  //%E 为以指数形式输出单、双精度实数
  return 0;
}

输出结果:
   float 存储最大字节数 : 4
float 最小值: 1.175494E-38
float 最大值: 3.402823E+38
精度值: 6

void 类型

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

序号 类型与描述
1 函数返回为空 C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);
2 函数参数为空 C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);
3 指针指向 void 类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。

c变量与常量

变量其实只不过是程序可操作的存储区的名称。C 中每个变量都有特定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。

变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头。大写字母和小写字母是不同的,因为 C 是大小写敏感的。

有以下几种基本的变量类型:

类型 描述
char 通常是一个字节(八位), 这是一个整数类型。
int 整型,4 个字节,取值范围 -2147483648 到 2147483647。
float 单精度浮点值。单精度是这样的格式,1位符号,8位指数,23位小数。img
double 双精度浮点值。双精度是1位符号,11位指数,52位小数。img
void 表示类型的缺失。

C 中的变量定义

变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。变量定义指定一个数据类型,并包含了该类型的一个或多个变量的列表。

int    i, j, k;
char   c, ch;
float  f, salary;
double d;
//变量列表可以由一个或多个标识符名称组成,多个标识符之间用逗号分隔。

c中的变量初始化

extern int d = 3, f = 5;    // d 和 f 的声明与初始化
int d = 3, f = 5;           // 定义并初始化 d 和 f
byte z = 22;                // 定义并初始化 z
char x = 'x';               // 变量 x 的值为 'x'

注意:不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为 NULL(所有字节的值都是 0),其他所有变量的初始值是未定义的。

c中的变量声明

变量声明向编译器保证变量以指定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。

变量的声明有两种情况:

  • 1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。

  • 2、另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。

  • 除非有extern关键字,否则都是变量的定义。

extern int i; //声明,不是定义
int i; //声明,也是定义

C 中有两种类型的表达式:

  1. 左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。

  2. 右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。

变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。

c常量

常量就像是常规的变量,只不过常量的值在定义后不能进行修改。

整数常量

可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

212         /* 合法的 */
215u        /* 合法的 */
0xFeeL      /* 合法的 */
078         /* 非法的:8 不是八进制的数字 */
032UU       /* 非法的:不能重复后缀 */

浮点常量

浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。

当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。

3.14159       /* 合法的 */
314159E-5L    /* 合法的 */
510E          /* 非法的:不完整的指数 */
210f          /* 非法的:没有小数或指数 */
.e55          /* 非法的:缺少整数或分数 */

字符常量

字符常量是括在单引号中,例如,’x’ 可以存储在 char 类型的简单变量中。

字符常量可以是一个普通的字符(例如 ‘x’)、一个转义序列(例如 ‘\t’),或一个通用的字符(例如 ‘\u02C0’)。

在 C 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码:

转义序列 含义
\ \ 字符
‘ 字符
” 字符
\? ? 字符
\a 警报铃声
\b 退格键
\f 换页符
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\ooo 一到三位的八进制数
\xhh . . . 一个或多个数字的十六进制数
#include <stdio.h>

int main()
{
  printf("Hello\tWorld\n\n");

  return 0;
}


输出结果:Hello   World

字符串常量

字符串字面值或常量是括在双引号 “” 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。

您可以使用空格做分隔符,把一个很长的字符串常量进行分行。

"hello, dear"

"hello, \

dear"

"hello, " "d" "ear"

C存储类

存储类定义 C 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。下面列出 C 程序中可用的存储类:

- auto
//auto 存储类是所有局部变量默认的存储类。
- register
// 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。
- static
//存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。

//static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

//全局声明的一个 static 变量或方法可以被任何函数或方法调用,只要这些方法出现在跟 static 变量或方法同一个文件中。
- extern
//存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

//当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。

//extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候
#include <stdio.h>

/* 函数声明 */
void func1(void);

static int count=10; /* 全局变量 - static 是默认的 */

int main()
{
while (count--) {
func1();
}
return 0;
}

void func1(void)
{
/* 'thingy' 是 'func1' 的局部变量 - 只初始化一次
* 每次调用函数 'func1' 'thingy' 值不会被重置。
*/
static int thingy=5;
thingy++;
printf(" thingy 为 %d , count 为 %d\n", thingy, count);
}


输出结果:
thingy 为 6 , count 为 9
thingy 为 7 , count 为 8
thingy 为 8 , count 为 7
thingy 为 9 , count 为 6
thingy 为 10 , count 为 5
thingy 为 11 , count 为 4
thingy 为 12 , count 为 3
thingy 为 13 , count 为 2
thingy 为 14 , count 为 1
thingy 为 15 , count 为 0

c运算符

运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言内置了丰富的运算符,并提供了以下类型的运算符:

  • 算术运算符

    运算符 描述 实例
    + 把两个操作数相加 A + B 将得到 30
    从第一个操作数中减去第二个操作数 A – B 将得到 -10
    * 把两个操作数相乘 A * B 将得到 200
    / 分子除以分母 B / A 将得到 2
    % 取模运算符,整除后的余数 B % A 将得到 0
    ++ 自增运算符,整数值增加 1 A++ 将得到 11
    自减运算符,整数值减少 1 A– 将得到 9
  • 关系运算符

    运算符 描述 实例
    == 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 为假。
    != 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。
    > 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 为假。
    < 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。
    >= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 为假。
    <= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。
  • 逻辑运算符

    运算符 描述 实例
    && 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 (A && B) 为假。
    || 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 (A || B) 为真。
    ! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 !(A && B) 为真。
  • 位运算符

    位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:

    运算符 描述 实例
    & 按位与操作,按二进制位进行”与”运算。运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1; (A & B) 将得到 12,即为 0000 1100
    | 按位或运算符,按二进制位进行”或”运算。运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1; (A | B) 将得到 61,即为 0011 1101
    ^ 异或运算符,按二进制位进行”异或”运算。运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0; (A ^ B) 将得到 49,即为 0011 0001
    ~ 取反运算符,按二进制位进行”取反”运算。运算规则:~1=-2; ~0=-1; (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。
    << 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 A << 2 将得到 240,即为 1111 0000
    >> 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 A >> 2 将得到 15,即为 0000 1111
  • 赋值运算符

    运算符 描述 实例
    = 简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给 C
    += 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 C += A 相当于 C = C + A
    -= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 C -= A 相当于 C = C – A
    *= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 C *= A 相当于 C = C * A
    /= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 C /= A 相当于 C = C / A
    %= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A
    <<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2
    >>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2
    &= 按位与且赋值运算符 C &= 2 等同于 C = C & 2
    ^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
    |= 按位或且赋值运算符 C |= 2 等同于 C = C | 2
  • 杂项运算符↦ sizeof & 三元

运算符 描述 实例
sizeof() 返回变量的大小。 sizeof(a) 将返回 4,其中 a 是整数。
& 返回变量的地址。 &a; 将给出变量的实际地址。
* 指向一个变量。 *a; 将指向一个变量。
? : 条件表达式 如果条件为真 ? 则值为 X : 否则值为 Y

c中运算符的优先级

类别 运算符 结合性
后缀 () [] -> . ++ – – 从左到右
一元 + – ! ~ ++ – – (type)* & sizeof 从右到左
乘除 * / % 从左到右
加减 + – 从左到右
移位 << >> 从左到右
关系 < <= > >= 从左到右
相等 == != 从左到右
位与 AND & 从左到右
位异或 XOR ^ 从左到右
位或 OR | 从左到右
逻辑与 AND && 从左到右
逻辑或 OR || 从左到右
条件 ?: 从右到左
赋值 = += -= *= /= %=>>= <<= &= ^= |= 从右到左
逗号 , 从左到右
#include <stdio.h>

main()
{
  int a = 20;
  int b = 10;
  int c = 15;
  int d = 5;
  int e;

  e = (a + b) * c / d;      // ( 30 * 15 ) / 5
  printf("(a + b) * c / d 的值是 %d\n",  e );

  e = ((a + b) * c) / d;    // (30 * 15 ) / 5
  printf("((a + b) * c) / d 的值是 %d\n" ,  e );

  e = (a + b) * (c / d);   // (30) * (15/5)
  printf("(a + b) * (c / d) 的值是 %d\n",  e );

  e = a + (b * c) / d;     // 20 + (150/5)
  printf("a + (b * c) / d 的值是 %d\n" ,  e );
 
  return 0;
}

输出结果:
  (a + b) * c / d 的值是 90
((a + b) * c) / d 的值是 90
(a + b) * (c / d) 的值是 90
a + (b * c) / d 的值是 50

c判断

判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。

C 语言把任何非零非空的值假定为 true,把null 假定为 false

判断语句

语句 描述
if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
if…else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
嵌套 if 语句 您可以在一个 ifelse if 语句内使用另一个 ifelse if 语句。
switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。
嵌套 switch 语句 您可以在一个 switch 语句内使用另一个 switch 语句。
#include<stdio.h>

int main()
{
int num;

printf("输入一个数字 : ");
scanf("%d",&num);

(num%2==0)?printf("偶数"):printf("奇数");
//三目运算符
}

c循环

一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。

编程语言提供了更为复杂执行路径的多种控制结构。

循环语句允许我们多次执行一个语句或语句组。

循环类型

循环类型 描述
while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for 循环 多次执行一个语句序列,简化管理循环变量的代码。
do…while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
嵌套循环 您可以在 while、for 或 do..while 循环内使用一个或多个循环。

循环控制语句

循环控制语句改变你代码的执行顺序。通过它你可以实现代码的跳转。

C 提供了下列的循环控制语句。点击链接查看每个语句的细节。

控制语句 描述
break 语句 终止循环switch 语句,程序流将继续执行紧接着循环或 switch 的下一条语句。
continue 语句 告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。
goto 语句 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。
#include <stdio.h>
//无限循环,ctrl+c终止一个无限循环
int main ()
{
  for( ; ; )
  {
     printf("该循环会永远执行下去!\n");
  }
  return 0;
}

c函数

函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。

您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。

函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。

C 标准库提供了大量的程序可以调用的内置函数。例如,函数 strcat() 用来连接两个字符串,函数 memcpy() 用来复制内存到另一个位置。

定义函数

return_type function_name( parameter list )
{
body of the function
}
在 C 语言中,函数由一个函数头和一个函数主体组成。函数的所有组成部分:

返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
函数主体:函数主体包含一组定义函数执行任务的语句。
#include <stdio.h>

/* 函数声明 */
int max(int num1, int num2);

int main ()
{
  /* 局部变量定义 */
  int a = 100;
  int b = 200;
  int ret;

  /* 调用函数来获取最大值 */
  ret = max(a, b);

  printf( "Max value is : %d\n", ret );

  return 0;
}

/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2)
{
  /* 局部变量声明 */
  int result;

  if (num1 > num2)
     result = num1;
  else
     result = num2;

  return result;
}

函数参数

如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数

形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。

当调用函数时,有两种向函数传递参数的方式:

调用类型 描述
传值调用 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
引用调用 通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

默认情况下,C 使用传值调用来传递参数。一般来说,这意味着函数内的代码不能改变用于调用函数的实际参数。

c作用域规则

任何一种编程中,作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。C 语言中有三个地方可以声明变量:

  1. 在函数或块内部的局部变量

  2. 在所有函数外部的全局变量

  3. 形式参数的函数参数定义中

    #include <stdio.h>

    /* 全局变量声明 */
    int a = 20;

    int main ()
    {
    /* 在主函数中的局部变量声明 */
    int a = 10;
    int b = 20;
    int c = 0;
    int sum(int, int);

    printf ("value of a in main() = %d\n", a);
    c = sum( a, b);
    printf ("value of c in main() = %d\n", c);

    return 0;
    }

    /* 添加两个整数的函数 */
    int sum(int a, int b)
    {
    printf ("value of a in sum() = %d\n", a);
    printf ("value of b in sum() = %d\n", b);

    return a + b;
    }

     

全局变量与局部变量在内存中的区别

  • 全局变量保存在内存的全局存储区中,占用静态的存储单元;

  • 局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元

初始化局部变量和全局变量

当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动对其初始化,如下所示:

数据类型 初始化默认值
int 0
char ‘\0’
float 0
double 0
pointer NULL

c数组

C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。

数组的声明并不是声明一个个单独的变量,而是声明一个个单独的变量。

所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。

初始化数组

double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//省略掉了数组的大小,数组的大小则为初始化时元素的个数。
balance[4] = 50.0;
//为数组中某个元素赋值
#include <stdio.h>

int main ()
{
  int n[ 10 ]; /* n 是一个包含 10 个整数的数组 */
  int i,j;

  /* 初始化数组元素 */        
  for ( i = 0; i < 10; i++ )
  {
     n[ i ] = i + 100; /* 设置元素 i 为 i + 100 */
  }
 
  /* 输出数组中每个元素的值 */
  for (j = 0; j < 10; j++ )
  {
     printf("Element[%d] = %d\n", j, n[j] );
  }

  return 0;
}

输出结果:
   Element[0] = 100
Element[1] = 101
Element[2] = 102
Element[3] = 103
Element[4] = 104
Element[5] = 105
Element[6] = 106
Element[7] = 107
Element[8] = 108
Element[9] = 109

c enum(枚举)

定义格式:

enum 枚举名 {枚举元素1,枚举元素2,……};
enum DAY
{
     MON=1, TUE, WED, THU, FRI, SAT, SUN
};
//第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
1、先定义枚举类型,再定义枚举变量
enum DAY
{
     MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
2、定义枚举类型的同时定义枚举变量
enum DAY
{
     MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
3、省略枚举名称,直接定义枚举变量
enum
{
     MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
#include <stdio.h>

enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};

int main()
{
enum DAY day;
day = WED;
printf("%d",day);
return 0;
}
输出结果:3
#include <stdio.h>

enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
int main()
{
// 遍历枚举元素
for (day = MON; day <= SUN; day++) {
printf("枚举元素:%d \n", day);
}
}
输出结果:
枚举元素:1
枚举元素:2
枚举元素:3
枚举元素:4
枚举元素:5
枚举元素:6
枚举元素:7
//将整数转换为枚举
#include <stdio.h>
#include <stdlib.h>

int main()
{

enum day
{
saturday,
sunday,
monday,
tuesday,
wednesday,
thursday,
friday
} workday;

int a = 1;
enum day weekend;
weekend = ( enum day ) a; //类型转换
//weekend = a; //错误
printf("weekend:%d",weekend);
return 0;
}
//输出结果:weekend:1
//枚举在Switch中的使用
#include <stdio.h>
#include <stdlib.h>
int main()
{

   enum color { red=1, green, blue };

   enum  color favorite_color;

   /* 用户输入数字来选择颜色 */
   printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): ");
   scanf("%u", &favorite_color);

   /* 输出结果 */
   switch (favorite_color)
  {
   case red:
       printf("你喜欢的颜色是红色");
       break;
   case green:
       printf("你喜欢的颜色是绿色");
       break;
   case blue:
       printf("你喜欢的颜色是蓝色");
       break;
   default:
       printf("你没有选择你喜欢的颜色");
  }

   return 0;
}

c指针(即地址)

每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。

#include <stdio.h>

int main ()
{
   int a = 10;
   int *p;              // 定义指针变量
   p = &a;

  printf("a 变量的地址: %p\n", p);
  return 0;
}
输出的结果:
   a变量的地址:0x7ffeeaae08d8

指针也就是内存地址,指针变量是用来存放内存地址的变量。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */
#include <stdio.h>

int main ()
{
  int  var = 20;   /* 实际变量的声明 */
  int  *ip;        /* 指针变量的声明 */

  ip = &var;  /* 在指针变量中存储 var 的地址 */

  printf("var 变量的地址: %p\n", &var );

  /* 在指针变量中存储的地址 */
  printf("ip 变量存储的地址: %p\n", ip );

  /* 使用指针访问值 */
  printf("*ip 变量的值: %d\n", *ip );

  return 0;
}
输出结果:
   var 变量的地址: 0x7ffeeef168d8
ip 变量存储的地址: 0x7ffeeef168d8
*ip 变量的值: 20

c中的NULL指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

NULL 指针是一个定义在标准库中的值为零的常量。

#include <stdio.h>

int main ()
{
  int  *ptr = NULL;

  printf("ptr 的地址是 %p\n", ptr );

  return 0;
}
输出结果:
   ptr 的地址是 0x0
   
  注意:如需检查一个空指针,您可以使用 if 语句,如下所示:
if(ptr)     /* 如果 p 非空,则完成 */
if(!ptr)    /* 如果 p 为空,则完成 */

C 指针详解

在 C 中,有很多指针相关的概念,这些概念都很简单,但是都很重要。下面列出了 C 程序员必须清楚的一些与指针相关的重要概念:

概念 描述
指针的算术运算 可以对指针进行四种算术运算:++、–、+、-
指针数组 可以定义用来存储指针的数组。
指向指针的指针 C 允许指向指针的指针。
传递指针给函数 通过引用或地址传递参数,使传递的参数在调用函数中被改变。
从函数返回指针 C 允许函数返回指针到局部变量、静态变量和动态内存分配。

c函数指针与回调函数

函数指针是指向函数的指针变量。

通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

函数指针可以像一般函数一样,用于调用函数、传递参数。

#include <stdio.h>

int max(int x, int y)
{
   return x > y ? x : y;
}

int main(void)
{
   /* p 是函数指针 */
   int (* p)(int, int) = & max; // &可以省略
   int a, b, c, d;

   printf("请输入三个数字:");
   scanf("%d %d %d", & a, & b, & c);

   /* 与直接调用函数等价,d = max(max(a, b), c) */
   d = p(p(a, b), c);

   printf("最大的数字是: %d\n", d);

   return 0;
}
输出结果:
   请输入三个数字:1 2 3
最大的数字是: 3

回调函数

函数指针作为某个函数的参数

函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。

简单讲:回调函数是由别人的函数执行时调用你实现的函数。

你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
#include <stdlib.h>  
#include <stdio.h>

// 回调函数
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
   for (size_t i=0; i<arraySize; i++)
       array[i] = getNextValue();
}

// 获取随机值
int getNextRandomValue(void)
{
   return rand();
}

int main(void)
{
   int myarray[10];
   /* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
   populate_array(myarray, 10, getNextRandomValue);
   for(int i = 0; i < 10; i++) {
       printf("%d ", myarray[i]);
  }
   printf("\n");
   return 0;
}
输出结果:
   16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709

c字符串

在 C 语言中,字符串实际上是使用 null 字符 \0 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。

char site[4] = {'R', 'U', 'N', '\0'};
//两句是等价的
char site[] = "RUN";

C 中有大量操作字符串的函数:

序号 函数 & 目的
1 strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
2 strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。
3 strlen(s1); 返回字符串 s1 的长度。
4 strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
5 strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
6 strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
#include <stdio.h>
#include <string.h>

int main ()
{
  char str1[14] = "runoob";
  char str2[14] = "google";
  char str3[14];
  int  len ;

  /* 复制 str1 到 str3 */
  strcpy(str3, str1);
  printf("strcpy( str3, str1) : %s\n", str3 );

  /* 连接 str1 和 str2 */
  strcat( str1, str2);
  printf("strcat( str1, str2):   %s\n", str1 );

  /* 连接后,str1 的总长度 */
  len = strlen(str1);
  printf("strlen(str1) : %d\n", len );

  return 0;
}

输出结果:
   strcpy( str3, str1) :  runoob
strcat( str1, str2):   runoobgoogle
strlen(str1) :  12

c结构体

C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。

定义结构

struct tag { 
   member-list
   member-list
   member-list  
  ...
} variable-list ;
//tag 是结构体标签。

//member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。

//variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。

在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。

//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct
{
   int a;
   char b;
   double c;
} s1;

//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
   int a;
   char b;
   double c;
};
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
struct SIMPLE t1, t2[20], *t3;

//也可以用typedef创建新类型
typedef struct
{
   int a;
   char b;
   double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;

结构体变量的初始化

#include <stdio.h>

struct Books
{
  char  title[50];
  char  author[50];
  char  subject[100];
  int   book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};

int main()
{
   printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}
输出结果:
   title : C 语言
author: RUNOOB
subject: 编程语言
book_id: 123456

访问结构成员

为了访问结构的成员,使用成员访问运算符(.)

#include <stdio.h>
#include <string.h>

struct Books
{
  char  title[50];
  char  author[50];
  char  subject[100];
  int   book_id;
};

int main( )
{
  struct Books Book1;        /* 声明 Book1,类型为 Books */
  struct Books Book2;        /* 声明 Book2,类型为 Books */

  /* Book1 详述 */
  strcpy( Book1.title, "C Programming");
  strcpy( Book1.author, "Nuha Ali");
  strcpy( Book1.subject, "C Programming Tutorial");
  Book1.book_id = 6495407;

  /* Book2 详述 */
  strcpy( Book2.title, "Telecom Billing");
  strcpy( Book2.author, "Zara Ali");
  strcpy( Book2.subject, "Telecom Billing Tutorial");
  Book2.book_id = 6495700;

  /* 输出 Book1 信息 */
  printf( "Book 1 title : %s\n", Book1.title);
  printf( "Book 1 author : %s\n", Book1.author);
  printf( "Book 1 subject : %s\n", Book1.subject);
  printf( "Book 1 book_id : %d\n", Book1.book_id);

  /* 输出 Book2 信息 */
  printf( "Book 2 title : %s\n", Book2.title);
  printf( "Book 2 author : %s\n", Book2.author);
  printf( "Book 2 subject : %s\n", Book2.subject);
  printf( "Book 2 book_id : %d\n", Book2.book_id);

  return 0;
}
输出结果:
  Book 1 title : C Programming
Book 1 author : Nuha Ali
Book 1 subject : C Programming Tutorial
Book 1 book_id : 6495407
Book 2 title : Telecom Billing
Book 2 author : Zara Ali
Book 2 subject : Telecom Billing Tutorial
Book 2 book_id : 6495700

结构作为函数参数

把结构作为函数参数,传参方式与其他类型的变量或指针类似

#include <stdio.h>
#include <string.h>

struct Books
{
  char  title[50];
  char  author[50];
  char  subject[100];
  int   book_id;
};

/* 函数声明 */
void printBook( struct Books book );
int main( )
{
  struct Books Book1;        /* 声明 Book1,类型为 Books */
  struct Books Book2;        /* 声明 Book2,类型为 Books */

  /* Book1 详述 */
  strcpy( Book1.title, "C Programming");
  strcpy( Book1.author, "Nuha Ali");
  strcpy( Book1.subject, "C Programming Tutorial");
  Book1.book_id = 6495407;

  /* Book2 详述 */
  strcpy( Book2.title, "Telecom Billing");
  strcpy( Book2.author, "Zara Ali");
  strcpy( Book2.subject, "Telecom Billing Tutorial");
  Book2.book_id = 6495700;

  /* 输出 Book1 信息 */
  printBook( Book1 );

  /* 输出 Book2 信息 */
  printBook( Book2 );

  return 0;
}
void printBook( struct Books book )
{
  printf( "Book title : %s\n", book.title);
  printf( "Book author : %s\n", book.author);
  printf( "Book subject : %s\n", book.subject);
  printf( "Book book_id : %d\n", book.book_id);
}
输出结果:
   Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
Book subject : Telecom Billing Tutorial
Book book_id : 6495700

指向结构的指针

定义指向结构的指针,方式与定义指向其他类型变量的指针相似

为了使用指向该结构的指针访问结构的成员,必须使用 -> 运算符

#include <stdio.h>
#include <string.h>

struct Books
{
  char  title[50];
  char  author[50];
  char  subject[100];
  int   book_id;
};

/* 函数声明 */
void printBook( struct Books *book );
int main( )
{
  struct Books Book1;        /* 声明 Book1,类型为 Books */
  struct Books Book2;        /* 声明 Book2,类型为 Books */

  /* Book1 详述 */
  strcpy( Book1.title, "C Programming");
  strcpy( Book1.author, "Nuha Ali");
  strcpy( Book1.subject, "C Programming Tutorial");
  Book1.book_id = 6495407;

  /* Book2 详述 */
  strcpy( Book2.title, "Telecom Billing");
  strcpy( Book2.author, "Zara Ali");
  strcpy( Book2.subject, "Telecom Billing Tutorial");
  Book2.book_id = 6495700;

  /* 通过传 Book1 的地址来输出 Book1 信息 */
  printBook( &Book1 );

  /* 通过传 Book2 的地址来输出 Book2 信息 */
  printBook( &Book2 );

  return 0;
}
void printBook( struct Books *book )
{
  printf( "Book title : %s\n", book->title);
  printf( "Book author : %s\n", book->author);
  printf( "Book subject : %s\n", book->subject);
  printf( "Book book_id : %d\n", book->book_id);
}
输出结果:
   Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
Book subject : Telecom Billing Tutorial
Book book_id : 6495700

c共用体

共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

定义共用体

为了定义共用体,必须使用 union 语句,方式与定义结构类似。union 语句定义了一个新的数据类型,带有多个成员。

union [union tag]
{
  member definition;
  member definition;
  ...
  member definition;
} [one or more union variables];
//union tag 是可选的,每个 member definition 是标准的变量定义,比如 int i; 或者 float f; 或者其他有效的变量定义。在共用体定义的末尾,最后一个分号之前,可以指定一个或多个共用体变量,这是可选的。
#include <stdio.h>
#include <string.h>

union Data
{
  int i;
  float f;
  char  str[20];
};

int main( )
{
  union Data data;        

  printf( "Memory size occupied by data : %d\n", sizeof(data));

  return 0;
}
输出结果:
   Memory size occupied by data : 20

访问共用体成员

为了访问共用体的成员,使用成员访问运算符(.)

#include <stdio.h>
#include <string.h>

union Data
{
  int i;
  float f;
  char  str[20];
};

int main( )
{
  union Data data;        

  data.i = 10;
  printf( "data.i : %d\n", data.i);
 
  data.f = 220.5;
  printf( "data.f : %f\n", data.f);
 
  strcpy( data.str, "C Programming");
  printf( "data.str : %s\n", data.str);

  return 0;
}
输出结果:
   data.i : 10
data.f : 220.500000
data.str : C Programming

c位域(选看-难)

#include <stdio.h>
#include <string.h>

/* 定义简单的结构 */
struct
{
 unsigned int widthValidated;
 unsigned int heightValidated;
} status1;

/* 定义位域结构 */
struct
{
 unsigned int widthValidated : 1;
 unsigned int heightValidated : 1;
} status2;

int main( )
{
  printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
  printf( "Memory size occupied by status2 : %d\n", sizeof(status2));

  return 0;
}
输出结果:
   Memory size occupied by status1 : 8
Memory size occupied by status2 : 4

位域声明

有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构,称为“位域”或”位段”

所谓”位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

位域的定义和位域变量的说明

位域定义与结构定义相仿,其形式为:

struct 位域结构名 
{

位域列表

};

其中位域列表的形式为:

type [member_name] : width ;

下面是有关位域中变量元素的描述:

元素 描述
type 只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。
member_name 位域的名称。
width 位域中位的数量。宽度必须小于或等于指定类型的位宽度。
#include <stdio.h>
#include <string.h>

struct
{
 unsigned int age : 3;
} Age;

int main( )
{
  Age.age = 4;
  printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
  printf( "Age.age : %d\n", Age.age );

  Age.age = 7;
  printf( "Age.age : %d\n", Age.age );

  Age.age = 8; // 二进制表示为 1000 有四位,超出
  printf( "Age.age : %d\n", Age.age );

  return 0;
}
输出结果:
   Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0

位域的使用

位域的使用和结构成员的使用相同,其一般形式为:

位域变量名.位域名
位域变量名->位域名
#include<stdio.h>
#include <string.h>

int main(){
   struct bs{
       unsigned a:1;
       unsigned b:3;
       unsigned c:4;
  } bit,*pbit;
   bit.a=1;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
   bit.b=7;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
   bit.c=15;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
   printf("%d,%d,%d\n",bit.a,bit.b,bit.c);    /* 以整型量格式输出三个域的内容 */
   pbit=&bit;    /* 把位域变量 bit 的地址送给指针变量 pbit */
   pbit->a=0;    /* 用指针方式给位域 a 重新赋值,赋为 0 */
   pbit->b&=3;    /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
   pbit->c|=1;    /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
   printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);    /* 用指针方式输出了这三个域的值 */
}

c typedef

C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。

#include <stdio.h>
#include <string.h>

typedef struct Books
{
  char  title[50];
  char  author[50];
  char  subject[100];
  int   book_id;
} 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;
}
输出结果:
   书标题 : C 教程
书作者 : Runoob
书类目 : 编程语言
ID : 12345

typedef vs #define

#define 是 C 指令,用于为各种数据类型定义别名,与 typedef 类似,但是它们有以下几点不同:

  • typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。

  • typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。

    #include <stdio.h>

    #define TRUE 1
    #define FALSE 0

    int main( )
    {
      printf( "TRUE 的值: %d\n", TRUE);
      printf( "FALSE 的值: %d\n", FALSE);

      return 0;
    }
    输出结果:
       TRUE 的值: 1
    FALSE 的值: 0

c 输入&输出

当我们提到输入时,这意味着要向程序填充一些数据。输入可以是以文件的形式或从命令行中进行。C 语言提供了一系列内置的函数来读取给定的输入,并根据需要填充到程序中。

当我们提到输出时,这意味着要在屏幕上、打印机上或任意文件中显示一些数据。C 语言提供了一系列内置的函数来输出数据到计算机屏幕上和保存数据到文本文件或二进制文件中。

标准文件

C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。以下三个文件会在程序执行时自动打开,以便访问键盘和屏幕。

标准文件 文件指针 设备
标准输入 stdin 键盘
标准输出 stdout 屏幕
标准错误 stderr 您的屏幕

文件指针是访问文件的方式,本节将讲解如何从屏幕读取值以及如何把结果输出到屏幕上。

C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。

scanf() 函数用于从标准输入(键盘)读取并格式化, printf() 函数发送格式化输出到标准输出(屏幕)。

#include <stdio.h>
int main( ) {

  char str[100];
  int i;

  printf( "Enter a value :");
  scanf("%s %d", str, &i);

  printf( "\nYou entered: %s %d ", str, i);
  printf("\n");
  return 0;
}
//scanf() 在读取字符串时,只要遇到一个空格,scanf() 就会停止读取

gets() & puts() 函数

char *gets(char *s) 函数从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF。

int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout

#include <stdio.h>

int main( )
{
  char str[100];

  printf( "Enter a value :");
  gets( str );

  printf( "\nYou entered: ");
  puts( str );
  return 0;
}
//当上面的代码被编译和执行时,它会等待您输入一些文本,当您输入一个文本并按下回车键时,程序会继续并读取一整行直到该行结束

getchar() & putchar() 函数

#include <stdio.h>

int main( )
{
  int c;

  printf( "Enter a value :");
  c = getchar( );

  printf( "\nYou entered: ");
  putchar( c );
  printf( "\n");
  return 0;
}
//输入一个文本并按下回车键时,程序会继续并只会读取一个单一的字符

c 文件读写(难)

一个文件,无论它是文本文件还是二进制文件,都是代表了一系列的字节。C 语言不仅提供了访问顶层的函数,也提供了底层(OS)调用来处理存储设备上的文件。

打开文件

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

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

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

模式 描述
r 打开一个已有的文本文件,允许读取文件。
w 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。
a 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+ 打开一个文本文件,允许读写文件。
w+ 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+ 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。

关闭文件

为了关闭文件,请使用 fclose( ) 函数。函数的原型如下:

 int fclose( FILE *fp );


//如果成功关闭文件,fclose( ) 函数返回零,如果关闭文件时发生错误,函数返回 EOF。这个函数实际上,会清空缓冲区中的数据,关闭文件,并释放用于该文件的所有内存。EOF 是一个定义在头文件 stdio.h 中的常量。

//C 标准库提供了各种函数来按字符或者以固定长度字符串的形式读写文件。

写入文件

int fputc( int c, FILE *fp );

//注意:请确保您有可用的 tmp 目录,如果不存在该目录,则需要在您的计算机上先创建该目录。

/tmp 一般是 Linux 系统上的临时目录,如果你在 Windows 系统上运行,则需要修改为本地环境中已存在的目录,例如: C:\tmp、D:\tmp等。
#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);
}

读取文件

int fgetc( FILE * fp );

//fgetc() 函数从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF。

函数允许您从流中读取一个字符串:

char *fgets( char *buf, int n, FILE *fp );

函数 fgets() 从 fp 所指向的输入流中读取 n – 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。

如果这个函数在读取最后一个字符之前就遇到一个换行符 ‘\n’ 或文件的末尾 EOF,则只会返回读取到的字符,包括换行符。您也可以使用 int fscanf(FILE *fp, const char *format, …) 函数来从文件中读取字符串,但是在遇到第一个空格和换行符时,它会停止读取。

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

}
//分析:首先,fscanf() 方法只读取了 This,因为它在后边遇到了一个空格。其次,调用 fgets() 读取剩余的部分,直到行尾。最后,调用 fgets() 完整地读取第二行。
输出结果:
   1: This
2: is testing for fprintf...
3: This is testing for fputs...

c 预处理器

C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP

所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。下面列出了所有重要的预处理器指令:

指令 描述
#define 定义宏
#include 包含一个源代码文件
#undef 取消已定义的宏
#ifdef 如果宏已经定义,则返回真
#ifndef 如果宏没有定义,则返回真
#if 如果给定条件为真,则编译下面代码
#else #if 的替代方案
#elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个 #if……#else 条件编译块
#error 当遇到标准错误时,输出错误消息
#pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

预处理器运算符(难)

C 预处理器提供了下列的运算符来帮助您创建宏:

宏延续运算符(\)

一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)。例如:

#define  message_for(a, b)  \
   printf(#a " and " #b ": We love you!\n")
字符串常量化运算符(#)

在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数或参数列表。例如:

#include <stdio.h>

#define message_for(a, b) \
   printf(#a " and " #b ": We love you!\n")

int main(void)
{
  message_for(Carole, Debra);
  return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Carole and Debra: We love you!
标记粘贴运算符(##)

宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:

#include <stdio.h>

#define tokenpaster(n) printf ("token" #n " = %d", token##n)

int main(void)
{
  int token34 = 40;
 
  tokenpaster(34);
  return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

token34 = 40

这是怎么发生的,因为这个实例会从编译器产生下列的实际输出:

printf ("token34 = %d", token34);

这个实例演示了 token##n 会连接到 token34 中,在这里,我们使用了字符串常量化运算符(#)标记粘贴运算符(##)

defined() 运算符

预处理器 defined 运算符是用在常量表达式中的,用来确定一个标识符是否已经使用 #define 定义过。如果指定的标识符已定义,则值为真(非零)。如果指定的标识符未定义,则值为假(零)。下面的实例演示了 defined() 运算符的用法:

#include <stdio.h>

#if !defined (MESSAGE)
  #define MESSAGE "You wish!"
#endif

int main(void)
{
  printf("Here is the message: %s\n", MESSAGE);  
  return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Here is the message: You wish!

参数化的宏

CPP 一个强大的功能是可以使用参数化的宏来模拟函数。例如,下面的代码是计算一个数的平方:

int square(int x) {
  return x * x;
}

我们可以使用宏重写上面的代码,如下:

#define square(x) ((x) * (x))

在使用带有参数的宏之前,必须使用 #define 指令定义。参数列表是括在圆括号内,且必须紧跟在宏名称的后边。宏名称和左圆括号之间不允许有空格。例如:

#include <stdio.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void)
{
  printf("Max between 20 and 10 is %d\n", MAX(10, 20));  
  return 0;
}
输出结果:
   Max between 20 and 10 is 20

c 头文件

头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。

在程序中要使用头文件,需要使用 C 预处理指令 #include 来引用它。前面我们已经看过 stdio.h 头文件,它是编译器自带的头文件。

引用头文件相当于复制头文件的内容,但是我们不会直接在源文件中复制头文件的内容,因为这么做很容易出错,特别在程序是由多个源文件组成的时候。

引用头文件的语法

#include <file>
//用于引用系统头文件。它在系统目录的标准列表中搜索名为 file 的文件。
#include "file"====速度快
//用于引用用户头文件。它在包含当前文件的目录中搜索名为 file 的文件。

#include < > 引用的是编译器的类库路径里面的头文件。

#include ” “ 引用的是你程序目录的相对路径中的头文件,如果在程序目录没有找到引用的头文件则到编译器的类库路径的目录下找该头文件。

引用头文件的操作(未做)

c强制类型转换

变量的根本类型并未改变,只是输出形式暂时改变了而已。

强制类型转换是把变量从一种类型转换为另一种数据类型。例如,如果您想存储一个 long 类型的值到一个简单的整型中,需要把 long 类型强制转换为 int 类型。可以使用强制类型转换运算符来把值显式地从一种类型转换为另一种类型。

#include <stdio.h>

int main()
{
  int sum = 17, count = 5;
  double mean;

  mean = (double) sum / count;
  printf("Value of mean : %f\n", mean );

}
输出结果:
   Value of mean : 3.400000

常用的算术转换是隐式地把值强制转换为相同的类型。编译器首先执行整数提升,如果操作数类型不同,则它们会被转换为下列层次中出现的最高层次的类型:

算术转换

#include <stdio.h>

int main()
{
  int  i = 17;
  char c = 'c'; /* ascii 值是 99 */
  float sum;

  sum = i + c;
  printf("Value of sum : %f\n", sum );

}

输出结果:
   Value of sum : 116.000000
       
       
//算术转换不适用于赋值运算符、逻辑运算符 && 和 ||。

c错误处理

C 语言不提供对错误处理的直接支持,但是作为一种系统编程语言,它以返回值的形式允许您访问底层数据。在发生错误时,大多数的 C 或 UNIX 函数调用返回 1 或 NULL,同时会设置一个错误代码 errno,该错误代码是全局变量,表示在函数调用期间发生了错误。

为什么最后要加一句return 0

C 程序员可以通过检查返回值,然后根据返回值决定采取哪种适当的动作。开发人员应该在程序初始化时,把 errno 设置为 0,这是一种良好的编程习惯。0 值表示程序中没有错误。

errno、perror() 和 strerror()

C 语言提供了 perror()strerror() 函数来显示与 errno 相关的文本消息。

  • perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。

  • strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。

//尝试打开一个文件不存在
#include <stdio.h>
#include <errno.h>
#include <string.h>

extern int errno ;

int main ()
{
  FILE * pf;
  int errnum;
  pf = fopen ("unexist.txt", "rb");
  if (pf == NULL)
  {
     errnum = errno;
     fprintf(stderr, "错误号: %d\n", errno);
     perror("通过 perror 输出错误");
     fprintf(stderr, "打开文件错误: %s\n", strerror( errnum ));
  }
  else
  {
     fclose (pf);
  }
  return 0;
}
输出结果:
   错误号: 2
通过 perror 输出错误: No such file or directory
打开文件错误: No such file or directory
//被除数为0的错误
#include <stdio.h>
#include <stdlib.h>

main()
{
  int dividend = 20;
  int divisor = 0;
  int quotient;

  if( divisor == 0){
     fprintf(stderr, "除数为 0 退出运行...\n");
     exit(-1);
  }
  quotient = dividend / divisor;
  fprintf(stderr, "quotient 变量的值为 : %d\n", quotient );

  exit(0);
}
//exit(0);等价于renturn 0;但是exit执行的速度快,因为exit函数内置在errno库里
输出结果:
   除数为 0 退出运行...
通常情况下,程序成功执行完一个操作正常退出的时候会带有值 EXIT_SUCCESS。在这里,EXIT_SUCCESS 是宏,它被定义为 0

如果程序中存在一种错误情况,当您退出程序时,会带有状态值 EXIT_FAILURE,被定义为 -1

#include <stdio.h>
#include <stdlib.h>

main()
{
  int dividend = 20;
  int divisor = 5;
  int quotient;

  if( divisor == 0){
     fprintf(stderr, "除数为 0 退出运行...\n");
     exit(EXIT_FAILURE);
  }
  quotient = dividend / divisor;
  fprintf(stderr, "quotient 变量的值为: %d\n", quotient );

  exit(EXIT_SUCCESS);
}

输出结果:
   quotient 变量的值为 : 4

c递归

理解:函数递归就是其函数本身进行的循环。

递归指的是在函数的定义中使用函数自身的方法。

语法格式

void recursion()
{
  statements;
  ... ... ...
  recursion(); /* 函数调用自身 */
  ... ... ...
}

int main()
{
  recursion();
}
//阶乘
#include <stdio.h>

double factorial(unsigned int i)
{
  if(i <= 1)
  {
     return 1;
  }
  return i * factorial(i - 1);
}
int  main()
{
   int i = 5;
   printf("%d 的阶乘为 %f\n", i, factorial(i));
   return 0;
}

输出结果:
   5 的阶乘为 120.000000
//斐波那契数列==兔子问题
#include <stdio.h>

int fibonaci(int i)
{
  if(i == 0)
  {
     return 0;
  }
  if(i == 1)
  {
     return 1;
  }
  return fibonaci(i-1) + fibonaci(i-2);
}

int  main()
{
   int i;
   for (i = 0; i < 10; i++)
  {
      printf("%d\t\n", fibonaci(i));
  }
   return 0;
}

c可变参数==慢慢理解

#include <stdio.h>
#include <stdarg.h>

double average(int num,...)
{

   va_list valist;
   double sum = 0.0;
   int i;

   /* 为 num 个参数初始化 valist */
   va_start(valist, num);

   /* 访问所有赋给 valist 的参数 */
   for (i = 0; i < num; i++)
  {
      sum += va_arg(valist, int);
  }
   /* 清理为 valist 保留的内存 */
   va_end(valist);

   return sum/num;
}

int main()
{
  printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
  printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}

//当上面的代码被编译和执行时,它会产生下列结果。应该指出的是,函数 average() 被调用两次,每次第一个参数都是表示被传的可变参数的总数。省略号被用来传递可变数量的参数。

输出结果:
   Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000

c内存管理

C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。

序号 函数和描述
1 void *calloc(int num, int size); 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。
2 void free(void *address); 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
3 void *malloc(int num); 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
4 void *realloc(void *address, int newsize); 该函数重新分配内存,把内存扩展到 newsize

注意:void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。

//如果预先不知道需要存储的文本长度,需要定义一个指针,该指针指向未定义所需内存大小的字符,后续再根据需求来分配内存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
  char name[100];
  char *description;

  strcpy(name, "Zara Ali");

  /* 动态分配内存 */
  description = (char *)malloc( 200 * sizeof(char) ); //malloc替换为calloc也可
    //calloc(200, sizeof(char));
  if( description == NULL )
  {
     fprintf(stderr, "Error - unable to allocate required memory\n");
  }
  else
  {
     strcpy( description, "Zara ali a DPS student in class 10th");
  }
  printf("Name = %s\n", name );
  printf("Description: %s\n", description );
}

输出结果:
   Name = Zara Ali
Description: Zara ali a DPS student in class 10th

重新调整内存的大小和释放内存

当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议您在不需要内存时,都应该调用函数 free() 来释放内存。

或者,您可以通过调用函数 realloc() 来增加或减少已分配的内存块的大小。让我们使用 realloc() 和 free() 函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
  char name[100];
  char *description;

  strcpy(name, "Zara Ali");

  /* 动态分配内存 */
  description = (char *)malloc( 30 * sizeof(char) );
  if( description == NULL )
  {
     fprintf(stderr, "Error - unable to allocate required memory\n");
  }
  else
  {
     strcpy( description, "Zara ali a DPS student.");
  }
  /* 假设您想要存储更大的描述信息 */
  description = (char *) realloc( description, 100 * sizeof(char) );
  if( description == NULL )
  {
     fprintf(stderr, "Error - unable to allocate required memory\n");
  }
  else
  {
     strcat( description, "She is in class 10th");
  }
 
  printf("Name = %s\n", name );
  printf("Description: %s\n", description );

  /* 使用 free() 函数释放内存 */
  free(description);
}

输出结果:
   Name = Zara Ali
Description: Zara ali a DPS student.She is in class 10th

c排序算法

1、冒泡法

冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。

#include <stdio.h>
void main(){
int i,j,t,a[10];                   //定义变量数组基本类型
printf("Please input numbers: ");
for(i=0;i<10;i++){
scanf("%d",&a[i]);             //从键盘输入要排序的数组
}
for(i=0;i<10;i++){
for(j=i+1;j<=9;j++){
if(a[i]>a[j]){            //如果后一个数比前一个数大利用中间变量t实现俩值互换
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
}
printf("Please output numbers: ");
for(i=0;i<=9;i++){
printf("%-3d",a[i]);
}
printf("\n");
}

2、选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

#include<stdio.h>
#include<stdlib.h>

void sort_min(int arr[], int length);

void sort_min(int arr[], int length)
{
   int i, j;
   int temp;

   for(i=0; i<length-1; i++)
  {
       
       for(j=i+1, j<length; j++)
      {
           if(arr[i]>arr[j])
              {
                   // 将较小的数放到索引i处,一个循环后,就i处就为该循环的最小值
                   temp = arr[j];  
                   arr[j] = arr[i];
                   arr[i] = temp;
              }
      }
  }
}

int  main()
{
   int arr_num[7] = {55, 17, 11, 65, 32, 9, 25};
   sort_min(arr_num, 7);
   for(int i=0; i<7; i++)
       printf("%d ", arr_num[i]);
   printf("\n");
   system("pause");
   return 0;
}

3、插入排序

插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到 {\displaystyle O(1)} {\displaystyle O(1)}的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

//插入排序(从小到大) 
#include<stdio.h>
int number[100000000];    
int main()
{
   int i=0,n,ii=0,temp=0;
   printf("输入数字个数:\n");    
   scanf("%d",&n);      
   printf("输入%d个数:\n",n);
   for(int j=0;j<n;j++)      
       scanf("%d",&number[j]) ;
   for(i=1;i<n;i++)  
  {
       temp=number[i];
       ii=i-1;  
       while(ii>=0&&temp<number[ii])   //在这里改顺序 !!!
      {
           number[ii+1]=number[ii];  
           ii--;
      }
       number[ii+1]=temp;  
  }                
   for(i=0;i<n-1;i++)    
       printf("%d ",number[i]);  
   printf("%d\n",number[i]);
   return 0;
}

4、希尔排序

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率

  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

#include <stdio.h>
#include <malloc.h>

void shellSort(int *a, int len); // 函数声明

int main(void)
{
   int i, len, * a;
   printf("请输入要排的数的个数:");
   scanf("%d",&len);
   a = (int *)malloc(len * sizeof(int)); // 动态定义数组
   printf("请输入要排的数:\n");
   for (i = 0; i < len; i++) { // 数组值的输入
       scanf("%d",&a[i]);
  }  
   shellSort(a, len); // 调用希尔排序函数
   printf("希尔升序排列后结果为:\n");
   for (i = 0; i < len; i++) { // 排序后的结果的输出
       printf("%d\t",a[i]);
  }
   printf("\n");

   return 0;
}

void shellSort(int *a, int len)
{
   int i, j, k, tmp, gap;  // gap 为步长
   for (gap = len / 2; gap > 0; gap /= 2) {  // 步长初始化为数组长度的一半,每次遍历后步长减半,
  for (i = 0; i < gap; ++i) { // 变量 i 为每次分组的第一个元素下标
       for (j = i + gap; j < len; j += gap) { //对步长为gap的元素进行直插排序,当gap为1时,就是直插排序
           tmp = a[j];  // 备份a[j]的值
           k = j - gap;  // j初始化为i的前一个元素(与i相差gap长度)
           while (k >= 0 && a[k] > tmp) {
               a[k + gap] = a[k]; // 将在a[i]前且比tmp的值大的元素向后移动一位
               k -= gap;
          }
           a[k + gap] = tmp;
      }
  }
  }
}

6、归并排序

//归并排序(从小到大) 
#include <stdio.h>
int a[3001000];   //在主函数外定义数组
int c[3001000];
void merge_sort(int left,int right)   //定义归并函数"merge_sort"
{
   if ( left == right ) return;   //判断是否只有一个数
   int mid = ( left + right ) / 2;   //取一个中间数
   merge_sort(left, mid);      //这两行将输入的数列强行二分
   merge_sort(mid + 1,right);
   int i = left;   //把开始和中间的值保存在别的变量中
   int j = mid + 1;
   int len = 0;
   while (i <= mid && j <= right)    //在范围内判断前后两数的大小
  {
       if (a[i] < a[j])    //判断大小 大到小"<",小到大">"!!!
      {
           c[len] = a[i];    //如果条件成立(这里是后数比前数小)把后面的值赋到前面
           len++;   //表示判断过一遍
           i++;    //当i与下面的j其中有一个不满足上面while后的条件则跳出循环,表示排序完成
      }
       else
      {
           c[len] = a[j];  //不成立就不变
           len++;
           j++;
      }
  }
   for (;i<=mid;i++)    //下面几个for循环把排序好的数记录下来
  {
       c[len] = a[i];      
       len++;           //挨个赋值
  }
   for (;j<=right;j++)
  {
       c[len] = a[j];
       len++;
  }
   for (int ii = left; ii <= right ;ii++)
       a[ii] = c[ii - left];
}


int main()    //主函数
{
   int n;
   printf("输入数字个数:\n");
   scanf("%d",&n);   //输入要排序的数字个数
   printf("输入%d个数:\n",n);
   for (int i = 0 ; i < n ; i++)   //循环输入
       scanf("%d",&a[i]);
   merge_sort(0,n-1);   //调用归并排序函数"merge_sort"
   for (int i = 0 ; i < n ; i++)   //循环输出
  {
       if(i!=0)     //第一个数前面不加空格
       printf(" ");
       printf("%d",a[i]);
  }
   return 0;
}
//ENDING

7、快速排序

//快速排序(我把它做成了一个小程序,复制下来可以直接运行) 
//感谢百度百科,本程序借鉴了一下
#include<stdio.h>
#include<string.h>    //字符串头文件
int a[1000001];     //在主函数外面定义数组可以大一点(很多很多)
void kuaisu_sheng(int left,int right)
{
   if(left>=right)  //如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了
       return ;
   int i=left;     //将区间记录下来
   int j=right;
   int key=a[i];    //记录参考值
   while(i<j)   //控制在当组内寻找一遍
  {
       while(i<j&&key<=a[j])   //而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升序还是降序)2,没有符合条件1的,并且i与j的大小没有反转
           j--;    //向前寻找
       a[i]=a[j];     //找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是a[left],那么就是给key)
       while(i<j&&key>=a[i])   //这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反
           i++;
       a[j]=a[i];
  }
   a[i]=key;                  //当在当组内找完一遍以后就把中间数key回归
   kuaisu_sheng(left,i-1);     //最后用同样的方式对分出来的左边的小组进行同上的做法
   kuaisu_sheng(i+1,right);    //用同样的方式对分出来的右边的小组进行同上的做法
                              //当然最后可能会出现很多分左右,直到每一组的i = j 为止
}
void kuaisu_jiang(int left,int right)
{
   if(left>=right)
       return ;
   int i=left;
   int j=right;
   int key=a[i];
   while(i<j)  
  {
       while(i<j&&key>=a[j])   //顺序这里改
           j--;    
       a[i]=a[j];    
       while(i<j&&key<=a[i])   //还有这里(不清楚怎么改可以对照上面升序的函数)
       i++;
       a[j]=a[i];
  }
   a[i]=key;              
   kuaisu_jiang(left,i-1);
   kuaisu_jiang(i+1,right);
}
int main()
{
   int n,m,i,choice;
   printf("快速排序小程序(By STY)\n\n");
   printf("升序还是降序\n");
   printf("升序选“1 ”\n");
   printf("降序选“2 ”\n");
   printf("请输入:(1~2)\n");
   printf("退出请按“Ctrl+Z ”(在键盘上),并按回车,谢谢使用!\n");
   while(scanf("%d",&choice)!=EOF)
  {
       memset(a,0,sizeof(a));    //清空数组
       printf("请输入要排序的数字个数:\n");
       scanf("%d",&n);
       printf("请输入%d个数字:\n",n);
       if(choice==1)
      {
           for(int ii=0;ii<n;ii++)
               scanf("%d",&a[ii]);
           kuaisu_sheng(0,n);    //引用函数
           printf("结果:\n");
           for(i=1;i<n;i++)      //循环输出
               printf("%d ",a[i]);
           printf("%d(升序)\n\n\n\n",a[i]);
      }
       else if(choice==2)
      {
           for(int ii=0;ii<n;ii++)
               scanf("%d",&a[ii]);
           kuaisu_jiang(0,n);
           printf("结果:\n");
           for(i=0;i<n-1;i++)
               printf("%d ",a[i]);
           printf("%d(降序)\n\n\n\n",a[i]);
      }
       else
           printf("开玩笑吧!\n");

       printf("快速排序小程序(By STY)\n\n");
       printf("升序还是降序\n");
       printf("升序选“1”\n");
       printf("降序选“2”\n");
       printf("请输入:(1~2)\n");
       printf("退出请按“Ctrl+Z”(在键盘上),并按回车,谢谢使用!\n");      
  }
   return 0;  
}
//ENDING

在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。