C语言基础 2

一、       C语言编程问题

a)      第一要素就是先写出一个main函数结构
        1,函数头:
                int max_value(int x, int y)
           所谓的函数头包含三个重要信息:函数的名字、函数的返回值类型以及函数的参数列表。
           在这里例子中函数名字是 max_value,函数的返回值类型是 int,而其参数列表是(int x, int y)。这三样信息就是这个函数的接口。
        2,函数体:
                {
                    int z;
                    z = x>y ? x : y;
                    return z;
                }

b)     int main(int argc, char const *argv[]) 返回值为int
        第一个参数是外部传参的个数
        第二个参数是外部传参的具体参数

c)      外部传参的作用:一般作用于经常会改变的参数
复制代码

二、       运算符

a)      位运算符:

1、 ~ : 按位取反

image.png

image.png

2、   &:针对两个变量进行位与:有0就是0,同为1才是1

image.png

3、 :针对两个变量进行位与或:有1就是1,同为0才是0

image.png

4、^异或:相同为0,不同为1

image.png

5  左移右移:

            第一,如果右移时符号位补0,则称之为逻辑右移。
            第二,如果右移时符号位补原来符号位的拷贝,则称之为算术右移。
复制代码

左移右移的运算效率远高于乘法和除法,左移相当于乘法,右移相当于除法。

左移

左移就是把一个数的所有位都向左移动若干位,在C中用<<运算符.

例如:
        
        int i = 1;
        i = i << 2; //把i里的值左移2位

也就是说,1的2进制是000...0001(这里1前面0的个数和int的位数有关,32位机器,gcc里有31个0),左移2位之后变成 000...0100,也就是10进制的4,所以说左移1位相当于乘以2,那么左移n位就是乘以2的n次方了(有符号数不完全适用,因为左移有可能导致符 号变化,下面解释原因)
需要注意的一个问题是int类型最左端的符号位和移位移出去的情况.我们知道,int是有符号的整形数,最左端的1位是符号位,即0正1负,那么移位的时候就会出现溢出,例如:
        int i = 0x40000000; //16进制的40000000,为2进制的01000000...0000
        i = i << 1;
  那么,i在左移1位之后就会变成0x80000000,也就是2进制的100000...0000,符号位被置1,其他位全是0,变成了int 类型所能表示的最小值,32位的int这个值是-2147483648,溢出.如果再接着把i左移1位会出现什么情况呢?在C语言中采用了丢弃最高位的处 理方法,丢弃了1之后,i的值变成了0.

              左移里一个比较特殊的情况是当左移的位数超过该数值类型的最大位数时,编译器会用左移的位数去模类型的最大位数,然后按余数进行移位,如:

                      int i = 1, j = 0x80000000; //设int为32位
                      i = i << 33; // 33 % 32 = 1 左移1位,i变成2
                      j = j << 33; // 33 % 32 = 1 左移1位,j变成0,最高位被丢弃

              在用gcc编译这段程序的时候编译器会给出一个warning,说左移位数>=类型长度.那么实际上i,j移动的就是1位,也就是33%32后的余数.在gcc下是这个规则,别的编译器是不是都一样现在还不清楚.

             总之左移就是: 丢弃最高位,0补最低位
复制代码

右移

再说右移,明白了左移的道理,那么右移就比较好理解了.

右移的概念和左移相反,就是往右边挪动若干位,运算符是>>.
右移对符号位的处理和左移不同,对于有符号整数来说,比如int类型,右移会保持符号位不变,例如:
         int i = 0x80000000;
         i = i >> 1; //i的值不会变成0x40000000,而会变成0xc0000000
就是说,符号位向右移动后,正数的话补0,负数补1,也就是汇编语言中的算术右移.同样当移动的位数超过类型的长度时,会取余数,然后移动余数个位.
负数10100110 >>5(假设字长为8位),则得到的是 11111101
复制代码

总之,在C中,左移是逻辑/算术左移(两者完全相同),右移是算术右移,会保持符号位不变.实际应用中可以根据情况用左/右移做快速的乘/除运算,这样会比循环效率高很多.

例:C语言中左移<<表示乘以2,右移>>表示除以2,这是由计算机工作原理导致的!但是要是7,二进制数为0111,右移一位得3.5,但是右移之后二进制数变成0011,是3。不一样啊,怎模解释呢??
移位操作符的两个操作数必须是整型的。整个移位表达式的值的类型也是整型的,而且,左移位操作符与右移位操作符的运算并不对称。0111右移一位是把最后一位的1去掉,左边补个0,得0011,转换为十进制是3,这是正确的。并不等同于除以2 。
复制代码

b)     负数存储

                 i.          大端序:低地址存高字节序

                ii.          小端序:低地址存低字节序

c)      负数存储时是正数的二进制取反再补码。

d)     三目运算符:?:  

 

三、       循环控制语句

w

1、while 循环

当想要循环地执行某些语句的时候,while 循环语句可以达到此目的,其标准语法格式如下

image.png

其中 expression 可以是任意表达式(C 语言中任何表达式都有一个确定的值),while 语句根据表达式 expression 的值来决定是否执行下面的 statement(被执行的这些语句也叫 循环体,可以是一句简单的语句,也可以是用花括号括起来的若干条语句组合起来的复合语 句),如果 expression 的值为假则跳过 statement,如果为真则执行 statement,执行完了 再来判断 expression 的值,不断循环一直到其值为假为止(如果 expression 的值始终为真, 则称为死循环),其执行流程如下所示

image.png
由图可以清楚地看到 while 循环的执行逻辑,执行流从上往下运行,遇到 while 循环先来计算表达式 p,如果表达式 P 的值为真则执行语句 A,否则跳出循环。因此 while 循环也被称为“入口条件循环”,因为它在执行循环体之前先测试循环条件(即表达式 p 的值),如果一开始表达式 p 的值就为假,那么循环体一次都不会被执行。

2、do…while 循环

image.png
其执行逻辑是:不管三七二十一把 statement 执行一遍,然后判断 expression 的值是否 为真,如果为真则再执行一遍 statement,不断循环一直到 expression 的值为假为止。另外 需要特别注意,do…while 循环最后必须要有一个分号作为结尾(while 循环中的 while 之后没有分号,如果有分号则该分号形成的空语句就会成为循环体)。下面是其执行流程图
image.png
do…while 先执行循环体语句 A,再来判断表达式 P 的值,以此决定是否继续循环。

3、for 循环

for 循环是一种更为灵活的循环结构,在 Linux 内核中出现的频率大约是 while 循环和 do…while 循环的四到五倍,下面是它的一般形式:

for(initialize; test; update)

statement

其中的语句块 statement 跟以上两种循环体一样,可以是单条语句也可以是用花括号括起来的复合语句。关键字 for 之后的圆括号中包含有三个表达式,它们的名字显示了它们作为 for 循环结构的一般用途,即:

第一个表达式一般用来初始化循环控制变量

第二个表达 式一般用作循环测试条件

而第三个表达式则一般用来更新循环控制变量,但从语法角度上 讲它们可以是任意的语句。

for 循环结构的执行逻辑:

1、 如果有 initialize 语句则执行它,然后跳到第2步,如果没有该语句则直接跳到第2步。 
2、如果有test语句且其值为真或者没有该语句则跳到第3步,否则如果有test语句且其值为假则跳出循环体。 
3、执行循环体语句 statement。完了执行第4步。 
4、如果有 update 语句则执行它,然后跳到第 2 步,如果没有 update 语句则直接跳到第2步。 由上述叙述可知,for 循环中的三个表达式是可以省略的,但是它们之间的两个分号不能省略
复制代码

4、分支控制

跟循环控制类似,分支控制也是 C 语言最基本的程序控制语法,用来使得某些语句在特定条件下被执行或者不被执行,大大提高了程序执行的灵活性。

a、两路分支 if else if

所谓的两路分支,指的是一种非此即彼的逻辑关系,如下图所示

image.png

非此即彼的分支关系 实现这样逻辑代码非常简单,如下:

if( expression ) // 当 expression 为真时,执行 statements 1 
{ statements 1 } 
else // 当 expression 为假时,执行 statements 2 
{ statements 2 } 
复制代码

上例中,else 语句代表了除 expression 为真以外的所有可能的情况,此语句可以省略,但是如果有else语句,它必须跟if

b、多路分支 switch

switch语句需要关注的语法点:

1. switch( expression ) 语句中的 expression 可以是变量、运算表达式甚至是函数调用,但其结果必须保证是整型。 
2. case constant 语句中的 constant 必须是整型常量,const 型变量都不行,但可以是 char 型常量,例如 'w'。 
3. 一旦判断 expression 的值与某一个 case 语句的 constant 的值相等,即从该 case 语句顺序往下执行,一直遇到 break 语句或者直到switch 结构结束为止。 
4. 当没有任何 case 语句中的 constant 与 switch 语句中的 expression 相等时,执行 default 语句,但是该语句不是必须的,可以不写
复制代码

c、直接跳转

goto出错处理中用得最多,小白不建议用,大牛喜欢用

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享