Standard C Episode 2

源程序中,但凡可以出现某种类型的数据的地方都可以用同类型的表达式替换。一个表达式的类型,取决于多个方面.

操作符有单目操作符和双目操作符, 还有三目操作符(? : 是C语言唯一的一个三目操作符)。"目"指的是操作数的个数!
C语言没有指定同一操作符的多个操作数的计算顺序(除了&& || ? : 和,)。所以语句x = f() +
g();的计算结果依赖不同的编译器。

 1 /*
 2  * 文件名: 3mu.c
 3  * 描述:(1)一个表达式的类型取决于多个方面(表达式的操作数类型)
 4  *         (2)C语言没有指定同一操作符的多个操作数的计算顺序,
 5  *          除了: 逻辑与(&&) 逻辑或(||) 三目操作符(? : ) 逗号操作符(,)
 6  *          她们的操作数计算顺序是自左而右,当可以得到计算结论时就停止计算,返回计算结果
 7  *
 8  *
 9  * */
10 #include <stdio.h>
11
12 int main()
13 {
14     char c = ‘a‘;
15     double d = 0.0;
16
17     printf("c是%c, d是%f\n", c, d);// a, 0.000000
18     c ? c = ‘A‘ : (d = 10.99); // 三目操作符号的运算从左至右,得出计算结论就不再计算,该行代码会导致c被赋值‘A‘,不会导致d被赋值
19
20     //c ? c = ‘A‘ : d = 10.99; //ERROR 赋值操作符号优先级低于三目操作符,三目操作符的表达式运算结果是右值! 也就是说该行语句等价于(c ? c = ‘A‘ : d) = 10.99;
21          /*编译器gcc报错如下:
22           *  3mu.c: 在函数‘main’中:
23           *  3mu.c:9:28: 错误:赋值运算的左操作数必须是左值
24           *       c ? c = ‘A‘ : d = 10.99
25       *                       ^
26           */
27
28     printf("c是%c, d是%f\n", c, d);// A, 0.000000
29     printf("表达式 c ? c = ‘A‘ : (d = 10.99) 的类型是double, 表达式的值将占有空间 %d Byte\n", sizeof(c ? c = ‘A‘ : (d = 10.99)));//sizeof()操作符计算表达式值占有的空间大小时候会全面考虑表达式所有的操作数
30
31
32     return 0;
33 }

逗号操作符,可以连接多个独立的表达式, 最后一个表达式的计算结果将作为逗号操作运算的结果。

自增自减操作符
++是自增操作符,--是自减操作符。他们可以对变量进行操作,可以实现把变量内部的数字加一或者减一。这两个操作符写在变量前面的时候被称为"前缀操作
符",写在后面是被称为"后缀操作符"。这两种情况下,其效果都是将变量的值加一或者减一。但是,它们之间有一点不同。作为前缀操作符,会先完成自增或者
自减,然后再供其他操作使用,作为后缀操作符,会在完成自增或者自减的同时,提供一个自增自减前的值给其他操作使用。

实际上可以进一步理解后++/后--
    int i = 1;
    int j = 0;
    j = i++;//OK.

++i = 200;// 在C中,前加加表达式计算结果是右值,该条语句ERROR。但是在C++中,前加加表达式计算结果是个左值,该条语句OK。
    i++ = 100;//ERROR.错误:赋值运算的左操作数必须是左值。 而无论C还是C++中,表达式i++的计算结果是个右值. 
    /*
     * 对于表达式i++;编译器将按照如下步骤处理:
     * step1:制作一个i的副本(临时的匿名的变量,这里临时记作i_bak.)
     * step2:给i增加1
     * step3:将i的副本i_bak返回作为表达式i++的计算结果
     *
     * 结论:(1)C/C++中,基本类型的临时匿名变量是不可更改的,是个典型的右值。所以表达式的计算结果一般都是个右值。但是在C++中,前加加表达式计算结果是个左值
     *         (2)C/C++中,后置++/后置--计算结果导致操作数增加或者减少1。
     */

 1 #include <stdio.h>
 2
 3  int main(int argc, char *argv[])
 4 {
 5      char i = 0x80;// -128
 6      ++i = 100;//C++编译器不会报错。C++规范里面认定前加加表达式计算结果为左值。
 7                      // C编译器会报左值错误。C规范里无论前加加还是后加加表达式计算结果都是右值
 8
 9      // i++ = 127; //ERROR
10
11      return 0;
12  }

逻辑运算(逻辑运算是指计算结果只有真假两种结果的运算)操作符:
== > < >= <= !=
逻辑非(!)   逻辑与(&&)      逻辑或(||)
按位求反(~) 按位与AND(&)    按位或OR(|) 按位异或XOR(^)

/*请理解这条语句 3 > 5 && ++num;*/
    3 > 5 && ++num;//计算机中逻辑运算(逻辑与和逻辑或)存在短路特性。当已经判定逻辑表达式运算结果,那么不管逻辑表达式是否全部判定结束都停止判定。
    printf("num是%d\n", num);//num的打印结果是0
    3 < 5 || ++num;//逻辑或的短路特性
    printf("num是%d\n", num);//num的打印结果依然是0

对于逻辑运算,要知道C语言中任何数据信息都可以参与逻辑运算。参与的规则:只有零才是假,其他都是真"

位运算。位运算操作符:
                  按位取反(~)
                  按位与AND(&)
                  按位或OR(|)
                  按位异或XOR(^)
 
                 左移<<
                 右移>>
(特别注意:机器数采用的二进制编码方式是补码,补码参与运算,计算结果依然是补码)
&是按位与,通常可以用按位与运算把某个机器数中特定的位清零。举个例子:判断一个int
integer 的奇偶性可以这样做 (integer & 0x1),这样做的好处是计算速度快。

|是按位或(OR), 通常可以用按位或运算把某个机器数中特定位置为1。

^是按位异或(XOR), 通常可以用按位异或运算把某个机器数特定位求反.举个例子:求一个int integer的相反数可以这样做 (integer ^ 0x11111111) + 1, 这样做的好处是计算速度快。

~是按位求反,按位求反实现了对某个机器数按位求反。

<<是按位左移,左移会给右边补零,左移一位数值扩大一倍。
>>是按位右移,对于无符号类型,右移会给左边补零;对于有符号类型,右移会给左边补符号位;右移一位数值缩小1/2倍

 1 /*
 2  * 文件名: swap.c
 3  * 描述:不引入第三个变量,交换两个变量的值
 4  * */
 5 #include <stdio.h>
 6
 7 int main(void)
 8 {
 9     char a = ‘A‘, b = ‘B‘;
10     printf("a是%c, b是%c\n", a, b);
11     a = a ^ b;
12     b = b ^ a;
13     a = a ^ b;
14     printf("a是%c, b是%c\n", a, b);
15 }

C语言中可以使用强制类型转换把任何一种数据类型的数据当成另外一种类型的数据来使用。

隐式类型转换会把占地小的数据类型转换为占地大的数据类型,如果数据类型占地大小一样则会把有符号的数据类型转换成无符号类型的数据。例如:
/*请理解如下代码*/
    int num = -10;
    unsigned int num2 = 3;
    num = (num + num2) > 0 ? 1 : -1;//隐身类型转换原则:把占有空间小的调整为占有空间大的,如果占有空间一样,则把有符号的数据转换为无符号数据
    printf("(num + num2) > 0 ? 1 : -1 是%d\n", num);//num是1

 1 /*
 2  * 若char c = 0xFF, 则 输出 c 十进制是-1, 十六进制是ffffffff
 3  * 若 强转c为unsigned char, 即(unsigned char)c, 则 输出 c 十进制是255, 十六进制是ff
 4  *
 5  */
 6
 7 #include <stdio.h>
 8
 9 int main(int argc, char *argv[])
10 {
11         char c = 0xFF;
12         printf("若char c = 0xFF, 则 输出 c 十进制是%d, 十六进制是%0x\n", c, c);
13         printf("若 强转c为unsigned char, 即(unsigned char)c, 则 输出 c 十进制是%d, 十六进制是%0x\n", (unsigned char)c, (unsigned char)c);
14         return 0;
15 }

所以大概可以看出来:C是向下泛型的,而我们知道C++是向上泛型的。

范例学习:

 1 /*
 2  *操作符练习 操作符的优先级和结合性练习
 3  */
 4
 5 #include <stdio.h>
 6 int num3 = 0;//这里安排这个变量,意图在于测试语句num2 = num3++ + ++num3;
 7 main()
 8 {
 9     int num = 0, num2 = 0;
10     num = (3, 7);
11     printf("num是%d\n", num);
12     //num++;
13     //++num;
14     //num--;
15     --num;
16     printf("num是%d\n", num);
17
18     //num2 = ++num;//前置加加操作符优先级非常高,基本上属于最高(优先级只低于 () [] -> 和 .)
19     num2 = num++;//后置加加操作符
20     printf("num是%d, num2是%d\n", num, num2);//num是7 num2是6
21
22
23     num2 = num++ + ++num;/*不要这么写语句,这样的语句具体执行结果依靠编译器,不同编译器编译时处理方式不一。 例如:num在局部变量和全局变量两种情况下是不同编译器处理方式就不一样*/
24     printf("num是%d, num2是%d\n", num, num2);//num是9 num2是16
25
26     num3 = 7;
27     num2 = num3++ + ++num3;/*num3为全局变量。同样的语句当num3为局部变量时候,运算结果可能就不一样,这取决于编译器在这种情况下的处理方案*/
28     printf("num2是%d, num3是%d\n", num2, num3);//num2是16 num3是9
29 }
 1 /*
 2  * 逻辑操作符练习
 3  * 重点掌握操作符的结合性和优先级
 4  * 逻辑运算是指计算结果只有真假两种结果的运算。
 5  * 逻辑运算操作符:== > < >= <= !=
 6  *                 逻辑非(!)   逻辑与(&&)      逻辑或(||)
 7  *                 按位求反(~) 按位与AND(&)    按位或OR(|) 按位异或XOR(^)
 8  * C语言中任何数据信息,都可以参与逻辑运算。规则:只有零才是假,其他都是真
 9  * */
10
11 #include <stdio.h>
12
13 main()
14 {
15     int num = 0;
16
17     printf("3 == 5是%d\n", 3 == 5);
18     printf("3 != 5是%d\n", 3 != 5);
19     printf("3 < 5是%d\n", 3 < 5);
20     printf("3 > 5是%d\n", 3 > 5);
21     printf("3 >= 5是%d\n", 3 >= 5);
22     printf("3 <= 5是%d\n", 3 <= 5);
23
24     printf("3 < 7 < 5是%d\n", 3 < 7 < 5);//操作符<的结合性是"自左而右" 逻辑表达式3 < 7 < 5计算结果是1
25     printf("3 < 7 && 7 < 5是%d\n", 3 < 7 && 7 < 5);
26     printf("3 < 7 || 7 < 5是%d\n", 3 < 7 || 7 < 5);
27
28
29     /*请理解这条语句 3 > 5 && ++num;*/
30     3 > 5 && ++num;//计算机中逻辑运算(逻辑与和逻辑或)存在短路特性。当已经判定逻辑表达式运算结果,那么不管逻辑表达式是否全部判定结束都停止判定。
31     printf("num是%d\n", num);//num的打印结果是0
32     3 < 5 || ++num;//逻辑或的短路特性
33     printf("num是%d\n", num);//num的打印结果依然是0
34
35     printf("!6是%d\n", !6);
36     printf("3 + 5 >= 1是%d\n", 3 + 5 >= 1);//双目操作符+ -优先级高于逻辑操作符>=
37
38     return 0;
39 }
 1 /*
 2  * 位运算。位运算操作符:
 3  *                 按位取反(~)
 4  *                 按位与AND(&)
 5  *                 按位或OR(|)
 6  *                 按位异或XOR(^)
 7  *
 8  *                 左移<<
 9  *                 右移>>
10  * */
11
12
13 #include <stdio.h>
14
15 main()
16 {
17     printf("3 & 5是%d\n", 3 & 5);
18     printf("0x0003 & 0x0005是%x\n", 0x0003 & 0x0005);
19     /*通常可以用按位与运算把某个机器数中特定的位清零*/
20     printf("0x00FE & 0x00C9 是%X\n", 0x00FE & 0x00C9);//表达式0xFE & 0xC9实现了把特定位(第2.3.5.6位)置零
21
22     printf("3 | 5是%d\n", 3 | 5);
23     printf("0x0003 | 0x0005是%X\n", 0x0003 | 0x0005);//表达式0x0003 | 0x0005实现了把特定位(第1.3位)置1
24
25     printf("3 ^ 5是%d\n", 3 ^ 5);
26     printf("0x005A ^ 0x0026是%X\n", 0x005A ^ 0x0026);//表达式0x005A ^ 0x0026实现把特定位(第2.3.6位)求反
27     printf("0x005A相反数是%X\n", (0x005A ^ 0xFFFF) + 1);//用按位异或XOR巧妙地获得了0x005A的相反数
28
29     printf("~0xffff是%X\n", ~0xffff);
30
31     printf("0xffffffff<<2是%X\n", 0xffffffff<<2);
32     printf("2<<1是%d\n", 2<<1);//左移会给右边补零,左移一位数值扩大2倍
33     printf("0x00000002<<1是0x%X\n", 0x00000002<<1);
34
35     printf("0xffffffff>>2是%X\n", 0xffffffff>>2);//对于源程序中出现的整型常量,编译器默认会将其认定为unsigned int型
36     printf("2>>1是%d\n", 2>>1);//对于无符号类型数据,右移会给左边补零,对于有符号类型数据,右移会给左边补符号位,右移一位数值缩小1/2倍
37     printf("0x00000002>>1是0x%X\n", 0x00000002>>1);
38 }
 1 /*
 2  * 隐式类型转换练习
 3  * */
 4 #include <stdio.h>
 5 main()
 6 {
 7     int num = -10;
 8     unsigned int num2 = 3;
 9     num = (num + num2) > 0 ? 1 : -1;//隐身类型转换原则:把占有空间小的调整为占有空间大的,如果占有空间一样,则把有符号的数据转换为无符号数据
10     printf("(num + num2) > 0 ? 1 : -1 是%d\n", num);//num是1
11
12     printf("3 > 4 ? 1 : 0.9占有的存储空间是%d\n", sizeof(3 > 4 ? 1 : 0.9)); // 输出8, 因为sizeof根据类型判定表达式结果所占存储空间,                                                                            // 所以根据类型隐式转换规则,最终应该以double所占空间为准。                                                                            // 注意:源程序中的文字 0.9 编译器默认为double类型。
13
14 }
 1 /*
 2  * 强制类型转换
 3  * */
 4 #include <stdio.h>
 5
 6 main()
 7 {
 8     int num = 300;
 9     printf("num是%d,地址是0x%p\n",  num, &num);
10     printf("num是%d,地址是0x%p\n", (unsigned char) num, & num);
11     printf("num是%d,地址是0x%p\n",  num, &num);
12
13 }
时间: 2024-10-21 12:26:52

Standard C Episode 2的相关文章

Standard C episode 1

计算机内存可以记录大量的信息,只有记录在内存中的信息才是可以使用的.计算机的内存管理者是操作系统.程序可以根据需要向操作系统申请存储区,所有存储区必须先申请并经过操作系统分配后才能由程序使用.我们知道计算机里的信息可以简单划分为: /指令       /控制信息 信息 -            \控制字       \数据信息                     |              /              \      数值信息     非数值信息       /    \ 

Standard C Episode 3

程序语言中的控制流语句用于控制各计算操作执行的次序.控制流结构有:(1)顺序结构(2)分支结构(3)循环结构 if...else语句是经典的分支结构控制语句,同样"switch (表达式)...case 常量表达式" 语句也可以实现分支处理."表达式"可以是任何表达式,"常量表达式"也可以是任何常量表达式.case分支一般包含多条语句时也不必用大括号括起来,因 为每一个case分支只会给出switch分支结构中语句执行的开始位置,而不确定终止位置

Standard C Episode 5

理解变量标识符的作用域和可见性,以及变量生存期. (1)标识符的作用域和可见性     作用域:作用域是一个标识符在源程序中有效的区域.     可见性:程序运行到某一点,能够引用到的标识符,就是该处可见的标识符. (2)变量生存期:     变量的生存期就是指变量从创建到销毁的时间范围.变量按照生存期分“静态生存期”和“动态生存期”.静态生存期:如果变量的生存期与程序的运行期相同,则称该变量具有“静态生存期”; 动态生存期:变量生存期始于创建声明点,结束于作用域结束处. 注意:源程序声明(定义

Standard C Episode 4

稍微简单的复杂数据类型:数组 计算机程序处理的对象是数据信息(计算机中的信息包括数据信息和控制信息),计算机内存可以存储大量信息,姑且可以认为只有存储在内存中的数据信息才是可 以使用的.内存由操作系统管理,程序要使用内存存储空间需要向操作系统申请并被成功分配后才可以使用,C语言提供的变量声明(定义)语句可以实现向操作系 统申请存储空间. C语言提供的变量声明(定义)语句实现向操作系统申请存储区(存储空间),变量名有"两个代表",一是代表所申请到的存储区,一是代表存储区里面存放的数 据信

Standard C Episode 6

字符串.字符串是以'\0'结尾的字符序列.C语言字符串在内存中的存储规则:按照串中字符出现的顺序在内存中连续存储,末尾是字符串结束符'\0'. 注:'\0'(查ASCII码表可知'\0'就是0)是表示字符串的字符,它的位置决定了一个字符串中有效字符的个数. 字符串常量.字符串常量在源程序词法记号中属于"文字",它和其他文字"数字.布尔文字.以及字符"一样.但区别是“字符串常量文字代表的是首字符地址”. 注:编译器编译时候会把源程序中重复出现的相同文字只存储一回.多个

Standard C Episode 8

C语言函数和程序结构 通过函数可以把大的计算任务分解成若干个较小任务,从而使得思路更加清晰,同时函数也大大提高了代码的复用率,提高了工作效率.要注意的是多函数之间应该尽可能地高聚合低耦合.另一方面,一个程序可以保存在一个或者多个源文件中.各个文件可以单独编译. 注意:一般地,头文件中不会放那些在程序运行时发生具体效应的语句.例如声明(定义)一个变量的语句是不会放在头文件中的.再例如结构体声明(定义)语句就可以放头文件里,但结构体变量声明(定义)语句是不会放在头文件中的. make工具可以用来进行

Standard C Episode 9

typedef关键字可以给数据类型起别名. 结构体.结构体可以如下定义一个结构体变量 1 /* 2 * 结构体 3 * */ 4 #include <stdio.h> 5 /*struct { 6 int age; 7 char gender; 8 float height; 9 } student;//声明(定义)无名结构体的同时定义了结构体变量student 10 */ 11 /*struct student { 12 int age; 13 char gender; 14 float h

Standard C Episode 10

标准库函数malloc/calloc/realloc以及free的堆内存分配与回收 1 /* 2 * malloc练习 3 * 4 * */ 5 6 #include <stdlib.h> 7 #include <stdio.h> 8 9 int main() 10 { 11 int *p_value = NULL; 12 13 p_value = (int *) malloc (1 * sizeof(int)); 14 if (p_value) { 15 printf("

Standard C Episode 7

编译预处理 #define可以定义宏.宏可以带参数,叫做带参数的宏,其参数叫做宏的参数. #undef 可以解除宏的定义. 1 /* 2 宏练习 3 */ 4 5 #include <stdio.h> 6 #define NEG( r) r = 0 - r 7 8 main() 9 { 10 int value = 4; 11 NEG(value); 12 printf("value是%d\n", value); 13 } 1 /* 2 宏练习 3 */ 4 #includ