C语言-回顾整理

C1 前言

C2 第一个C语言项目

//引入头文件stdio.h
#include<stdio.h>

//主函数
int main(){
    //printf是一个库函数,专门用来打印数据的,此函数来自stdio.h
    printf("Hello World!\n");
    return 0;
}

C语言代码中一定要有main(主函数)函数,main函数只能有一个。虽然和C++同用一个编译器,但C语言项目要用.C后缀,C++用.CPP后缀。

//标准的主函数写法
int main(){
    return 0;
}

//古老的写法-不推荐
void main(){
    
}

vs2022运行代码快捷键:CTRL+F5

vs2022调试运行快捷键:F10

C3 数据类型

3.1 编程规范:

3.1.1 代码缩进

​ 代码缩进统一为4个字符。不采用空格,而用Tab键制表位。

3.1.2 变量、常量命名规范

​ 常量命名统一为大写。

​ 如果是成员变量,均以m_开始。

​ 如果是普通变量,取与实际意义相关的名称并且使用驼峰命名法首字母大写,要在前面添加数据类型的首字母。

​ 如果是指针,则为其标识符前添加p字符,并且使用驼峰命名法首字母大写。

#define AGE 20			/*定义常量*/
int m_iAge;				/*定义整形成员变量*/
int iMaxNumber;			/*定义普通整型变量*/
int * pAge;				/*定义指针变量*/
3.1.3 函数命名规范

函数命名使用驼峰命名法。

int AddTwoNum(int num1,int num2);
3.1.4 注释

尽量注释并对齐。

3.2 关键字:

autodoubleintstruct
breakelselongswitch
caseenumregistertypedef
charexternunionreturn
constfloatshortunsigned
continueforsignedvoid
defaultgotosizeofvolatile
dowhilestaticif

3.3 标识符:

​ 在C语言中为了在程序的运行过程中可以使用变量、常量、函数、数组等,就要为这些形式设定一个名称,而设定的名称就是所谓的标识符。

  1. 所有标识符必须由字母或下划线开头,而不能使用数字或者符号作为开头
  2. 在设定标识符时,除开头外,其他位置都可以由字母、下划线或数字组成
  3. 英文字母的大小写代表着不同的标识符。也就是说,在C语言中是区分大小写字母的。
  4. 标识符不能是关键字。关键字是定义一种类型使用的字符,标识符不能使用。

3.4 数据类型:

p3-数据类型.png

3.4.1 常量:

​ 常量就是其值在程序运行中不可改变的量。

常量可以分为以下三大类:

  • ​ 数值型常量。包括整型常量和实型常量。

  • ​ 字符型常量。

  • ​ 符号常量。

3.4.1.1 整型常量

​ 整型常量就是直接使用的整型常数,如123、-456.7等。整型常量可以是长整型、短整型、符号整型和无符号整型

  1. 无符号短整型的取值范围是065535,有符号短整型的取值范围是-32768+32767,这些都是16位整型常量的范围。

  2. 如果整型是32位的,那么无符号形式的取值范围是04294967295,而有符号的形式的范围是-2147483648+2147483647。但是整型如果是16位的那么同无符号短整型的范围相同。

  3. 长整型是32位的,其取值范围可以参考上面有关整型的描述。在编写整型常量时,可以在常量的后面加上符号L或者U进行修饰。L表示该常量是长整型,U表示该常量为无符号整型。

    LongNum = 1000L;	/*L 表示长整型*/
    UnsignLongNum = 500U;	/*U 表示无符号整型*/
    

    表示长整型和无符号整型的后缀字母L和U可以使用大写,也可以使用小写。

  4. 八进制整数

    如果要使得使用的数据表达形式是八进制,需要在常数前加上0进行修饰。八进制包含的数字是0~7。

    OctalNumber = 0123;	/*在常熟前面加一个0来代表八进制*/
    OctalNumber = 0432;
    
  5. 十六进制整数

    常量前面使用0x作为前缀,表示该常量是用十六进制进行表示。十六进制中包含的数字有09以及字母AF。

    HexNumber1 = 0x123;	/*前面加上0x表示常量为十六进制*/
    HexNumber2 = 0x3ba4;
    

    其中字母AF可以使用大写形式,也可以使用af小写形式。

  6. 十进制整数

    十进制是不需要在其前面添加前缀的。十进制中包含的数字由0~9。

    AlgorismNumber1 = 123;
    AlgorismNumber1 = 456;
    

    这些整型数据都是以二进制的方式存放在计算机的内存之中,其数值是以补码的形式进行表示的。一个正数的补码与其源码形式相同,一个负数的补码是将该数绝对值的二进制形式按位取反再加上1。

    例如,一个十进制数11在内存中的表现形式

    0000000000001011
    

    如果是-11,那么在内存中又是怎样的呢?因为是以补码进行表示,所以负数要先将其绝对值求出然后进行取反操作,得到取反后的结果。

    1111111111110100
    

    取反之后还要进行加1操作,这样就得到最终的结果,-11在内存中的存储情况。

    1111111111110101
    

    对于有符号整数,其在内存中存放的最左面一位表示符号位,如果该位为0,则说明该数为正,若为1,则说明该数为负。

3.4.1.2 实型常量

​ 实型也称为浮点型,是由整数部分和小数部分这两块组成,其中用十进制的小数点进行隔开。表示实数的方式有两种:

  1. 科学计数方式

    科学计数方式就是使用十进制小数的方法进行描述实型。

    SciNum1 = 123.45;	/*科学计数法*/
    SciNum2 = 0.5458;
    
  2. 指数方式

    有时候实型非常大或者非常的小,这样使用科学计数方式是不利于观察的,这时可以使用指数方法进行显示实型常量。其中使用字母e或者E进行指数显示,如果45e2表示的就是4500,而45e-2表示的就是0.45。

    如上面的SciNum1和SciNum2代表的实型常量,使用指数方式显示着两个实型常量,如下。

    SciNum1 = 1.2345e2;	/*指数方式显示*/
    SciNum2 = 5.458e-1;
    

    在编写实型常量时,可以在常量的后面加上符号F或者L进行修饰。F表示该常量是float单精度类型,L表示该常量为long double长双精度类型。例如:

    FloatNum = 1.2345e2F;	/*单精度类型*/
    LongDoubleNum = 5.458e-1L;	/*长双精度类型*/
    

    如果不在后面加上后缀,那么在默认状态下,实型常量为double双精度类型。例如:

    DoubleNum = 1.2345e2;	/*双精度类型*/
    
3.4.1.3 字符型常量

​ 字符型常量与之前所介绍的常量有所不同,要对其字符型常量使用指定的定界符进行限制。字符型常量可以分成两种:一种是字符常量,另一种是字符串常量。下面分别对这两种字符型常量进行介绍。

  1. 字符常量

    使用单引号括起来的一个字符就是字符常量。

    'A'
    '#'
    'b'
    

    使用putchar函数将单个字符常量进行输出,使得输出的字符常量形成一个单词Hi。

    #include<stdio.h>
    
    int main(){
        putchar('H');	/*输出字符常量H*/
        putchar('i');	/*输出字符常量i*/
        return 0;
    }
    
  2. 字符串常量

    字符串常量是用一组双引号括起来的若干字符序列。

  3. 定界符的使用不同:字符常量使用的是单引号,而字符串常量使用的是双引号。

  4. 长度不同:在上面提到过字符常量只能有一个字符,也就是说字符常量的长度就是1。而字符串常量的长度至少是2。

  5. 存储的方式不同:在字符常量中存储的是字符的ASCII码值,而在字符串常量中,不仅要存储有效的字符,还要存储结尾处的结束标志'\0'。

    w e lc o m e \0
    

    使用printf函数将字符串常量"What a nice day!"在控制台输出显示。

    #include<stdio.h>
    
    int main(){
        print("What a nice day!\n");	/*输出字符串*/
        return 0;
    }
    
3.4.1.4 转义字符

转义符号在字符常量中是一种特殊的字符。转义字符是以反斜杠""为开头的字符,后面跟一个或几个字符。常用的转义字符及其含义如下表所示:

转义字符
\n回车换行
\t横向跳到下一制表位置
\v竖向跳格
\b退格
\r回车
\f走纸换页
\\反斜线符""
'单引号符
\a鸣铃
\ddd1~3位八进制数所代表的字符
\xhh1~2位十六进制数所代表的字符
3.4.1.5 符号常量

使用一个符号名进行代替固定的常量值,这里使用的符号名就称之为符号常量。使用符号常量的好处在于可以为编程和阅读带来方便。

#define PAI 3.14

3.4.2 变量:

在前面的例子中已经多次接触过变量。变量就是在程序运行期间其值可以变化的值。每个变量都属于一种类型,每种类型都定义了变量的格式和行为。因此,一个变量应该有属于自己的名称,并且在内存中占有存储空间,其中,变量的大小取决于类型。C语言中的变量类型包括整型变量、实型变量和字符变量。

3.4.2.1 整型变量

整型变量是用来存储整型数值的变量。整型变量可以分为下表的6种数据类型,其中基本类型的符号使用int关键字,在此基础上可以根据需要加上一些符号进行修饰,如关键字short或long。

类型关键字
有符号基本整型[signed] int
无符号基本整型unsigned [int]
有符号基本整型[signed] short [int]
无符号基本整型unsigned short [int]
有符号基本整型[signed] long [int]
无符号基本整型unsigned long [int]

表格中的[]为可选部分,例如[signed] int,在编写时可以省略signed关键字。

  1. 有符号基本类型

    有符号基本类型是指signed int型,其值是基本的整型常量。编写时常将signed进行省略。有符号基本类型在内存中占4个字节,取值范围是-2147483648~2147483647。

    通常说到的整型,都是指有符号基本整型int。

    定义一个有符号整型变量的方法是在变量前使用关键字int。定义一个整型变量iNumber,并为其赋值10的方法:

    int iNumber;	/*定义有符号基本整型变量*/
    iNumber = 10;	/*为变量赋值*/
    

    或者在定义变量的同时为其赋值:

    int iNumber = 10;	/*定义有符号基本整型变量*/
    

    打印输出整型:

    #include<stdio.h>
    
    int main(){
        signed int iNumber;		/*定义有符号基本整型变量*/
        iNumber=10;				/*为变量进行赋值*/
        printf("%d\n",iNmber);	/*显示整型变量*/
        return 0;				/*程序结束*/
    }
    
  2. 无符号基本整型

    无符号基本整型使用的关键字是unsigned int,其中的关键字int在编写时可以省略。无符号基本整型在内存中占4个字节,取值范围是0~4294967295。

    定义一个无符号基本类型变量的方法是在变量前使用关键字unsigned。例如,要定义一个无符号基本整型的变量iUnsignedNum,并为其赋值10的方法如下:

    unsigned iUnsignedNum;	/*定义无符号基本整型变量*/
    iUnsignedNum = 10;		/*为变量赋值*/
    
  3. 有符号短整型

    有符号短整型使用的关键字是signed short int,其中的关键字signed和int在编写时可以省略。有符号短整型在内存中占两个字节,取值范围是-32768~32767。

    定义一个有符号短整型变量的方法是在变量前使用关键字short。例如,要定义一个有符号短整型的变量iShortNum,并为其赋值10的方法如下:

    short iShortNum;	/*定义有符号短整型变量*/
    iShortNum=10;		/*为变量赋值*/
    
  4. 无符号短整型

    无符号短整型使用的关键字是unsigned short int,其中的关键字int在编写时可以省略。无符号短整型在内存中占两个字节,取值范围是0~65535。

    定义一个无符号短整型变量的方法是在变量前使用关键字unsigned short。例如,要定义一个无符号短整型的变量iUnsignedShtNum,并为其赋值10的方法如下:

    unsigned short iUnsignedShtNum;		/*定义无符号短整型变量*/
    iUnsignedShtNum = 10;		/*为变量赋值*/
    
  5. 有符号长整型

    有符号长整型使用的关键字是signed long int,其中的关键字signed和int在编码时可以省略。有符号长整型在内存中占4个字节,取值范围是-2147483648~2147483647。

    定义一个有符号长整型变量的方法是在变量前使用关键字long。例如,要定义一个有符号长整型的变量iLongNum,并为其赋值10的方法如下:

    long iLongNum;		/*定义有符号长整型变量*/
    iLongNum=10;		/*为变量赋值*/
    
  6. 无符号长整型

    无符号长整型使用的关键字是unsigned long int,其中的关键字int在编写时可以省略。无符号长整型在内存中占4个字节,取值范围是0~4294967295。

    定义一个无符号长整型变量的方法是在变量前使用关键字unsigned long。例如,要定义一个有符号长整型的变量iUnsignedLongNum,并为其赋值10的方法如下:

    unsigned long iUnsignedLongNum;		/*定义无符号长整型变量*/
    iUnsignedLongNum = 10;		/*为变量赋值*/
    
3.4.2.2 实型变量

实型常量也称为浮点型变量,是指用来存储实型数值变量,其中实型数值是由整数和小数两部分组成的。实型变量根据实型的精度可以分为单精度类型、双精度类型和长双精度类型3中类型如下:

类型名称
单精度类型float
双精度类型double
长双精度类型long double
  1. 单精度类型

    单精度类型使用的关键字是float,它在内存中占4个字节,取值范围是-3.410^-38~3.40^38。

    定义一个单精度类型变量的方法是在变量前使用关键字float。例如,要定义一个变量fFloatStyle,并为其赋值3.14的方法如下:

    float fFloatStyle;		/*定义单精度类型变量*/
    fFloatStyle=3.14;		/*为变量赋值*/
    

    打印输出浮点型:

    #include<stdio.h>
    
    int main(){
        float fFloatStyle;		/*定义有符号基本整型变量*/
        fFloatStyle=1.23f;				/*为变量进行赋值*/
        printf("%f\n",fFloatStyle);	/*显示单精度变量*/
        return 0;				/*程序结束*/
    }
    

    运行结果

    1.230000
    
  2. 双精度类型

    双精度类型使用的关键字是double,它在内存中占8个字节,取值范围是-1.7x10^-308~1.7x10^308。

    定义一个双精度类型变量的方法是在变量前使用关键字double。例如,要定义一个变量dDoubleStyle,并为其值5.321的方法如下:

    double dDoubleStyle;		/*定义双精度类型变量*/
    dDoubleStyle=5.321;			/*为变量赋值*/
    

    打印输出双精度浮点型:

    #include<stdio.h>
    
    int main(){
        double dDoubleStyle;		/*定义双精度类型变量*/
    	dDoubleStyle=5.321;			/*为变量赋值*/
        printf("%f\n",dDoubleStyle);	/*显示双精度变量*/
        return 0;				/*程序结束*/
    }
    

    运行结果

    5.321000
    
  3. 长双精度类型

    长双精度类型使用的关键字是long double,它在内存中8个字节,取值范围是-1.7x10^-308~1.7x10^308。

    定义一个双精度类型变量的方法是在变量前使用关键字long double。例如,要定义一个变量fLongDouble,并为其赋值46.257的方法如下:

    long double fLongDouble;	/*定义长双精度类型变量*/
    fLongDouble=46.257;			/*为变量赋值*/
    

    打印输出双精度浮点型:

    #include<stdio.h>
    
    int main(){
        long double fLongDouble;	/*定义长双精度类型变量*/
    	fLongDouble=46.257;			/*为变量赋值*/
        printf("%f\n",fLongDouble);	/*显示长双精度变量*/
        return 0;				/*程序结束*/
    }
    
3.4.2.3 字符型变量

字符型变量是用来存储字符常量的变量。将一个字符常量存储到一个字符变量中,实际上是将该字符的ASCII码(无符号整数)存储到内存单元中。

字符型变量在内存空间中占一个字节,取值范围是-128~-127。

定义一个字符型变量的方法是使用关键字char。例如,要定义一个字符型的变量cChar,为其赋值为'a'的方法如下:

char cChar;		/*定义字符型变量*/
cChar='a'		/*为变量赋值*/

字符数据在内存中存储的是字符的ASCII码,即一个无符号整数,其形式与整数的存储形式一样,因此C语言允许字符型数据与整型数据之间通用,例如:

char cChar1;	/*字符型变量cChar1*/
char cChar2;	/*字符型变量cChar2*/
cChar1='a';		/*为变量赋值*/
cChar2=97;

printf("%c\n",cChar1);		/*显示结果为a*/
printf("%c\n",cChar2);		/*显示结果为a*/

在上面的代码中可以看到,首先定义两个字符型变量,在为两个变量进行赋值时,一个变量赋值为'a',而另一个赋值为97。最后显示结果都是字符'a'。

在本实例中为定义的字符型变量和整型变量进行不同的赋值,然后通过输出结果观察整型变量和字符型变量之间的转换。

#include<stdio.h>
int main(){
    char cChar1;	/*字符型变量cChar1*/
    char cChar2;	/*字符型变量cChar2*/
    int iInt1;		/*整型变量iInt1*/
    int iInt2;		/*整型变量iInt2*/
    
    cChar1='a';
    cCHar2=97;
    iInt1='a';
    iInt2=97;
    
    printf("%c",cChar1);	/*显示结果为a*/
    printf("%d",cChar2);	/*显示结果为97*/
    printf("%c",iInt1);		/*显示结果为a*/
    printf("%d",iInt2);		/*显示结果为97*/
    return 0;
}
变量总结

数值型和字符型数据的字节数和数值范围

类型关键字字节数值范围
整型[signed] int4-2147483648~2147483647
无符号整型unsigned [int]40~4294967295
短整型short [int]2-32768~32767
无符号短整型unsigned short [int]20~65535
长整型long [int]4-2147483648~2147483647
无符号长整型unsigned long [int]40~4294967295
字符型[signed] ing1-128~127
无符号字符型unsigned char10~255
单精度float4-3.4x10^-38~3.4x10^38
双精度double8-1.7x10-308~1.7x10^308
长双精度long double8-1.7x10^-308~1.7x10^308

3.4.3 变量的存储类别:

静态存储与动态存储

根据变量的产生时间,可将其分为静态存储动态存储

​ 静态存储是指程序运行时为其分配固定的存储空间,动态存储则是在程序运行期间根据需要动态地分配存储空间。

3.4.3.1 auto变量

​ auto关键字用于定义一个局部变量为自动的,这意味着每次执行到定义该变量时,都会产生一个新的变量,并且对其重新进行初始化。

在AddOne函数中定义一个auto型的整型变量iInt,在其中对变量进行加1操作。之后在主函数main中通过显示的提示语句,可以看到调用两次AddOne函数的输出,从结果中可以看到,在AddOne函数中定义整型变量时系统会为其分配内存空间,在函数调用结束时自动释放这些存储空间。

#include<stdio.h>

void AddOne(){
    auto int iInt = 1;	/*定义auto整型变量*/
    iInt = iInt + 1;	/*变量+1*/
    printf("%d\n",iInt);/*显示结果*/
}

int main(){
    printf("第一次调用:");	/*显示结果*/
    AddOne();				/*调用AddOne函数*/
    printf("第二次调用:");	 /*显示结果*/
    AddOne();				/*调用AddOne函数*/
    return 0;				/*程序结束*/
}

运行程序,效果如下。

第一次调用:2
第二次调用:2

我的理解:

每调用一次AddOne就重新声明一个iInt变量,调用结束后销毁该变量,所以每次调用都输出2。

​ 事实上,关键字auto是可以省略的,如果不特别指定,局部变量的存储方式默认为自动的。

3.4.3.2 static变量

​ static变量为静态变量,将函数的内部变量和外部变量声明成static变量的意义是不一样的。不过对于局部变量来说,static变量是和auto变量相对而言的。尽管两者的作用域都仅限于声明变量的函数之中,但是在语句块执行期间,static变量将始终保持它的值,并且初始化操作只在第一次执行时起作用。在随后的运行过程中,变量将保持语句块上一次执行的值。

​ 在AddOne函数中定义一个static型的整型变量 iInt,在其中对变量进行加1操作。之后在主函数main中通过显示的提示语句,可以看到调用两次AddOne函数的输出,从结果中可以发现static变量的值保持不变。

#include<stdio.h>

void AddOne(){
    static int iInt = 1;	/*定义static整型变量*/
    iInt = iInt + 1;		/*变量+1*/
    printf("%d\n",iInt);	/*显示结果*/
}

int main(){
    printf("第一次调用:");	/*显示结果*/
    AddOne();				/*调用AddOne函数*/
    printf("第二次调用:");	 /*显示结果*/
    AddOne();				/*调用AddOne函数*/
    return 0;				/*程序结束*/
}

运行程序效果如下:

第一次调用:2
第二次调用:3

我的理解:

static变量在程序执行期间始终存在,即从程序开始执行直至程序结束时才会被销毁。即使函数执行结束后,静态变量的值也会被保留下来,下次函数调用时继续使用上次的值。
3.4.3.3 register变量

​ register变量称为寄存器存储类变量。通过register变量,程序员可以把某个局部变量指定存放在计算机的某个硬件寄存器中,而不是内存中。这样的好处是可以提高程序的运行速度。不过,这只是反映了程序员的主观意愿,实际上,编辑器可以忽略register对变量的修饰。

​ 用户无法获得寄存器变量的地址,因为绝大多少计算机的硬件寄存器都不占用内存地址。而且,即使编译器忽略register,而把变量存放在可以设定的内存中,也是无法获取变量地址的。

​ 如果想有效地利用寄存器register关键字,必须像汇编语言程序员那样了解处理器的内部结构,知道可用于存放变量的寄存器的数量、种类以及工作方式。但是,不同计算机对于这些细节可能是不同的,因此,对于一个具备可移植性的程序来说,register的作用并不大。

下面通过一个实例来介绍寄存器变量的使用方法。

#include<stdio.h>

int main(){
    register int iInt;		/*定义寄存器整型变量*/
    iInt = 100;	
    printf("%d\n",iInt);	/*显示结果*/
    return 0;				/*程序结束*/
}

运行结果

100
3.4.3.4 extern 变量

​ extern变量称为外部存储变量。extern声明了程序中将要用到但尚未定义的外部变量。通常,外部存储类都用于声明在另一个转换单元定义中定义变量。

​ 一个工程是由多个C文件组成的。这些源代码文件会分别进行编译,然后链接成一个可执行模块。把这样的一个程序作为一个工程进行管理,并且生成一个工程文件夹记录所包含的所有源代码文件。

下面通过一个实例来具体了解一下extern变量。

​ 在本实例中,首先在Extern1.c中定义一个iExtern变量,并为其进行赋值,然后在Extern2.c文件中使用iExtern变量,将其变量值显示到控制台。

文件Extern1.c

#include<stdio.h>

int main(){
    extern int iExtern;		/*定义外部整型变量*/
    printf("%d\n",iExtern); /*显示变量值*/
    return 0;				/*程序结束*/
}

文件Extern2.c

#include<stdio.h>

int iExtern=100;		/*定义一个整型变量,为其赋值100*/

运行结果

100

混合运算

​ 不同类型之间可以进行混合运算,如10+'a'-1.5+3.2*6。

​ 在进行这样的计算时,不同类型的数据要先转换成同一类型,然后再进行运算。转换的方式如图。

p3-混合运算.jpeg

例子

#include<stdio.h>

int main()
{
	int   iInt=1;					/*定义整型变量*/
	char  cChar='A';				/*ASCII码为65*/
	float fFloat=2.2f;				/*定义单精度型变量整型变量*/

	double result=iInt+cChar+fFloat;	/*得到相加的结果*/

	printf("%f\n",result);			/*显示变量值*/
	return 0;						/*程序结束*/
}

结果

68.200000

​ 在本实例中,将int型变量与char型变量、float型变量进行相加,将其结果存放在double类型的result变量中,最后使用printf函数将其输出。

C4 运算符与表达式

4.1 表达式

​ 表达式是C语言的主体。在C语言中,表达式由操作符和操作数组组成。最简单的表达式可以只包含有一个操作数。根据表达式所含操作符的个数,可以把表达式分为简单表达式和复杂表达式两种,简单表达式是只含有一个操作符的表达式,而复杂表达式是包含两个或两个以上操作符的表达式。

​ 下面通过几个表达式进行观察:

5+5
iNumber+9
iBase+(iPay*iDay)

​ 表达式本身什么事也不做,只是返回结果值。在程序不对返回的结果值进行任何操作的情况下,返回的结果值不起任何作用,也就是忽略返回的值。

​ 表达式产生的作用主要有以下两种情况:

  • 放在赋值语句的右侧(本章主要讲解)
  • 放在函数的参数中(C9章讲解)

表达式返回的结果值是有类型的。表达式隐含的数据类型取决于组成表达式的变量和常量的类型。

每个表达式的返回值都具有逻辑特性。如果返回值是非零的,那么该表达式返回真值,否则返回假值。通过这个特点,可以将表达式放在用于控制程序流程的语句中,这样就构建了条件表达式。

掌握表达式的使用:

​ 本实例中声明了3个整型变量,其中有对变量赋值为常数,还有将表达式的结果赋值给变量,最后将变量的值显示在屏幕上。

#include<stdio.h>

int main(){
    int iNumber1,iNumber2,iNumber3;		/*声明变量*/
    iNumber1=3;							/*为变量赋值*/
    iNumber2=7;
    
    printf("the first number is : %d\n",iNumber1);	/*显示变量值*/
    printf("the second number is :%d\n",iNumber2);
    
    iNumber3=iNumber1+10;		/*表达式中利用iNumber1变量加上一个常量*/
    printf("the first number add 10 is :%d\n",iNumber3);	/*显示iNumber3的值*/
    
    iNumber3=iNumber2+10;		/*表达式中用iNumber2变量加上一个常量*/
    printf("the second number add 10 is :%d\n",iNumber3);
    return 0;
}

(1) 在程序中,主函数main中的第1行代码是声明变量的表达式,可以看到使用逗号通过一个表达式声明3个变量。

在C语言中,逗号既可以作为分隔符,又可以用在表达式中。
1	逗号作为分隔符使用时,用于间隔说明语句中的变量或函数中的参数,如上面程序中声明变量时,就属于在语句中使用逗号,将iNumber1、iNumber2和iNumber3变量进行分割声明,使用代码举例如下:
    int iNumber1,iNumber2;		/*使用逗号分隔变量*/
	printf("the number is %d",iResult);		/*使用逗号分隔参数*/

2	逗号用在表达式中:可以将若干个独立的表达式联合在一起。其一般的表现形式如下:
	表达式1,表达式2,表达式3...
    
    其运算过程就是先计算表达式1,然后计算表达式2... ...依次计算下去。在循环语句中,逗号就可以在for语句中使用,例如:
	
	for(i=0;j=100;i++,j++){		/*在for语句中,使用逗号将表达式进行分隔*/
        k=i+j;
    }

(2) 接下来的语句是使用常量为变量赋值的表达式,其中 “iNumber1=3;” 是将常量3赋值给iNumber1, “iNumber2=7;” 语句是将7赋值给iNumber2,然后通过输出语句printf显示这两个变量的值。

(3) 在语句 “iNumber3=iNumber1+10;” 中,表达式将变量iNumber与常量10相加,然后将返回的值赋给iNumber3变量,之后使用输出函数printf将iNumber3变量的值进行显示。接下来将变量iNumber2与常量10相加,进行相同的操作。

(4) 在语句 “iNumber3=iNumber1+iNumber2;” 中,可以看到表达式中是两个变量进行相加,同样返回相加的结果,将其赋值给变量iNumber3,最后输出显示结果。

程序运行结果:

the first number is :3
the second number is :7
the first number add 10 is :13
the second number add 10 is :17

4.2 赋值运算符与赋值表达式

​ 在程序中尝尝遇到的赋值符号“ = ”就是赋值运算符,其作用就是将一个数据赋给一个变量。例如:

iAge = 20;

​ 这就是一次赋值操作,是将常量20赋给变量iAge。同样也可以将一个表达式的值赋给一个变量。例如:

Total = Counter*3;

4.2.1 变量赋初值

​ 在声明变量时,可以为其赋一个初值,就是将一个常数或者一个表达式的结果赋值给一个变量,变量中保存的内容就是这个常量或者赋值语句中表达的值。这就是为变量赋初值。

  • 将常量赋值给变量
类型 变量名 = 常量;

​ 其中的变量名也称为变量的标识符。通过变量赋初值的一般形式,以下是相关的代码实例:

char cChar = 'A';
int iFirst = 100;
float fPlace = 1450.78f;
  • 通过赋值表达式为变量赋初值
类型 变量名 = 表达式;

​ 可以看到,其一般形式与常数赋值的一般形式是相似的,例如:

int iAmount = 1+2;
float fPrice = fBase+Day*3;

​ 在上面的举例中,得到赋值的变量iAmount和fPrice称为左值,因为它出现的位置在赋值语句的左侧。产生值的表达式称为右值,因为它出现的位置在表达式的右侧。

​ 这是一个重要的区别,并不是所有的表达式都可以作为左值,如常数只可以作为右值。

​ 在声明变量的同时直接为其赋值的操作称为赋初值,也就是变量的初始化。先将变量声明,再进行变量的赋值操作也是可以的。例如:

int iMonth;		/*声明变量*/
iMonth = 12;	/*为变量赋值*/

为变量赋初值

​ 为变量赋初值的操作是编程时常见的操作。在本实例中,模拟钟点工的计费情况,使用赋值语句和表达式得出钟点工工作8个小时后得出的薪水。

#include<stdio.h>

int main(){
    int iHoursWorded = 8;		/*定义变量,为变量赋初值,表示工作时间*/
    int iHourlyRate;			/*声明变量,表示一个小时的薪水*/
    int iGrossPay;				/*声明变量,表示得到的工资*/
    
    iHourlyRate = 13;			/*为变量赋值*/
    iGrossPay = iHourWorded*iHourlyRate;		/*将表达式的结果赋值给变量*/
    
    printf("The HourseWorded is: %d\n",iHoursWorded);		/*显示工作时间变量*/
    printf("The HourlyRate is: %d\n",iHourlyRate);			/*显示一个小时的薪水*/
    printf("The GrossPay is: %d",iGrossPay);				/*显示工作所得的工资*/
    
    return 0;
}
  1. 钟点工的薪水是一个小时的工薪x工作的小时数量,因此在程序中需要3个变量来表示这个钟点工薪水的计算过程。iHoursWorded表示工作时间,一般的工作时间都是固定的,在这里为其赋初值为8,表示8个小时。iHourlyRate表示一个小时的工薪。iGrossPay表示钟点工工作8个小时后,应得到的工资。
  2. 工资是可以变化的,iHourlyRate变量声明之后,为其设定工资,设定为一个小时为13。根据步骤1中计算钟点工薪水的公式,得到总工薪的表达式,将表达式的结果保存在iGrossPay变量中。
  3. 最后通过输出函数,将变量的值和计算的结果在屏幕上进行显示。
The HourseWorded is: 8
The HourlyRate is: 13
The GrossPay is: 104

4.2.2 自动类型转换

​ 数值类型有很多种,如字符型、整型、长整型和实型等,因为这些类型的变量、长度和符号特性都不同,所以取值范围也不同。混合使用这些类型时会出现什么情况?第三章已经对此有所介绍。

​ C语言中默认存在一些自动类型转换规则。根据这些转换规则,数值类型变量可以混合使用。如果 把比较短的数值类型变量的赋值给比较长的数值类型变量,那么比较短的数值类型变量中的值会升级为较长的数值类型,数据信息不会丢失。但是,如果把较长的数值变量的值赋给比较短的数值类型变量,那么数据就会降低级别显示,当数据大小超过比较短的数值类型的可表示范围时,就会发送数据截断。

​ 有些编译器遇到这种情况时就会发出警告信息,例如:

float i = 10.1f;
int j= i;

此时编译器就会发出警告:

warning c4244: 'initializing' : conversion from from 'float' to 'int', possible loss of data

4.2.3 强制类型转换

​ 通过自动类型转换的介绍得知,如果数据类型不同,系统会根据不同情况自动进行类型转换,但是此时编译器会提示警告信息。这时如果使用强制类型转换告知编译器,就不会出现警告。

​ 强制类型转换的一般形式为:

(类型名) (表达式)

例如,在上述不同变量类型转换时使用强制类型转换:

float i = 10.0f;
int j = (int)i		/*进行强制类型转换*/

​ 在代码中可以看到,在变量前使用包含要转换类型的括号,这样就对变量进行了强制类型转换。

显示类型转换的结果。

​ 在本实例中,通过不同类型变量之间的赋值,将赋值操作后的结果进行输出,观察类型转换后的结果。

#include<stdio.h>

int main() {
    char cChar;             /*字符型变量*/
    short int iShort;       /*短整型变量*/
    int iInt;               /*整型变量*/
    float fFloat = 70000;   /*单精度浮点型*/

    cChar = (char)fFloat;   /*强制转换赋值*/
    iShort = (short)fFloat;
    iInt = (int)fFloat;

    printf("the char is: %c\n",cChar);      /*输出字符变量值*/
    printf("the long is: %ld\n", iShort);   /*输出短整型变量值*/
    printf("the int is: %d\n", iInt);       /*输出整型变量值*/
    printf("the float is: %f\n", fFloat);   /*输出单精度浮点型变量值*/

    return 0;
}

​ 在本实例中定义了一个单精度浮点型变量,然后通过强制转换将其赋给不同类型的变量。因为是由高的基本向低的级别转换,所以可能会出现数据丢失。在使用强制转换时要注意此问题。

运行结果:

the char is: p
the long is: 4464
the int is: 70000
the float is: 70000.000000

4.3 算术运算符与算术表达式

​ C语言中有两个单目算术运算符和5个双目运算符。在双目运算符中,乘法、除法和取模运算符比加法和减法运算符的优先级高。单目正和单目运算符的优先级最高。

4.3.1 算术运算符

​ 算术运算符包括两个单目运算符(正和负)和5个双目运算符(即乘法、除法、取模、加法和减法)。具体符号和对应的功能如表。

符号
+单目正%取模
-单目负+加法
*乘法-减法
/除法

​ 在上述算术运算符中,取模运算符 “%” 用于计算两个整数相除得到的余数,并且取模运算符的两侧均为整数,如7%4的结果是3。

​ 其中的单目正运算符是冗余的,也就是为了与单目运算符构成一对而存在的。单目运算符不会改变任何值,如不会将一个负值表达式改为正。

​ 运算符 “-” 作为减法运算符,此时为双目运算符,如5-3。“-” 也可作为负值运算符,此时为单目运算符,如-5等。

4.3.2 算术表达式

​ 在表达式中使用算术运算符,则该表达式称为算术运算符。下面是一些算术表达式的例子,其中使用的运算符就是上表中所列出的算术运算符:

Number=(3+5)/Rate;
Height=Top-Bottom+1;
Area=Height * Width;

​ 需要说明的是,两个整数相除的结果为整数,如7/4的结果为1,舍去的是小数部分。但是,如果其中的一个数是负数时会出现什么情况呢?此时机器会采取 “向零取整” 的方法,即为-1,取整后向0靠拢。

​ 如果用+、-、*、/运算的两个数中有一个为实数,那么结果是double型,这是因为所有的实数都按double型进行运算。

​ 在本实例中,通过在表达式中使用上面介绍的算术运算符,完成摄氏度计算,即把用户的华氏温度换算为摄氏度,然后显示出来。

#include<stdio.h>

int main(){
    int iCelsius,iFahrenheit;					/* 声明两个变量 */
    printf("请输入华氏温度 :\n");				/*显示提示信息*/
    scanf("%d",&iFahrenheit);					/*在键盘上输入华氏温度*/
    iCelsius = 5*(iFahrenheit-32)/9;			/*通过算术表达式进行计算,并将结果赋值*/
    
    printf("温度是:");						/*显示提示信息*/
    printf("%d",iCelsius);						/*显示摄氏温度*/
    printf(" 摄氏度\n");						/*显示提示信息*/
    return 0;
}
  1. 在主函数main中声明两个整型变量,iCelsius表示摄氏度,iFahrenheit表示华氏温度。
  2. 使用printf函数显示提示信息。之后使用scanf函数获得在键盘上输入的数据,其中%d是格式字符,用来表示输入有符号的十进制整数,这里输入80。
  3. 利用算术表达式,将获得的华氏温度转换成摄氏温度。最后将转换的结果进行输出,可以看到80是用户输入的华氏温度,而26是计算后输出的摄氏温度。

运行程序

请输入华氏温度 :
80
温度是:26 摄氏度

4.3.3 优先级与结合性

​ C语言中规定了各种运算符的优先级和结合性,首先来看一下有关算术运算符的优先级。

  1. 算术运算符的有优先级

    ​ 在进行表达式求值时,通常会按照运算符的优先级别从高到低次序执行。算术运算符中,*、/、%的优先

    别高于+、-的级别。例如,如果在表达式中同时出现 *和+,那么应先运算乘法:

    R=x+y*z;
    

    上述表达式中,因为*比+的优先级高,所以会先进行y * z的运算,最后加上x。

    ​ 在表达式中通常会出现这样的情况。例如,要进行a+b,再将结果与c相乘,一不小心将表达式写为a+b*c。因为 * 的优先级高于+,这样的话就会先执行乘法运算,显然不是期望得到的结果。这时应该怎么办呢?可以使用括号 “()” 将+运算级别提高,使其先进行运算,就可以得到预期的结果了,例如解决上式的方法是(a+b)*c。括号可以使其中的表达式进行运算的原因在于——括号在所有运算符中的优先级别是最高的。

  2. 算术运算符的结合性

    ​ 当算术运算符的优先级相同时,结合方向为 “自左向右”。例如:

    a-b+c
    

    ​ 因为减法和加法的优先级是相同的,所以b先与减号相结合,执行a-b的操作,然后执行加c的操作,这样的操作过程就称为 “自左向右的结合性”。在后面的介绍中还可以看到 “自右向左的结合性”。

    本章小结处将会给出有关运算符的优先级和结合性的表格。

    算术运算符的优先级和结合性

    ​ 在本实例中,通过不同运算符的优先级和结合性,使用printf函数显示最终的计算结果,根据结果体会优先级和结合性的概念。

    #include<stdio.h>
    
    int main(){
        int iNumber1,iNumber2,iNumber3,iResult=0;	/*声明整型变量*/
        iNumber1 = 20;
        iNumber2 = 5;
        iNumber3 = 2;
        
        iResult = iNumber1+iNumber2-iNumber3;
        printf("the result is: %d\n",iResult);
        
        iResult = iNumber1-iNumber2+iNumber3;
        printf("the result is: %d\n",iResult);
        
        iResult = iNumber1+iNumber2*iNumber3;
        printf("the result is:%d\n",iResult);
        
        iResult = iNumber1/iNumber2*iNumber3;
    	printf("the result is:%d\n",iResult);
    	
    	iResult = (iNumber1+iNumber2)*iNumber3;
    	printf("the result is:%d\n",iResult);
        return 0;
    }
    
    1. 在程序中先声明要用到的变量,其中iResult的作用是存储计算结果,为其他变量进行赋值。
    2. 使用算术运算符完成不同操作,根据这些不同操作输出的结果来观察优先级与结合性。
  • 根据代码“iResult = iNumber1+iNumber2-iNumber3;” 与 “iResult = iNumber1-iNumber2+iNumber3;”的结果,可以看出,相同优先级别的运算符根据结合性由左向右进行运算。
  • 语句 “iResult = iNumber1+iNumber2*iNumber3;” 与上面的语句进行比较,可以看出,不同级别的运算符按照优先级高低依次进行运算。
  • 语句 “iResult = iNumber1/iNumber2*iNumber3;” 又体现 出同优先级的运算符按照结合性进行运算。
  • 语句 “iResult = (iNumber1+iNumber2)*iNumber3;” 中使用括号提高优先级,使括号中的表达式先进行运算。从中可知,括号在运算符中具有最高优先级。

运行程序:

the result is: 23
the result is: 17
the result is:30
the result is:8
the result is:50

4.3.4 自增/自减运算符

​ 在C语言中还有两个特殊的运算符,即自增运算符 “++” 和自减运算符 “--”。自增运算符和自减运算符对变量的操作分别是增加1和减少1。

​ 自增运算符和自减运算符可以放在变量的前面或者后面,放在变量前面称为前缀,放在后面称为后缀。使用的一般方法如下:

--Counter;		/*自减前缀符号*/
Grade--;		/*自减后缀符号*/
++Age;			/*自增前缀符号*/
Height++;		/*自增后缀符号*/

​ 在上面这些例子中,运算符的前后位置不重要,因为所有得到的结果是一样的,自减就是减1,自增就是加1。

注意

​ 在表达式内部,作为运算的一部分,两者的用法可能有所不同。如果运算符放在变量前面,那么变量在参加表达式运算之前完成自增或者自减运算;如果运算符放在变量后面,那么变量的自增或者自减运算在变量参加了边大师运算之后完成。

例-比较自增、自减运算符前缀与后缀的不同。

​ 在本实例中定义一些变量,为变量赋相同的值,然后通过前缀和后缀的操作来观察在表达式中前缀和后缀的不同结果。

#include<stdio.h>

int main(){
    int iNumber1=3;		/*定义变量,赋值为3*/
    int iNumber2=3;		
    
    int iResultPreA,iResultLastA;		/*声明变量,得到自增运算的结果*/
    int iResultPreD,iResultLastD;		/*声明变量,得到自减运算的结果*/
    
    iResultPreA=++iNumber1;		/*前缀自增运算*/
    iResultLastA=iNumber2++;	/*后缀自增运算*/
    
    printf("The Addself ...\n");		
    printf("the iNumber1 is :%d\n",iNumber1);			/*显示自增运算后自身的数值*/
    printf("the iResultPreA is : %d\n",iResultPreA);	/*得到自增表达式中的结果*/
    printf("the iNumber2 is :%d\n",iNumber2);			/*显示自增运算后自身的数值*/
    printf("the iResultLastA is : %d\n",iResultLastA);	/*得到自增表达式中结果*/
    
    iNumber1=3;		/*恢复变量的值为3*/
    iNumber2=3;
    
    iResultPreD=--iNumber1;		/*前缀自减运算*/
    iResultLastD=iNumber2--;	/*后缀自减运算*/
    
    printf("The Deleteself ..\n");
    printf("the iNumber1 is :%d\n",iNumber1);			/*显示自减运算后自身的数值*/
    printf("the iResultPreD is : %d\n",iResultPreD);	/*得到自减表达式中结果*/
    printf("the iNumber2 is : %d\n",iNumber2);			/*显示自减运算后自身数值*/
    printf("the iResultLastD is : %d\n",iResultLastD);	/*得到自减表达式中的结果*/
    return 0;
}
  1. 在程序代码中,定义iNumber1和iNumber2两个变量用来进行自增、自减运算。
  2. 进行自减运算,分为前缀自增和后缀自增。通过程序最终的显示结果可以看到,自增变量iNumber1和iNumber2的结果同为4,但是得到表达式结果的两个变量iResultPreA和iResultLastA却不一样。iResultPreA的值为4,iResultLastA的值为3,因为前缀是的自增使得iResultPreA变量先进行自增操作,然后进行赋值操作;后缀自增操作是先进行赋值操作,然后进行自增操作。因此两个变量得到表达式的结果是不一样的。
  3. 在自减运算中,前缀自减和后缀自减与自增运算方式是相同的,前缀自减是先进行减1操作,然后赋值操作;而后缀自减是先进行赋值操作,再进行自减操作。

运行程序

The Addself ...
the iNumber1 is :4
the iResultPreA is : 4
the iNumber2 is :4
the iResultLastA is : 3
The Deleteself ..
the iNumber1 is :2
the iResultPreD is : 2
the iNumber2 is : 2
the iResultLastD is : 3

4.4 关系运算符与关系表达式

​ 在数学中,经常需要比较两个数的大小。在C语言中,关系运算符的作用就是判断两个操作数的大小关系。

符号功能
>大于<=小于等于
>=大于等于==等于
<小于!=不等于

注意

​ 符号 ">=" (大于等于) 与 “<=” (小于等于) 的意思分别是大于或等于、小于或等于。

4.4.1 关系运算符

​ 关系运算符用于对两个表达式的值进行比较,返回一个真值或者假值。返回真值还是假值,取决于表达式中的值和所用运算符。其中真值为1,假值为0,真值表示指定的关系成立,假值则表示指定的关系不成立。例如:

7>5		/*因为7大于5,所以该关系成立,表达式的结果为真值*/
7>=5	/*因为7大于5,所以该关系成立,表达式的结果为真值*/
7<5		/*因为7大于5,所以该关系不成立,表达式的结果为假值*/
7<=5	/*因为7大于5,所以该关系不成立,表达式的结果为假值*/
7==5	/*因为7不等于5,所以该关系不成立,表达式的结果为假值*/
7!=5	/*因为7不等于5,所以该关系成立,表达式的结果为真值*/

​ 关系运算符通常用来构造条件表达式,多用在流程处理控制语句中。例如,if语句是用于判断条件而执行语句块,在其中使用关系表达式作为判断条件,如果关系表达式返回的是真值,则执行下面的语句块;如果为假值,就不去执行。代码如下:

if(iCount<10){
    ...
}

​ 其中,if(iCount<10)就是判断iCount小于10这个关系是否成立,如果成立则为真,如果不成立则为假。

注意

​ 在镜像判断时,一定要注意等号运算符 “==” 的使用,千万不要与赋值运算符 “=” 弄混。如在if语句中进行判断,使用的是 “=”:

if(Amount=100){
    ...
}

​ 上面的代码看上去是在检验变量Amount是否等于常量100,但是事实上没有起到这个效果。因为表达式使用的是赋值运算符 “=” 而不是等于运算符 “==” 。赋值表达式 Amount=100,本身也是表达式,其返回值是100。既然是100,说明是非零值也就是真值,则该表达式的值始终为真值,没有起到进行判断的作用。如果赋值表达式右侧不是常量100,而是变量,则赋值表达式的真值就是由这个变量的值决定。

​ 因为这两个运算符在语言上存在差别,使用其构造条件表达式时很容易出现错误,新手在编写时一定要加以注意。

4.4.2 优先级与结合性

​ 关系运算符的结合性都是自左向右的。使用关系运算符时常常会判断两个表达式的关系,但是由于运算符存在着优先级的问题,因此如果不小心处理则会出现错误。例如,要进行这样的判断操作:

先对一个变量进行赋值,然后判断这个赋值的变量是否不等于一个常数,代码如下:

if(Number=NewNum!=10){
    ...
}

​ 因为 “!=” 运算符比 “=” 的优先级要高,所以NewNum!=10 的判断操作会在赋值之前实现,变量Number得到的就是关系表达式的真值或者假值,这样并不会按照之前的意愿执行。

​ 前文曾经介绍过括号运算符,其优先级具有最高性,因此应该使用括号来表示需要优先进行计算的表达式,例如:

if((Number=NewNum)!=10){
    ...
}

​ 这种写法比较清楚,不会产生混淆,没有人会对代码的含义产生误解。由于这种写法格式比较精确、简介,因此被多数程序员接受。

例-关系运算符的使用

​ 在本实例中,定义两个变量表示两个学科的分数,使用if语句判断两个学科的分数大小,通过printf输出函数显示信息,得到比较的结果。

#include<stdio.h>

int main(){
   	int iChinese,iEnglish;
   	printf("输入语文成绩:");
   	scanf("%d",&iChinese);
   	printf("输入英语成绩:");
   	scanf("%d",&iEnglish);
   	
   	if(iChinese>iEnglish){
	   	printf("语文成绩比英语成绩高\n");
	}
	if(iChinese<iEnglish){
		   	printf("英语成绩比语文成绩高\n");
	}
	if(iChinese==iEnglish){
			   	printf("英语成绩和语文成绩一样\n");
	}
    return 0;
}

​ 为了可以在键盘上得到两个学科的分数,定义变量iChinese和iEnglish。然后利用if语句进行判断,在判断条件中使用了关系表达式,判断分数是否使得表达式成立。如果成立,则返回真值:如果不成立,则返回假值。最后根据真值和假值选择输出语句。

运行程序

输入语文成绩:98
输入英语成绩:95
语文成绩比英语成绩高

4.5 逻辑运算符与表达式

​ 逻辑运算符根据表达式的真或则假属性返回真值或假值。在C语言中,表达式的值非零,那么其值为真。非零的值用于逻辑运算,则等价于1;假值总为0。

4.5.1 逻辑运算符

逻辑运算符有3种,如表4.3所示。

符号
&&逻辑与
||逻辑或
单目逻辑非

注意

​ 表中的逻辑与运算符 “&&” 和逻辑或运算符 “||” 都是双目运算符。

4.5.2 逻辑表达式

​ 前文介绍过,关系运算符可用于对两个操作数进行比较,使用逻辑运算符可以将多个关系表达式的结果合并在一起进行判断。其一般形式如下:

表达式 逻辑运算符 表达式

例如,使用逻辑运算符:

Result = Func1&&Func2;		/*Func1和Func2都为真时,结果为真*/
Result = Func1||Func2;		/*Func1、Func2其中一个为真时,结果为真*/
Result = !Func2;			/*如果Func2为真,则Result为假*/

​ 前面已经介绍过,但这里还要作重点强调,不要把逻辑与运算符 “&&” 和逻辑或运算符 “||” 与下面要讲的位与运算符 “&” 和位运算符 “|” 混淆。

​ 逻辑与运算符和逻辑或运算符可以用于相当复杂的表达式中。一般来说,这些运算符用来构造条件表达式,用在控制程序的流程语句中,例如在后面章节中要介绍if、for、while语句等。

​ 在程序中,通常使用单目逻辑非运算符 “!” 把一个变量的数值转换为相应的逻辑真值或者假值,也就是1或0。例如:

Result = !!Value;		/*转换成逻辑值*/

4.5.3 优先级与结合性

​ “&&” 和 “||” 是双目运算符,它们要求有两个操作数,结合方向自左至右;“!” 是单目运算符,要求有一个操作数,结合方向自左向右。

​ 逻辑运算符的优先级从高到低依次为单目逻辑非运算符 "!" 、逻辑与运算符 “&&” 和逻辑或运算符 “||”。

例-逻辑运算符的应用

​ 在本实例中,使用逻辑运算符构造表达式,通过输出函数显示表达式的结果,根据结果分析表达式中逻辑运算符的计算过程。

#include<stdio.h>

int main(){
   	int iNumber1,iNumber2;		/*声明变量*/
   	iNumber1=10;				/*为变量赋值*/
   	iNumber2=0;
   	
   	printf("the 1 is Ture,0 is False\n");		/*显示提示信息*/
   	printf("5<	iNumber1&&iNumber2 is %d\n",5<iNumber1&&iNumber2);		/*显示逻辑与表达式的结果*/
   	printf("5<	iNumber1||iNumber2 is %d\n",5<iNumber1||iNumber2);		/*显示逻辑或表达式的结果*/
   	iNumber2=!!iNumber1;												/*得到iNumber1的逻辑值*/
   	printf("iNumber2 is %d\n",iNumber2);								/*输出逻辑值*/
    return 0;
}
  1. 在程序中,先声明两个变量用来进行下面的计算。为变量赋值,iNumber1的值为10,iNumber2的值为0。
  2. 先进行输出信息,说明显示为1表示真值。在printf函数中,进行表达式的运算,最后将结果输出。分析表达式5<iNumber&&iNumber2,由于“&&”运算符的优先级高于 “<” 运算符,因此先执行与运算,之后进行关系判断。iNumber1的值为10,iNumber2的值为0,这个表达式的含义是数值5小于iNumber1的同时也必须小于iNumber2,很明显是不成立的,因此表达式返回的是假值。表达式5<iNumber||iNumber2的含义是数值5小于iNumber1或者iNumber2,此时表达式成立,返回值为真值。
  3. 将iNumber1进行两次单目逻辑非运算,得到的是逻辑值,因为iNumber1的数值是10,所以逻辑值为1。

运行程序

the 1 is Ture,0 is False
5<      iNumber1&&iNumber2 is 0
5<      iNumber1||iNumber2 is 1
iNumber2 is 1

4.6 位逻辑运算符与位逻辑表达式

​ 位运算是C语言中比较有特色的内容。位逻辑运算符可以实现位的设置、清零、取反和取补操作。利用位运算可以实现只有部分汇编语言才能实现的功能。

4.6.1 位逻辑运算符

​ 位逻辑运算符包括位逻辑与、位逻辑或、位逻辑非和取补,如表。

符号
&位逻辑与
|位逻辑或
^位逻辑非
~取补

​ 上表中除了最后一个运算符是单目运算符外,其他都是双目运算符,这些运算符只能用于整型表达式。位逻辑运算符通常用于对整型变量进行位的设置、清零和取反,以及对某些选定的位进行检测。

4.6.2 位逻辑表达式

​ 在程序中,位逻辑运算符一般被程序员用作开关标志。较低层次的硬件设备驱动程序,经常需要对输入、输出设备进行位操作。

​ 如下为位逻辑与运算符的典型应用,对某个语句的位设置进行检查;

if(Field & BITMASK)

​ 语句的含义是if语句对后面括号中的表达式进行检测。如果表达式返回的是真值,则执行下面的语句块,否则跳过该语句块不执行。其中,运算符用来对BITMASK变量的位进行检测,判断其是否与Field变量的位有相吻合之处。

4.7 逗号运算符与逗号表达式

​ 在C语言中,可以用逗号将多个表达式分隔开来。其中,用逗号分隔的表达式被分别计算,并且整个表达式的值是最后一个表达式的值。

逗号表达式称为顺序求值运算符。逗号表达式的一般形式如下:

表达式 1,表达式 2,...,表达式n

逗号表达式的求解过程是:先求解表达式1,再求解表达式2,一直求解到表达式n。整个逗号表达式的值是表达式n的值。

观察下面使用逗号运算符的代码:

Value=2+5,1+2.5+7;

上面语句中,Value所得到的值为7,而非12。整个逗号表达式的值不应该是最后一个表达式的值吗?为什么不等于12呢?答案在于优先级的问题,由于赋值运算符的优先级比逗号运算符的优先级高,因此先执行赋值的运算。如果要先执行逗号运算,则可以使用括号运算符,代码如下:

Value=(2+5,1+2,5+7)

使用括号之后,Value的值为12。

例-用逗号运算符分隔的表达式。

本实例中,通过逗号运算符将其他运算符结合在一起形成表达式,再将表达式的最终结果赋值给变量。根据变量的值分析逗号运算符的计算过程。

#include<stdio.h>

int main(){
   	int iValue1,iValue2,iValue3,iResult;	/*声明变量,使用逗号运算符*/
   	
   	iValue1=10;
   	iValue2=43;
   	iValue3=26;
   	iResult=0;
   	
   	iResult=iValue1++,--iValue2,iValue3+4;	/*计算逗号表达式*/
   	printf("the result is: %d\n",iResult);	/*将结果输出显示*/
   	
   	iResult=(iValue1++,--iValue2,iValue3+4);	/*计算逗号表达式*/
   	printf("the result is: %d\n",iResult);		/*将结果输出显示*/
    return 0;
}
  1. 在程序代码的开始处,声明变量时使用了逗号运算符,分隔声明变量。前文已经对此有所介绍。
  2. 将前面使用逗号分隔声明的变量进行赋值。在逗号表达式中,赋值的变量进行各自的计算,变量iResult得到表达式的结果。这里需要注意的是,通过输出可以看到iResult的值为10,从前面的讲解知道因为逗号表达式没有使用括号运算符,所以iResult得到第一个表达式的值。在第一个表达式中,iValue变量进行的是后缀自加操作,于是iResult先得到iValue1的值,iValue1再进行自加操作。
  3. 在第二个表达式中,由于使用了括号运算符,因此iResult变量得到的是第3个表达式iValue3+4的值,iResult变量赋值为30。

运行程序

the result is: 10
the result is: 30

4.8 复合赋值运算符

复合赋值运算符是C语言中独有的,实际这种操作是一种缩写形式,可以使得变量操作的描述方式更为简介。例如,在程序中为一个变量赋值:

Value=Value+3;

这个语句是对一个变量进行赋值操作,值为这个变量本身与一个整数常量3相加的结果值。使用复合赋值运算符可以实现同样的操作。例如,上面的语句可以改写成:

Value+=3;

这种描述更为简洁。关于上面两种实现相同操作的语句,赋值运算符和复合赋值运算符的区别在于后者:

  • 简化了程序,使程序精练。

  • 提高了编译效率。

    对于简单赋值运算符,如Func=Func+1中,表达式Func计算两次;对于复合赋值运算符,如Func+=1中,表达式Func仅计算一次。一般来说,这种区别于程序的运行没有太大的影响,但是如果表达式中存在某个函数的返回值,那么函数将被调用两次。

例-使用复合赋值运算符简化赋值运算

#include<stdio.h>

int main(){
   	int iTotal,iValue,iDetail;		/*声明变量*/
   	iTotal=100;						/*为变量赋值*/
   	iValue=50;
   	iDetail=5;
   	
   	iValue*=iDetail;				/*计算得到iValue变量值*/
   	iTotal+=iValue;					/*计算得到iTotal变量值*/
   	printf("Value is:%d\n",iValue);/*显示计算结果*/
   	printf("Total is: %d\n",iTotal);
    return 0;
}

从程序代码中可以看到,语句iValue*=iDetail中使用复合赋值运算符,表示的意思是iValue的值等于iValue*iDetail的结果。而iTotal+=iValue表示的是iTotal的值等于iTotal+=iValue的结果。最后将结果显示输出。

运行程序

Value is:250
Total is: 350

小结

运算符的优先级和结合性

优先级结合性
(最高)() [] ->.自左向右
! ~ ++ -- + - * & (type)sizeof自右向左
* / %自左向右
+ -自左向右
<< >>自左向右
< <= > >=自左向右
== !=自左向右
&自左向右
^自左向右
|自左向右
&&自左向右
||自左向右
?:自右向左
= += -= *= /= %= &= ^= |= <<= >>=自右向左
(最低),自左向右

C5 常用的数据输入/输出函数

5.1 语句

C语言的语句用来向计算机系统发出操作指令。一条语句经过编译后,会产生若干条机器码指令,实际程序中通常包含若干条语句,用于完成一定的操作任务。

注意

在编写程序时,声明部分不能算作语句。例如,“int iNumber;” 就不是一条语句,因为不产生机器的操作,只是对变量提前进行了定义。

通过前面的介绍可知,程序包括声明部分和执行部分,其中执行部分由语句组成。

5.2 字符数据输入/输出

​ 本节将介绍C标准I/O函数库中最简单也最容易理解的字符输入/输出函数getcharputchar

5.2.1 字符数据输出

输出字符数据使用的是putchar函数,作用是向显示设备输出一个字符。其语法格式如下:

int putchar(int ch);

使用该函数时,要添加一个头文件stdio.h。其中,参数ch为要进行输出的字符,可以是字符型变量或整型变量,也可以是常量。例如,输出一个字符A的代码如下:

putchar('A');

使用putchar函数也可以输出转义字符,如输出字符A:

putchar('\101');

使用putchar函数实现字符数据输出。

在程序中使用putchar函数,输出字符串 "Hello",并在输出完毕之后换行。

#include<stdio.h>

int main(){
    char cChar1,cChar2,cChar3,cChar4;	/*声明变量*/
    cChar1='H';							/*为变量赋值*/
    cChar2='e';
    cChar3='l';
    cChar4='o';
    
    putchar(cChar1);					/*输出字符变量*/
    putchar(cChar2);
    putchar(cChar3);
    putchar(cChar3);
    putchar(cChar4);
    putchar('\n');						/*输出转义字符*/
    return 0;
}

(1)要使用putchar函数,首先要包含头文件stdio.h。

(2)声明字符型变量,用来保存要输出的字符。

(3)为字符变量赋值时,因为putchar函数只能输出一个字符,如果要输出字符串,就需要多次调用putchar函数。

(4)当字符串输出完毕之后,使用putchar函数输出转义字符"\n"进行换行操作。

运行程序:

Hello

5.2.2 字符数据输入

字符数据输入使用的是getchar函数,其作用是从终端(输入设备)输入一个字符。getchar与putchar函数的区别在于没有参数。

getchar函数的语法格式如下:

int getchar();

使用getchar函数时也要添加头文件stdio.h,函数的值就是从输入设备得到的字符。例如,从输入设备得到一个字符赋给字符变量cChar,代码如下:

cChar=getchar();

注意:

​ getchar函数只能接收一个字符,该字符可以赋给一个字符变量或整型变量,也可以不赋给任何变量,只是作为表达式的一部分,如"putchar(getchar());"。这里,getchar函数作为putchar函数的参数,通过getchar函数从输入设备得到一个字符,然后通过putchar函数将字符输出。

使用getchar函数实现字符数据输入。

在本实例中,使用getchar函数获取在键盘上输入的字符,再利用putchar函数进行输出。在本实例演示了将getchar函数作为putchar函数表达式的一部分,输入和输出字符的方式。

#include<stdio.h>

int main(){
    char cChar1;		/*声明变量*/
    cChar1=getchar();	/*在输入设备得到字符*/
    putchar(cChar1);	/*输出字符*/
    putchar('\n');		/*输出转义字符换行*/
    getchar();			/*得到回车字符*/
    putchar(getchar()); /*得到输入字符,直接输出*/
    putchar('\n');		/*换行*/
    return 0;
}

(1)要使用getchar函数,首先要包括头文件stdio.h。

(2)声明变量cChar1,通过getchar函数得到输入的字符,赋值给cChar1字符型变量,然后使用putchar函数将变量输出。

(3)使用getchar函数得到输入过程中的回车符。

(4)在putchar函数的参数位置调用getchar函数,得到字符,并将得到的字符输出

A
A
a
a

在这个例子中,有一处使用getchar函数接收回车符,这是怎么回事呢?原来在输入时,当输入完A字符后,为了确定输入完毕,要按Enter键。回车符也算是一种字符,如果这里不进行获取,那么下次使用getchar函数时将得到回车符。下面来看一下不调用getchar函数获取回车符的情况。

使用getchar函数实现字符数据输入(取消获取回车符)。

#include<stdio.h>

int main(){
    char cChar1;		/*声明变量*/
    cChar1=getchar();	/*在输入设备得到字符*/
    putchar(cChar1);	/*输出字符*/
    putchar('\n');		/*输出转义字符换行*/
    putchar(getchar()); /*得到输入字符,直接输出*/
    putchar('\n');		/*换行*/
    return 0;
}

这里将getchar函数获取回车符的语句去掉了。运行程序,显示效果。

A
A
		这是回车符
		这是回车符

比较两个程序的运行情况,从程序的显示结果可以发现,程序没有获取第二次的字符输入,而是进行了两次回车操作。

5.3 字符串输入/输出

putchar和getchar函数都只能对一个字符进行操作,如果要进行一个字符串的操作则会很麻烦。C语言提供了两个对字符串进行操作的函数,分别是getsputs函数

5.3.1 字符串输出函数

字符串输出使用的是puts函数,作用是输出一个字符串到屏幕上。其语法格式如下:

int puts(char *str);

使用puts函数时,先要在程序中添加stdio.h头文件。其中,形式参数str是字符指针类型,可以用来接收要输出的字符串。例如,使用puts函数输出一个字符串:

puts("I LOVE CHINA!");		/*输出一个字符串常量*/

上述语句首先会输出一个字符串,之后会自动进行换行操作。这与printf函数有所不同,在前面的实例中使用printf函数进行换行时,要在其中添加转义字符 “\n”。puts函数会在字符串中判断“\0”结束符,遇到结束符时,后面的字符不再输出,并且自动换行。例如:

puts("I LOVE\0 CHINA!");		/*输出一个字符串常量*/

在上面的语句中,加上"\0"字符后,puts函数输出的字符串就变成了"I LOVE"。

前面的章节曾经介绍到,编译器会在字符串常量的末尾添加结束符"\0",这也就说明了puts函数会在输出字符串常量时最后进行换行操作的原因。

使用字符串输出函数显示信息提示。

在本实例中,使用puts函数对字符串常量和字符串变量进行操作,在这些操作中观察puts函数的使用方式。

#include<stdio.h>

int main(){
    char* Char="ILOVECHINA";	/*定义字符串指针变量*/
    
    puts("ILOVECHINA!");		/*输出字符串常量*/
    puts("I\0LOVE\0CHINA!");	/*输出字符串常量,其中加入结束符“\0” */
    puts(Char);					/*输出字符串变量的值*/
    Char="ILOVE\0CHINA!";		/*改变字符串变量的值*/
    puts(Char);					/*输出字符串变量的值*/
    return 0;
}

(1)字符串常量赋值给字符串指针变量。有关字符串指针的内容将会在后面的章节进行介绍,此时可以将其看作整型变量。为其赋值后,就可以使用该变量。

(2)第一次使用puts函数输出的字符串常量中,由于在该字符串中没有结束符“\0”,所以会完整输出整个字符串,直到最后编译器为其添加结束符“\0”为止。

(3)第二次使用puts函数输出的字符串常量中,人为添加了两个"\0",因此只能输出第一个结束符之前的字符,然后进行换行操作。

(4)第三次使用puts函数输出的字符串指针变量,函数根据变量的值进行输出。因为在变量的值中没有结束符,所以会完整输出整个字符串,直至最后编译器为其添加结束字符,然后进行换行操作。

(5)改变变量的值,再使用puts函数输出变量时,由于变量的值中包含结束符“\0”,因此将输出第一个结束符后之前的所有字符,然后进行操作。

ILOVECHINA!
I
ILOVECHINA
ILOVE

5.3.2 字符串输入函数

字符串输入使用的是gets函数,作用是将读取的字符串保存在形式参数str变量中,读取过程直到出现新的一行为止。其中新一行的换行字符将会转换为空终止符“\0”。gets函数的语法格式如下:

char *gets(char *str);

在使用gets函数输入字符串前,要为程序加入头文件stdio.h。其中,str字符指针变为形式参数。

例如,定义字符数组变量cString,然后使用gets函数获取输入字符的代码如下:

gets(cString);

在上面的代码中,cString变量获取到了字符串,并将最后的换行符转换成了终止符。

使用字符串输入函数gets获取输入信息。

#incldue<stdio.h>

int main(){
    char cString[30];		/*定义一个字符数组变量*/
    gets(cString);			/*获取字符串*/
    puts(cString);			/*输出字符串*/
    return 0;
}

(1)因为要接收输入的字符串,所以要定义一个可以接收字符串的变量。在程序代码中,定义cString为字符数组变量的标识符。关于字符数组的内容将在后面的章节中进行介绍,此处知道此变量可以接收字符串即可。

(2)调用gets函数,其中函数的参数为定义的cString变量。调用该函数后,程序会等待用户输入字符,当用户字符输入完毕按Enter键确定时,gets函数获取字符结束。

(3)使用puts字符串输出函数,将获取后的字符串进行输出。

I Love China!
I Love China!

5.4 格式输出函数

C6 选择结构程序设计

C7 循环控制

C8 数组

C9 函数

C10 指针

C11 结构体和共用体

C12 位运算

C13 预处理

C14 文件

C15 存储管理

C16 网络套接字编程

C17 学生成绩管理系统