C语言入门这一篇就够了!!!-万字长文(前半部分)


前言

这是我学习c语言中进行的笔记总结,每一节都有我的思想在其中,期待能帮助准备学习c语言的人一些帮助。


一、C语言-简介

  C是一种通用的编程语言,广泛用于系统软件与应用软件的开发。于1969年至1973年间,为了移植与开发UNIX操作系统,由丹尼斯·里奇与肯·汤普逊,以B语言为基础,在贝尔实验室设计、开发出来。

  C语言具有高效、灵活、功能丰富、表达力强和较高的可移植性等特点,在程序员中备受青睐,成为最近25年使用最为广泛的编程语言。目前,C语言编译器普遍存在于各种不同的操作系统中,例如Microsoft Windows, Mac OS X, Linux, Unix等。C语言的设计影响了众多后来的编程语言,例如C++、Objective-C、Java、C#等。

  二十世纪八十年代,为了避免各开发厂商用的C语言语法产生差异,由美国国家标准局为C语言订定了一套完整的国际标准语法,称为ANSI C,作为C语言的标准。二十世纪八十年代至今的有关程序开发工具,一般都支持匹配ANSI C的语法。

二、C语言-程序结构

2.1 程序结构

1、C语言的程序构成方式
1)c语言程序由函数构成,每个函数可以实现一个或多个功能。
2)一个正规程序可以有多个函数,但是有且只有一个主函数。
3)函数只有在被调用的时候才执行,主函数由系统调用执行。
4)函数的格式必须按照规范书写。
5)C 语言程序文件的后缀为 .c

2、C语言的程序主要包括:
          1、预处理器指令
          2、函数
          3、变量
          4、语句 & 表达式
          5、注释
以下面这个程序为例,其中:
1)#include <stdio.h> 是预处理器指令
2)int main()是主函数,程序从这里开始
3)// 后面为注释,之间的内容会被编译器忽略
4)printf(…) 是 C 语言中的函数,用处是在屏幕上显示消息。
5)return 0; 终止 main() 函数,并返回值 0。
这些东西就是组成一个程序的结构。

#include <stdio.h> 
int main() 
{
    
     
//第一个C语言程序 
printf("Hello, World! "); 
return 0; 
}

还是这个程序,我们对其进行更加细致的解释
  首先是#include <stdio.h>,这句话是做什么的呢?

  1、#include 告诉预处理器将指定头文件的内容插入到预处理器命令的相应位导入头文件的预编译指令,

  2、<> 表示系统自带的库,也可以写成" " 表示用户自定义的库,如果写成" "并且自定义的库里面没有这个文件系统会自动查找自带的库,如果还是没有就会报错。

  3、int main()是主函数,是程序的入口,其中因为是int main()所以main() 的返回值是 int 类型,所以是 return 0; 现在 C 标准里规定 main() 返回值必须为 int,所以必须写成是 int main()。
  4、//用于注释说明。

  5、printf() 用于格式化输出到屏幕。printf() 函数在 “stdio.h” 头文件中声明。当编译器遇到 printf() 函数时,如果没有找到 stdio.h 头文件,会发生编译错误。

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

2.2 无头文件-错误解析

  当没有包含头文件 #include <stdio.h> 就使用 printf 时,会出现下面警告:

warning: implicitly declaring library function 'printf' with type'int (const char *, ...)' [-Wimplicit-function-declaration]

这句话提示的意思是:用类型“int (const char *,…)”隐式声明了库函数printf。
该提示的背景知识:因为 printf 这一类库函数太常用了,所以编译器在编译的时候,发现源文件中并没有声明 printf 这个函数就直接使用了它,那么编译器就会为 printf 函数隐式生成一个声明。你那个编译器提示你,它为printf生成的隐式声明是:int printf(const char *, …)
该提示后果:通常情况下,这个提示只是一个 warning,而不是 error,编译仍然可以通过。
解决方案:要消除这个提示,只要加上 printf 的声明就行了。printf 函数是一个库函数,它的声明在 stdio.h 文件里。所以,你只要在源文件中 #include <stdio.h> 就可以了。

三、C语言-基本语法

C语言的程序是由各种令牌组成,令牌可以是关键字、标识符、常量、字符串值、符号等

3.1 关键字

按年份开始有:
auto  break  case  char  const   continue  default  do   double   else  enum  extern  float    for  goto   if    int  long  register  return  short  signed   sizeof  static  struct  switch   typedef  union  unsigned  void  volatile  while
以及部分关键字说明:

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

3.2 标识符

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

  C 标识符内不允许出现标点字符,比如 @、$ 和 %。下面列出几个有效的标识符:
hello  hello_1  hello_world

  重申一下C语言中标识符的命名规范:

  1.标识符由字母、数字、下划线组成,并且首字母不能是数字。

  2.不能把C中的关键字作为用户的标识符,例如:int、if\for、while等。(注:标识符不能和C语言的关键字相同,也不能和用户自定义的函数或C语言库函数同名)

  3.标识符长度是由机器上的编译系统决定的,一般的限制为8字符,(注:8字符长度限制是C89标准,C99标准已经扩充长度,其实大部分工业标准都更长)。

  4.标识符对大小写敏感,即严格区分大小写。一般对变量名用小写,符号常量命名用大写。(注:C语言中字母是区分大小写的,因此score、Score、SCORE分别代表三个不同的标识符)

  5.标识符命名应做到"见名知意",例如,长度(外语:length),求和、总计(外语:sum),圆周率(外语:pi)

3.3 注释

C 语言有两种注释方式:单行注释和多行注释
以 // 开始的单行注释,这种注释可以单独占一行。

/* 单行注释 */
/* 
 多行注释
 多行注释
 多行注释
 */

/* */ 这种格式的注释可以单行或多行。
  这里需要注意的是:不能在注释内嵌套注释,注释也不能出现在字符串或字符值中。

四、C语言-数据类型

  C 中的类型可分为:
  1.基本类型:整数类型和浮点类型,属于算术类型。
  2.枚举类型:被用来定义在程序中只能赋予其一定的离散整数值的变量,属于算术类型。
  3.void 类型:类型说明符 void 表明没有可用的值。
  4.派生类型:指针类型、数组类型、结构类型、共用体类型和函数类型。
  数组类型和结构类型统称为聚合类型。函数的类型指的是函数返回值的类型。

4.1 整数类型

类型 存储大小 值范围
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

Note:各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。

4.2 浮点类型

类型 存储大小 值范围 精度
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 位有效位

4.3 void 类型

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

五、C语言-变量

5.1 变量类型

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

5.2 变量的定义、声明

  变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。
  变量声明:用于向程序表明变量的类型和名字。
  变量的名称可以由字母、数字和下划线字符组成。
变量的声明有两种情况:
  1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。
  2、另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。
Note:它必须以字母或下划线开头。大写字母和小写字母是不同的,又 C语言 是大小写要求区分的。

5.3 Lvalues和Rvalues

  C 中有两种类型的表达式:分别是左值(lvalue)和右值(Rvalues)。
  左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。
  右值(Rvalues):术语右值(Rvalues)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
  变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。

5.4 全局变量和局部变量

  全局变量保存在内存的全局存储区中,占用静态的存储单元;局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。
  C语言经过编译之后将内存分为以下几个区域:
  (1)栈(stack):由编译器进行管理,自动分配和释放,存放函数调用过程中的各种参数、局部变量、返回值以及函数返回地址。操作方式类似数据结构中的栈。
  (2)堆(heap):用于程序动态申请分配和释放空间。C语言中的malloc和free,C++中的new和delete均是在堆中进行的。正常情况下,程序员申请的空间在使用结束后应该释放,若程序员没有释放空间,则程序结束时系统自动回收。注意:这里的“堆”并不是数据结构中的“堆”。
  (3)全局(静态)存储区:分为DATA段和BSS段。DATA段(全局初始化区)存放初始化的全局变量和静态变量;BSS段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中BBS段在程序执行之前会被系统自动清0,所以未初始化的全局变量和静态变量在程序执行之前已经为0。
  (4)文字常量区:存放常量字符串。程序结束后由系统释放。
  (5)程序代码区:存放程序的二进制代码。
  显然,C语言中的全局变量和局部变量在内存中是有区别的。C语言中的全局变量包括外部变量和静态变量,均是保存在全局存储区中,占用永久性的存储单元;局部变量,即自动变量,保存在栈中,只有在所在函数被调用时才由系统动态在栈中分配临时性的存储单元。
跟形象的我们可以用下面这段代码来了解:

#include <stdio.h>
#include <stdlib.h>
int a1 = 1;
int a2;
static int a3 = 2;
static int a4;
int main( )
{
    
      static int b1=2, b2;
    int i=1;
    char*p;
    char str[10] = "hello";
    char*q = "hello";
    p= (char *)malloc( 100 );
    free(p);
    printf("栈区-变量地址  i:%p\n", &i);
    printf("                p:%p\n", &p);
    printf("              str:%p\n", str);
    printf("                q:%p\n", &q);
    printf("堆区地址-动态申请:%p\n", p);
    printf("全局外部有初值 a1:%p\n", &a1);
    printf("    外部无初值 a2:%p\n", &a2);
    printf("静态外部有初值 a3:%p\n", &a3);
    printf("    外静无初值 a4:%p\n", &a4);
    printf("  内静态有初值 b1:%p\n", &b1);
    printf("  内静态无初值 b2:%p\n", &b2);
    printf("文字常量地址    :%p, %s\n",q, q);
    printf("程序区地址      :%p\n",&main);
    return 0;
}

我们使用编译器编译过后能够得到:
在这里插入图片描述

六、常量

  常量是固定值,在程序执行期间不会改变的。
  常量根据数据类型不同又分为整数常量、浮点常量、字符常量,或字符串、枚举常量等。

6.1 整数常量

  整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
  当然整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

6.1.1 进制说明

1)在二进制中只有 0、1 两种情况,你不会看到比 1 大的数字。二进制是逢 2 就进位,所有数字中没可能有 2 或大于 2 的数字;

2)在八进制中有 0、1、2、3、4、5、6、7这八种情况,你也不会看到比7大的数字。八进制是逢8就进位,所有数字中没可能有8或大于8的数字;

3)在十进制中有0、1、2、3、4、5、6、7、8、9这十种情况,你更不会看到比9大的数字。十进制是逢10就进位,所有数字中有0~9都有;

4)在十六进制中有 0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F,其中 A 表示 10;B 表示 11;C 表示 12;D 表示 13;E 表示 14;F 表示 15。十六进制数字中含有 A~F 字母,它是 0 ~ 9+A ~ F;

6.2 浮点常量

  浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。
  当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的
如3.14就是合法的,但是312就不是合法的因为没有小数。

6.2.1 浮点型常数

1.十进制小数(浮点数)形式
它由数字0-9、小数点和+、-号组成,例如3。14、-23.56都是十进制小数。
2.指数形式
它由数字0-9、字母e(或E)和+、-号组成,它的形式为aEn,意为a✖️10^n,其中a为十进制整数或小数,n为十进制整数。在表示浮点型畅常量时,需注意几点:

  (1)以指数形式表示实数时,a和n都不能省略,n必须为整数。
  (2)以十进制小数形式表示实数时,整数和小数部分可省略其中任一个
  (3)浮点型常量默认是double型,如果在后面加上F或f,则其类型为float实数在机内是以指数形式存储的,以float类型为例,大多数C编译系统使用4个连续的字节(即32位)存储在float类型数据。这32位分为4个部分,最高位为数的符号,接着使用若干位存储小数的部分,然后是指数的符号位,最后一个部分是指数。在4个字节中,究竟小数部分和指数部分分别占多少位,ANSI C 本身并没有作规定,由具体的C语言编译系统自定。不少C语言编译系统用24位表示数符号和指数部分。
  由实数的存储形式可看出,小数部分占的位数越多,所能表示的精度越高,指数部分占的越多,所能表示的数值范围越大。

6.3 字符常量

  字符常量是括在单引号中,例如,‘a’ 可以存储在 char 类型的简单变量中。
  在 C语言 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符——\n或制表符—— \t等。

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

6.4 字符串常量

字符串字面值或常量是括在双引号 “” 中的。

6.5 常量定义与声明

在 C语言 中,有两种简单的定义常量的方式:
一是:使用 #define 预处理器。
二是:使用 const 关键字。

6.5.1 define和const的区别

(1) 编译器处理方式不同
#define 宏是在预处理阶段展开。
const 常量是编译运行阶段使用。
(2) 类型和安全检查不同
#define 宏没有类型,不做任何类型检查,仅仅是展开。
const 常量有具体的类型,在编译阶段会执行类型检查。
(3) 存储方式不同
#define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。)
const常量会在内存中分配(可以是堆中也可以是栈中)。
(4) const 可以节省空间,避免不必要的内存分配。 例如:

#define NUM 3.14//常量宏
const doulbe Num = 3.14; //此时并未将3.14放入ROM中 
double i = Num; //此时为Pi分配内存,以后不再分配!
double I= NUM; //编译期间进行宏替换,分配内存
double j = Num; //没有内存分配
double J = NUM; //再进行宏替换,又一次分配内存!

请注意:这里const 定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象 #define 一样给出的是立即数,所以,const 定义的常量在程序运行过程中只有一份拷贝(因为是全局的只读变量,存在静态区),而 #define 定义的常量在内存中有若干个拷贝。
(5) 提高了效率。 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
(6) 宏替换只作替换,不做计算,不做表达式求解;

七、存储类

  存储类是用来定义 C语言 程序中变量或函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。
  下面介绍C语言中可用的存储类:
  1)auto 是局部变量的默认存储类, 限定变量只能在函数内部使用;
  2)register 代表了寄存器变量,不在内存中使用;
  3)static是全局变量的默认存储类,表示变量在程序生命周期内可见;
4)extern 表示全局变量,即对程序内所有文件可见,类似于Java中的public关键字;

7.1 auto

  auto是普通局部栈变量,自动存储,这种对象会自动创建和销毁 ,建议这个变量要放在堆栈上面,调用函数时分配内存,函数结束时释放内存。一般隐藏auto默认为自动存储类别。我们程序都变量大多是自动变量。
  下面我们用实例进行说明:

#include <stdio.h>

int main(void)
{
    
    
    auto int i = 10; 
    /* 声明局部变量的关键字是 auto; 可以省略, 所以一般没人使用 */   
    printf("%d\n", i);  
    return 0;
}

7.2 Register

  动态和静态变量都是存放在内存中,程序中遇到该值时用控制器发指令将变量的值送到运算器中,需要存数再保存到内存中。如果频繁使用一个变量,比如一个函数体内的多次循环每次都引用该局部变量,我们则可以把局部变量的值放到CPU的寄存器中,叫寄存器变量。不需要多次到内存中存取提高效率。但是只能局部自动变量和形参可以做寄存器变量。在函数调用时占用一些寄存器,函数结束时释放。不同系统对register要求也不一样,比如对定义register变量个数,数据类型等限制,有的默认为自动变量处理。所以在程序一般也不用。

7.3 static

  static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。

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

  全局声明的一个 static 变量或方法可以被任何函数或方法调用,只要这些方法出现在跟 static 变量或方法同一个文件

7.3.1 static 的用法

  static是用来说明静态变量 ,如果是在函数的,那么其效果和全局变量类似 。但是,static定义的变量只能在当前 c原因 程序文件中使用,在另一个 c 语言代码里面 , 即使使用 extern 关键词也不能访问这个static变量。如果实在函数内部定义的,那么这个变量只初始化一次,即使再次调用这个函数,这个static变量也不会再次初始化 , 这个变量的取值就会一直保存着,也就是说,当你再次调用这个函数的时候,里面用到这个static变量时,就会发现,它还是上一次函数调用时的结果。
我们用一个实例进行说明:

#include <stdio.h>

void varfunc(void)
{
    
    
    int var=0;
    static int static_var=0;
    printf("var equal: %d\n",var);
    printf("static_var equal: %d\n",static_var);
    printf("");
    var++;
    static_var++;
}

int main(int argc, char *argv[])
{
    
    
    int i;
    for(i=0;i<3;i++)
        varfunc();
    return 0;
}

会出现以下结果:
在这里插入图片描述
由此可见,用static修饰的局部变量只初始化一次。

7.3.2 static的用处

在 C 语言中,static 在不同的情况下 static 有着不同的作用。
(1)修饰局部变量
(2)修饰全局变量
(3)修饰函数

7.4 extern

  extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

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

  extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候。

八、C 运算符

C语言运算符包括:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、杂项运算符

8.1 算术运算符

算术运算符就是+(加)、-(减)、*(乘)、/(除)、%(取模)、++(自增)、- -(自减)
假设我们有两个变量x=10,y=5,有:

运算符 描述 例子
+ 把两个操作数相加 x + y 将得到 15
- 从第一个操作数中减去第二个操作数 x - y 将得到 5
* 把两个操作数相乘 x * y将得到 50
/ 分子除以分母 x / y 将得到 2
% 取模运算符,整除后的余数 x % y 将得到 0
++ 自增运算符,整数值增加 1 x++ 将得到 11
- - 自减运算符,整数值减少 1 x - -将得到 9

程序实例:

 z = x + y;
   printf("相加时z的值是 %d\n", z );
   z = x - y;
   printf("相减时z的值是 %d\n", z );
   z = x * y;
   printf("相乘时z的值是 %d\n", z );
   z = x / y;
   printf("相除时z的值是 %d\n", z );
   z = x % y;
   printf("取余时z的值是 %d\n", z );
   z = x++;  // 赋值后再加 1 ,z 为 10,x 为 11
   printf("自增z的值是 %d\n", z );

得到结果如图:
在这里插入图片描述

8.1.1 x++ 与 ++x 的区别

x++ 与 ++x 的区别在于一个后加,一个先加。
x++ 输出 x 的值再自加,缓存 x 自加后的结果,用于下次进行与 x 相关的计算。

++a 则相当于 a+1。

8.2 关系运算符

还是假设我们有两个变量x=10,y=5,有:

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

我们用下面程序进行测试,以为更好理解:

#include <stdio.h>
 
int main()
{
    
    
   int x = 11;
   int y = 10;
 
   if( x == y )
   {
    
    
      printf("x等于y\n" );
   }
   else
   {
    
    
      printf("x不等于y\n" );
   }
   if ( x < y )
   {
    
    
      printf("x小于y\n" );
   }
   else
   {
    
    
      printf("x不小于y\n" );
   }
   if ( x > y )
   {
    
    
      printf("x大于y\n" );
   }
   else
   {
    
    
      printf("x不大于y\n" );
   }
   /* 改变 x 和 y 的值 */
   x = 5;
   y = 10;
   if ( x <= y )
   {
    
    
      printf("x小于或等于y\n" );
   }
   if ( y >= x )
   {
    
    
      printf("y大于或等于x\n" );
   }
}

在这里插入图片描述

8.3 逻辑运算符

逻辑运算符指的是与或非

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

一句话总结:

逻辑是将两个操作值转换成Boolean进行操作。

逻辑与:双真真(双真为真,下同);

逻辑或:单真真;

逻辑非:取反;

没有说明的情况都为假(0)。

下面我们用程序进行测试,以为更好理解:

#include <stdio.h>
 
int main()
{
    
    
   int x = 11;
   int y = 10;
 
   if ( x && y )
   {
    
    
      printf("条件为真\n" );
   }
   if ( x || y )
   {
    
    
      printf("条件为真\n" );
   }
   /* 改变 x 和 y 的值 */
   x = 10;
   y = 6;
   if ( x && y )
   {
    
    
      printf("条件为真\n" );
   }
   else
   {
    
    
      printf("条件为假\n" );
   }
   if ( !(x && y) )
   {
    
    
      printf("条件为真\n" );
   }
}

在这里插入图片描述

8.4 位运算符

位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:
(https://img-blog.csdnimg.cn/cec87a61ad4249a6a01090ba8e826e68.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NsdENq,size_16,color_FFFFFF,t_70)
假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:

A = 0011 1100

B = 0000 1101


A&B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001

~A = 1100 0011
一句话总结:
按位是将两个操作值转换成二进制然后逐位进行操作。

按位与:双真真;

按位或:有真真(有一个真即为真,两个真的情况也属于有一个真);

按位异或:单真真(区别于有真真,两个真的情况不属于单真);

按位是二进制逐位操作,未说明的皆假。

8.5 赋值运算符

运算符 描述 例子
= 简单的赋值运算符,把右边操作数的值赋给左边操作数 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

8.6 杂项运算符

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

我们用下面程序进行测试,以为更好理解:

#include <stdio.h>
 
int main()
{
    
    
   int x = 4;
   short y;
   double z;
   int* ptr;
 
   /* sizeof 运算符实例 */
   printf("变量 x 的大小 = %lu\n", sizeof(x) );
   printf("变量 y 的大小 = %lu\n", sizeof(y) );
   printf("变量 z 的大小 = %lu\n", sizeof(z) );
 
   /* & 和 * 运算符实例 */
   ptr = &x;    /* 'ptr' 现在包含 'x' 的地址 */
   printf("x 的值是 %d\n", x);
   printf("*ptr 是 %d\n", *ptr);
 
   /* 三元运算符实例 */
   x = 10;
   y = (x == 1) ? 20: 30;
   printf( "y 的值是 %d\n", y );
 
   y = (x == 10) ? 20: 30;
   printf( "y 的值是 %d\n", x );
}

在这里插入图片描述

8.7 运算符优先级

初等运算符>单目运算符>算术运算符>关系运算符>逻辑运算符>条件运算符>赋值运算符
初等运算符有:()、[ ]、->、. (后两者均为结构体成员运算符);
单目运算符有:!、~、++、–、sizeof、&、
算术运算符有:
、/、+、-、<<、>>;
关系运算符有:<、<=、>、>=、==、!=、&、^、|;(此栏排列仍有优先级顺序;
逻辑运算符有:&&、||;
条件运算符有:?:(即三目运算符);
赋值运算符有:=、+=、-=、*=、/=、%=、>>=、<<=;等

8.8 原码、反码、补码

原码:一个整数,按照绝对值大小转换成的二进制数,称为原码。

比如 00000000 00000000 00000000 00000101 是 5 的原码。

反码:将二进制数按位取反,所得的新二进制数称为原二进制数的反码。

取反操作指:原为 1,得 0;原为 0,得 1。(1 变 0; 0 变 1)

比如:将 00000000 00000000 00000000 00000101 每一位取反,得 11111111 11111111 11111111 11111010。

称:11111111 11111111 11111111 11111010是 00000000 00000000 00000000 00000101 的反码。

反码是相互的,所以也可称:

11111111 11111111 11111111 11111010 和00000000 00000000 00000000 00000101 互为反码。

补码:反码加1称为补码。

也就是说,要得到一个数的补码,先得到反码,然后将反码加上 1,所得数称为补码。

比如:00000000 00000000 00000000 00000101 的反码是:11111111 11111111 11111111 11111010。

那么,补码为:

11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011

所以,-5 在计算机中表达为:11111111 11111111 11111111 11111011。

九、判断语句

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

9.1 if 语句

if 语句语法
if(布尔表达式)
{
    
    
   /* 如果布尔表达式为真将执行的语句 */
}
如果布尔表达式为 true,则 if 语句内的代码块将被执行。如果布尔表达式为 false,则 if 语句结束后的第一组代码(闭括号后)将被执行。

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

if语句流程图:

Created with Raphaël 2.3.0 开始 条件? 有关条件的代码 结束 yes no

9.2 if…else 语句

一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为 false 时执行。

if else 语句语法
if(布尔表达式)
{
    
    
   /* 如果布尔表达式为真将执行的语句 */
}
else
{
    
    
   /* 如果布尔表达式为假将执行的语句 */
}
如果布尔表达式为 true,则执行 if 块内的代码。如果布尔表达式为 false,则执行 else 块内的代码。

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

if else 语句流程图:

Created with Raphaël 2.3.0 开始 条件? if条件的代码 结束 else条件的代码 yes no

处理if else语句外还有if…else if…else 语句
个 if 语句后可跟一个可选的 else if…else 语句,这可用于测试多种条件。

当使用 if…else if…else 语句时,以下几点需要注意:

一个 if 后可跟零个或一个 else,else 必须在所有 else if 之后。
一个 if 后可跟零个或多个 else if,else if 必须在 else 之前。
一旦某个 else if 匹配成功,其他的 else if 或 else 将不会被测试。

9.3 嵌套 if 语句

在 C 语言中,嵌套 if-else 语句是合法的,这意味着您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。

嵌套 if 语句语法
if(布尔表达式1)
{
    
    
   /* 当布尔表达式 1 为真时执行 */
   if(布尔表达式2)
   {
    
    
      /* 当布尔表达式 2 为真时执行 */
   }
}

9.4 switch 语句

一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case,且被测试的变量会对每个 switch case 进行检查。

//switch语法
switch(expression){
    
    
    case constant-expression  :
       statement(s);
       break; /* 可选的 */
    case constant-expression  :
       statement(s);
       break; /* 可选的 */
  
    /* 您可以有任意数量的 case 语句 */
    default : /* 可选的 */
       statement(s);
}

switch 语句必须遵循下面的规则:

switch 语句中的 expression 是一个常量表达式,必须是一个整型或枚举类型。
在一个 switch 中可以有任意数量的 case 语句。每个 case 后跟一个要比较的值和一个冒号。
case 的 constant-expression 必须与 switch 中的变量具有相同的数据类型,且必须是一个常量或字面量。
当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。
当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。
不是每一个 case 都需要包含 break。如果 case 语句不包含 break,控制流将会 继续 后续的 case,直到遇到 break 为止。
一个 switch 语句可以有一个可选的 default case,出现在 switch 的结尾。default case 可用于在上面所有 case 都不为真时执行一个任务。default case 中的 break 语句不是必需的。

9.4.1 switch小例子

下面我们是用swith实现一个小demo:
根据输入的年份判断是否为闰年,根据输入的月份判断这月有多少天。
闰年的定义:

普通年(不能被100整除的年份)能被4整除的为闰年。(如2004年就是闰年,1999年不是闰年);
世纪年(能被100整除的年份)能被400整除的是闰年。(如2000年是闰年,1900年不是闰年);

#include<stdio.h>

int main() {
    
    
    int year, month, ex;
    printf("请输入年份及月份(空格分隔):");
    scanf("%d %d", &year, &month);
    if(month < 1 || month > 12) {
    
    
        printf("输入有误!!");
    } else {
    
    
        if((year%4 ==  0  && year % 100 != 0) || year%400 == 0) {
    
    
            ex = 1;
            printf("%d年为闰年。\n", year);

        } else {
    
    
            ex = 0;
            printf("%d年不为闰年。\n", year);
        }

        switch(month) {
    
    
            case 4: case 6: case 9: case 11: printf("%d月有%d天。", month, 30); break;
            case 2: printf("%d月有%d天。", month, 27+ex); break;
            default: printf("%d月有%d天。", month, 31);
        }
    }
}

9.5 嵌套 switch 语句

我们可以把一个 switch 作为一个外部 switch 的语句序列的一部分,即可以在一个 switch 语句内使用另一个 switch 语句。即使内部和外部 switch 的 case 常量包含共同的值。

//嵌套 switch语法
switch(ch1) {
    
    
   case 'A': 
      printf("这个 A 是外部 switch 的一部分" );
      switch(ch2) {
    
    
         case 'A':
            printf("这个 A 是内部 switch 的一部分" );
            break;
         case 'B': /* 内部 B case 代码 */
      }
      break;
   case 'B': /* 外部 B case 代码 */
}

实例:一个满足员工工龄的demo:

#include<stdio.h>
int main(void)
{
    
    
    char sex;
    int age  ;
    printf("请输入你的性别简称!男(M),女(F)\n");
    scanf_s("%c", &sex);
    switch (sex)
    {
    
    
    case 'M':
    case 'm':
    printf("你的性别为“男”请你进入测试!\n");
    printf("请输入你的工龄!\n");
    scanf_s("%2d",&age);
        switch (age)
        {
    
     
        case 5:
            printf("奖励iphone一台!!\n");
            break;
        case 10:
            printf("奖励汽车一辆!!\n");
            break;
        case 15:
            printf("奖励小楼一栋!!\n");
            break;
        default:
            printf("抱歉,未满足奖励条件或者超出工龄!!\n");
            break;
        }
    break;
    case 'F':
    case 'f':
        printf("你的性别为“女”请你进入测试!\n");
        printf("请出入你的工龄!\n");
        scanf_s("%2d",&age);
        switch (age)
        {
    
    
        case 5:
            printf("奖励iphone一台!\n");
            break;
        case 10:
            printf("奖励名牌化妆品一套!\n");
            break;
        case 15:
            printf("奖励爱马仕包一个!\n");
            break;
        default:
            printf("抱歉, 未满足奖励条件或者超出工龄!!\n");
            break;
            }
        break;    
    }
    return 0;
}

总结

期待大家和我交流,留言或者私信,一起学习,一起进步!

猜你喜欢

转载自blog.csdn.net/CltCj/article/details/119318074