再学C语言

1.程序设计的步骤
(1)问题分析
(2)设计算法
(3)编写程序
(4)测试
(5)编写程序文档
2.厨师制作菜肴,需要有菜谱,菜谱上一般应说明:
(1)所用原料,指出为了做出顾客所指定的菜肴,应该使用哪些材料;(类似于数据结构)
(2)操作步骤,指出有了这些原料,应按什么样的步骤进行加工,才能做出所需的菜肴。(类似于算法)
3.不要认为只有“计算”的问题才有算法。广义地说,为解决一个问题而采取的方法和步骤,就称为“算法”。例如,描述太极拳动作的图解,就是“太极拳的算法”。一首歌曲的乐谱,也可以称为改歌曲的算法,因为它指定了演奏改歌曲的每个步骤,按照它的规定就能演奏出预订的曲子。
4.算法的特点:
(1)有穷性
(2)确定性
(3)有效性
(4)0个或多个输入
(5)1个或多个输出
5.结构化程序设计方法的基本思路是:把一个复杂问题的求解过程分阶段进行,每个阶段处理的问题都控制在人们容易理解和处理的范围内。
(1)自顶向下
(2)逐步细化
(3)模块化设计
(4)结构化编码
6.常用的常量有以下几种
(1)整型常量
(2)实型变量(十进制小数形式、指数形式)
(3)字符常量(普通字符、转义字符)
(4)字符串常量
(5)符号常量(#define PI 3.1416)
7.变量名实际上是以一个名字代表的一个存储地址。
8.常变量

const  int  a = 3;

常变量是有名字的不变量,而常量是没有名字的不变量。
9.在计算机高级语言中,用来对变量、符号常量名、函数、数组等命名的有效字符序列,统称为标识符。
10.C语句分为以下5类
(1)控制语句

1. if() ... else ... (条件语句)
2.for()...            (循环语句)
3.while()...        (循环语句)
4.do... while()  (循环语句)
5.continue       (结束本次循环语句)
6.break            (中止执行switch或循环语句)
7.switch         (多分支选择语句)
8.return          (从函数返回语句)
9.goto               (转向语句)

(2)函数调用语句
(3)表达式语句
(4)空语句
(5)复合语句
可以用{}把一些语句和声明括起来成为复合语句(又称语句块)。
11.关系运算符及其优先次序
(1)<
(2)<=
(3)>
(4)>=
上面四种优先级相同(高)
(5)==
(6)!=
上面二种优先级相同(低)
关系运算符的优先级低于算术运算符
关系运算符的优先级高于赋值运算符
用关系运算符将另个数值或数值表达式连接起来的式子,称关系表达式。
12.逻辑符号和逻辑表达式
(1)&&
(2)||
(3)!

屏幕快照 2018-03-24 下午9.01.03.png

13.在函数内定义的变量是局部变量,而在函数之外定义的变量称为全局变量。全局变量可以为本文件中其它函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。
14.C的存储类别包括4种:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)。
将外部变量的作用域扩展到其他文件
在任一个文件中定义外部变量Num,而在另一文件中,用extern对Num做外部变量声明,即“extern Num”。
15.对变量而言,声明与定义的关系稍微复杂一些。在声明部分出现的变量有两种情况:一种是需要建立存储空间的(如“int a;”),另一种是不需要建立存储空间的(“如extern a;”).前者称为定义性声明,简称定义。后者称为引用性声明。把建立存储空间的声明称定义,而把不建立存储空间的声明称为声明。
16.在需要调用此函数的其他文件中,需要对此函数做声明。在对此函数做声明时,要加关键字extern,表示该函数“是在其他文件中定义的外部函数”。
17.指针是一个地址,而指针变量是存放地址的变量。
程序中有两处出现 pointer_1和pointer_2,两者的含义不同。程序第4行的pointer_1和pointer_2表示定义两个指针变量pointer_1和pointer_2。它们前面的“”只是表示该变量是指针变量,指针变量名是pointer_1和pointer_2。程序最后一行printf函数中的pointer_1和pointer_2则代表指针变量pointer_1和pointer_2所指向的变量,其中的“”表示指向。
屏幕快照 2018-03-25 下午3.34.33.png

18.在输入操作时,数据从文件流向计算机内存,在输出操作时,数据从计算机流向文件。
19.对文件读写之前应该“打开”该文件,在使用结束之后应“关闭”该文件。
所谓“打开”是指为文件建立相应的信息区(用来存放有关文件的信息)和文件缓存区(用来暂时存放输入输出的数据)。
所谓“关闭”是指撤销文件信息区和文件缓存区,使文件指针变量不再指向改文件,显然就无法进行文件的读取了。
20.一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的、适当的地址。这是关于使用指针的金科玉律。
21.C语言使用预处理器语句#define来实现宏——内联代码的原始实现。这并不是通过传递参数实现的,而是通过文本替换来实现的。

#define SQUARE(X)  X*X
b = SQUARE(4+7) 会被替代为4+4*7+7
可以使用括号来进行改进
#define SQUARE(X)  ((X)*(X))

————————————————————————————————————
程序一启动就会自动调用名称叫做main的函数

int main()
{
    // C语言中每一条语句后面都必须有;号
    // C语言中除了""引起来的地方, 其它任何地方都不能出现中文
    // main函数中的return 0;可以写, 也可以不写
    // main函数前面的int可以写, 可以不写
    // main函数后面的()不可以省略
    // 不要把main写错了
    // 同一程序中只能有一个main函数
    printf("hello");
    return 0;
}

常量

int main() {

    // 1.整型常量
    10;
    
    // 2.实型常量(小数)
    // 单精度float / 双精度double
    // 注意: 默认情况下编写的小数都是双精度的实型常量, 也就是默认情况下所有小数都是double
    10.1;// 双精度double
    88.8;// 双精度double
    
    5.2f; //单精度float
    
    // 3.字符型常量, 用单引号括起来的就是字符型常量
    // 注意: 字符型常量的单引号中只能写一个字符, 如果写多个会报错
    'a'; // 字符型常量
    'ab'; // 错误
    '1';
    '12'; // 错误, 12是由两个字符组成, 由1和2组成
    // '李'; // 错误写法, 因为一个汉字占3个字节
    
    // 字符型常量的特殊情况, 在C语言中, 系统给某些字符的组合定义了特殊的含义, 这些字符的组合可以当做单个字符来使用
    '\n';
    
    
    
    
    // 4.字符串型常量, 用双引号括起来的就是字符串型常量
    // 注意: 无论你用双引号括起来了几个字符, 只要使用双引号括起来的都是字符串常量
    "abc";
    "a";
    
    
    return 0;
}

变量

int main(int argc, const char * argv[]) {
    
    // 1.申请一块存储空间, 也就是定义一个变量
    /*
    // 定义变量的格式: 数据类型  变量名称;
    // 定义一个基本数据类型的变量
    // int float double char
    int number; // 定义了一个名称叫做number的变量
    // 会在内存中开辟一块存储空间给number, 用于存储数据
    int value; // 定义了一个名称叫做value的变量
    // 又会在内存中开辟一块存储空间给value, 用于存储数据
    */
    
    /*
    // 2.连续定义, 如果多个变量都是相同类型的, 那么可以采用连续定义
    int number, value;
    
    // 3.如何往变量对应的存储空间当中存储数据
    // 注意: 变量中不能随便存储数据, 只能存储与之对应的数据
    // 概念: 变量的第一次赋值, 我们称之为初始化,
    // 注意, 要想使用变量都必须先对变量进行初始化, 然后才能够使用
    // 如果不对变量进行初始化, 那么其实变量中存储的是一些垃圾数据
    number = 10; // 会将等号右边的数据放到等号左边的变量对应的存储空间中
    
    
    // 4.如何使用变量
    // 占位符号, %号是占位符号的起始符号
    printf("%i\n", number);
    
    
    float floatValue; // 定义了一个实型的变量
    floatValue = 10.1f; // 往floatValue对应的存储空间中存储了10.1这个值
    printf("%f\n", floatValue);
    
    int intValue, age; // 定义了一个整型的变量
    printf("%i\n", intValue);
    printf("%i\n", age);
    */
    
    // 6.初始化的两种方式
    /*
    // 1.先定义再初始化
    int number;
    number = 10;
    // 2.定义的同时初始化
    int value = 10;
     */
    
    // 7.完全初始化
//    int number, value;
//    number = value = 10;
    
    int number;
    number = 10;
    int value;
    value = number;

    // 8.部分初始化
//    int number, value = 10;
        /*
//    number = 10;
    int number; // 变量有自己的作用域
    // 变量的作用域从定义的哪一行开始, 直到大括号结束或者遇到return为止
    number = 10;
    printf("return之前");
    return 0; // 结束函数, 只要执行到return就和执行到}一样, 代表着函数结束了
//    printf("%i", number);
     */
    
//    call();
//    number = 50;
    
    /*
    {
        int number;
    }
    number = 50;
     */
    
    /*
    int number;
    {
     // 自己没有找粑粑
        number = 10;
        printf("%i", number);
    }
     */
    
    /*
    int number;
//    int number; // 同意范围内不能出现同名的变量
    {
        // 1.如果自己对应的范围中有, 就使用自己的
        // 2.不同的范围中可以有相同名称的变量
        int number;
        number = 10;
        printf("%i\n", number);// 10
    }
    printf("%i\n", number);// 0
    */
    
    // 内存寻址从大到小, 如果按照这种规则, 那么先定义的变量内存地址一定大于后定义的变量
    int number = 0;
    int value = 0;
    // 如何获得变量的详细地址(变量的地址), &变量名称
    // 利用%p占位符来打印变量的详细地址
    // 输出的是一个十六进制的数
    //  a/ b/ c/ d/ e/ f/
    // 10/11/12/13/14/15
    printf("%p\n", &number);
    printf("%p\n", &value);
    
    return 0;
}

printf

int main(int argc, const char * argv[]) {
//   折叠代码的快捷键 : command + option + 方向键
//   单行注释的快捷键: command + /
    
    // 1.输出基本数据类型
    /*
    int number = 10;
    int value = 20;
    printf("%i, %i\n", number, value);
    
    float floatValue = 10.1f;
    printf("%f\n",floatValue);
    
    char charValue = 'a';
    printf("%c\n", charValue);
    
    double doubleValue = 9.9;
    printf("%lf", doubleValue);
     */
    
    // 3..指定位宽
    /*
    // 如何指定位宽? %mi; // 其中m就是位宽 而i输出对应类型的数据
    // 默认情况下位宽是右对齐, 如果需要输出的类型宽度不够, 会在左边补空格
    // 如果在位宽前面加上-号, 就会改变位宽的对齐方式为左对齐,如果需要输出的类型宽度不够,会在右边补空格
//    int number = 99;
//    printf("%5i!!!", number);
//    printf("%-5i!!!", number);
    
    // 注意: 如果指定了位宽, 但是实际输出的内容超出了宽度, 会按照实际的宽度来输出
//    int number = 9999;
//    printf("%2i", number);
    
    // 可以在位宽前面加上0, 如果输出的内容宽度不够就会用0来补, 如果宽度已经够了, 那么会按照实际的宽度输出
    int number = 8;
    // 01
    // 2015-06-05
    printf("%02i", number);
     */
    
    // 3.保留位数
    /*
    // %f默认会保留6位小数
    // 指定保留多少位小数: %.nf, 其中n就是需要保留多少位小数, f用于输出实型
    float floatValue = 3.14;
    printf("%.2f", floatValue);
     */
    
    // 4.%f各种坑
    /*
    // 为什么指定保留10位之后还不对呢? 因为float类型有效位数是7位(不包括小数点)
    // 如何计算有效位? 从第一个数开始计算
    // 如果输出的数据超出了有效位数, 那么就会出现一些垃圾数据
    float floatValue = 3.1415926525;
    printf("%.10f\n", floatValue); // 默认情况保留6位
    
    // 如何想完整的输出那么必须将数据保存位double类型, 因为double类型的有效位是15位
    double doubleValue = 3.1415926525;
    printf("%.10lf\n", doubleValue);
     */
    
    // 5.如何提高比格
    float floatValue = 3.1415926;
    printf("%.*f", 5,floatValue);
    return 0;
}

scanf

int main(int argc, const char * argv[]) {
    // 1.要求: 存储用户输入的整数
    /*
    // 1.用户输入的整数确定吗? 不确定 --> 定义变量
    // 2.如何接收用户输入的整数? scanf函数
    // scanf格式: scanf("格式化字符串", 列表项); --> scanf("%i", 变量);
    
    // 1.定义变量
    int number;
    // 2.利用scanf函数接收用户输入的数据
    scanf("%i", &number);// 注意: 必须告诉scanf函数变量的详细地址, 才能存储用户输入的数据
    // 3.输出接收到的数据
    printf("number = %i", number);
     */
    
    // 2.要求从控制台接收用户输入的两个整数, 然后计算两个整数的和
    /*
    // 0.提示用户输入数据
    printf("亲, 请输入第一个数字, 以回车键结束\n");
    // 1.定义两个变量保存用户输入的数据
    int num1, num2;
    // 2.利用scanf接收用户输入的数据
    // 2.1接收第一个数
    scanf("%i", &num1);
    printf("亲, 请输入第二个数字, 以回车键结束\n");
    scanf("%i", &num2);
    
    // 3.将接收到的两个数相加
    int result = (num1 + num2);
    
    // 4.输出两个数验证结果
    printf("result = %d", result);
     */
    /*
    int num1, num2;
    num1 = num2 = 10;
    printf("num1 = %i, num2 = %i", num1, num2);
     */
    
    /*
    // 0.提示用户输入数据
    printf("亲, 请输入两个数字, 以回车键结束\n");
    // 1.定义两个变量保存用户输入的数据
    int num1, num2;
    // 2.利用scanf接收用户输入的数据
    // 注意: 利用scanf接收多个数据的时候, 输入的内容要求和"格式化字符串"中的一模一样
    // 如果输入的数据与格式化字符串中的不匹配, 那么scanf函数会自动终止
//    scanf("%i,%i", &num1, &num2);
    // 如果多个数据之间没有其它字符, 那么可以利用 空格, 回车, table来作为分隔
    // 但是, 如果格式化字符串中有%c除外
//    scanf("%i%i", &num1, &num2);
    char charValue;
    // 输入: 123-->num1, a -->charValue, 456-->num2
    // 技巧: 无论想接收什么类型的数据, 只要有需要一次性接收多个, 就给每一个数据之间加上固定的符号作为分隔符(例如: ,)
    scanf("%i-%c-%i", &num1, &charValue, &num2);
    
    // 3.将接收到的两个数相加
//    int result = (num1 + num2);
    
    // 4.输出两个数验证结果
//    printf("result = %d", result);
    
    printf("num1 = %i, char = %c , num2 = %i", num1, charValue, num2);
     */
    
    // 3.\n问题
    int number, number2;
    // \n代表换行
    // 如何告诉scanf函数我们输入完毕? 回车 == \n
    // 因为回车是scanf的结束符, 所以不能在scanf的格式化字符串末尾写上\n
    // 如果不小心把\n放到了scanf格式化字符串的末尾, 也可以破, 原样输入或输入一个不匹配的类型
    scanf("%i\n%i\n", &number, &number2);
    printf("number = %i , number2 = %i\n", number, number2);


 // 1.提醒用户
//    printf("请输入两个整数 , 整数之间用逗号隔开, 使用回车结束\n");
    // 2.定义变量保存用户输入的数据
    int num1, num2;
    char charValue;
    // 3.接收用户输入的数据
    // num1 = 123, charValue = + , num2 = 456
    scanf("%i%c%i", &num1, &charValue, &num2);
    // 4.求差
//    int result = num1 - num2;
    // 5.输出验证
//    printf("result = %i\n", result);
    printf("num1 = %i, cahrValue = %c, num2 = %i\n", num1, charValue, num2);
    
    // 用户输入-->输入缓冲区-->scanf
    
    char charValue2;
    int num3;
    // scanf只要输入缓冲区中有内容, 就不会要求用户输入数据
    scanf("%c%i", &charValue2, &num3);
    printf("charVaue2 = %c, num3 = %i\n", charValue2, num3);
    return 0;
}

类型转换

int main(int argc, const char * argv[]) {
    // 1.类型转换
    /*
    // int 占用4个字节 double 占用8个字节
    // 只是相同类型的数据才能进行运算, 隐式类型转换 将小类型转换为大类型, 然后再进行运算
    // 在赋值的时候系统又给我们进行了一次隐式类型转换
    // 发现, 如果在隐式类型转换的时候, 将大类型转换为小类型会丢失精度
    int result = 10.9;
    printf("result = %d\n", result);
    
    // 强制类型转换格式: (类型)被强制类型转换的数据
    int result2 = (int)10.9;
    printf("result2 = %i", result2);
    
    // 自动类型提升
    // 自动类型的提升, 将参与运算的数据都转换为同一类型后再运算
    // 会自动将小的数据类型提升为大的数据类型
    int result3 = 1 + 9.9; // 1.0 + 9.9 = 10.9
     */
    
    /*
    // 在算术运算中, 参与运算的是什么类型, 计算出来就是什么类型
//    double result = 1.0 / 2; // 1.0 / 2.0 --> 0.5
//    double result = (double)1 / 2;// 1.0 / 2.0 --> 0.5
    double result = (double)(1 / 2); // (double)(0) --> 0.0
    printf("result = %lf\n", result);
     */
    
    // 2.算术运算符的结合性, 左结合, 从左至右的计算
    // 3.算术运算符的优先级 * / % 大于 + -
    // 如果优先级和结合同时存在, 那么先优先级再结核性
    int result = 3 + 4 * 5 + 6;
    printf("result = %i\n", result);
    
    return 0;
}

算术运算符

void test()
{
    /*
     + 加法
     - 减法
     * 乘法
     / 除法
     % 取模(取余)
     */
    // 1.定义变量保存计算结果
    int result;
    // 2.开始计算
    result = 1 + 1;
    result = 1 - 1;
    result = 2 * 2;
    result = 6 / 3;
    // 注意: 取模运算只能用于整数
//    result = 10 % 3;
//    result = 12 % 5;
//    result = 10.0 % 3.0;
    // 取模运算的正负性取决于左边的操作数, 如果左边为负数, 那么结果就是负数
//    result = 10 % -3;
//    result = -10 % -3;
//    result = -10 % 3;
    
    // 如果取模运算的左边小于右边, 那么结果就是左边
    result = 2 % 9;
    
    // 3.验证计算结果
    printf("result = %i", result);
}

赋值运算符

int main(int argc, const char * argv[]) {
    
    // 1.最简单的赋值运算符
    // 赋值运算符的结合性: 从右至左
//    int result = 10;
    
    // 2.复合赋值运算符: += -= *= /= %=
    int result = 10;
//    result = result + 5;// result = 10 + 5; result = 15
//    result += 5; // 相当于 result = result + 5;
    result -= 5; // 相当于result = result - 5;
    printf("result = %i\n", result);
    return 0;
}

自增自减

int main(int argc, const char * argv[]) {
    
    // 1.自增自减基本概念
    /*
    int result = 10;
//    result = result + 5;
//    result += 5;
    
//    result = result + 1;
//    result += 1;
    // 自增: 如果想让某一个数加1可以使用自增
//    result++;
//    result++;
//    result = result - 1;
//    result -= 1;
    // 自减: 如果想让某一个数减1可以使用自减
//    result--;
     */
    
    // 2.自增的两种写法
    /*
//    result++;
//    ++result;
    // 自减的两种写法
    result--;
    --result;
     printf("result = %i\n", result);
    */
    
    // 3.自增自减写在前面和后面的区别
    /*
    // 如果++写在变量的前面, 那么会先将变量自增再用自增之后的结果参与运算
    // 如果++写在变量的后面, 那么会先将变量的值参与运算再将变量自增
    // 总结一句话: ++在前, 先自增再运算, ++在后, 先运算再自增
    int a = 10;
//    int b = a++;// b = 10, a = 11
//    int b = ++a;// a = 11,  b = 11;
//    int b = a--;// b = 10, a = 9;
    int b = --a; // a = 9, b = 9;
    printf("a = %i , b = %i\n", a, b);
     */
    
    /*
    // 无论++在前还是在后, 最终都会自增一次
    int a = 10;
    //       10   +  12
//    int b = (a++) + (++a);
    // a = 12
    // b = 22
    
    //        10  +   11
//    int b = (a++) + (a++);
    // a = 12
    // b = 21
//           11   +   12
    int b = (++a) + (++a);
    // a = 12
    // b = 23
    printf("a = %i, b = %i\n", a, b);
     */
    
    /*
//    5++;
    double doubleValue = 10.9;
    doubleValue++;
    printf("%f", doubleValue);
     */
    
    int a = 10;
//    int b = a++;
    // b = a; a = a + 1;
    
    int b = ++a;
    // a = a + 1; b = a;
    printf("a = %i, b = %i\n", a, b); // a = 11, b = 11
    
    return 0;
}

sizeof

int main(int argc, const char * argv[]) {
    // sizeof可以用来计算一个变量或一个常量、一种数据类型所占的内存字节数
    // 注意: sizeof是一个运算符, 不是一个函数
    // 利用sizeof计算的格式: sizeof(变量/常量/数据类型);
    
    // 1.计算常量占用的内存字节数
    /*
//    int number = sizeof(10);// 10是一个整型常量, 整型 == int == 4
    // 如果利用sizeof计算常量, 那么可以省略()
    int number = sizeof 10;
    printf("number = %i\n", number);
     */
    
    // 2.计算变量
    /*
    double doubleValue = 10.9;// doubleValue是实型 , 实型 ==  double == 8
//    int number = sizeof(doubleValue);
     // 如果利用sizeof计算变量, 那么可以省略()
    int number = sizeof doubleValue;
    printf("number = %i\n", number);
     */
    
    // 3.计算数据类型
    /*
    int number = sizeof(char);
    // 注意: 如果利用sizeof计算数据类型, ()不能省略
//    int number = sizeof char;
    printf("number = %i\n", number);
     */

    return 0;
}

逗号表达式

int main(int argc, const char * argv[]) {
    /*
    int a = 10;
    int b = 5;
    int result;
    // 结合性 从左至右
    // a = 15, b = 6, result = 15 + 6 = 21
    a = a + 5, b = b + 1 , result = a + b;
    printf("a = %i, b = %i, result = %i\n", a, b, result);
     */
    
    // 只要是运算符那么一定会有运算结果, 逗号运算符也不例外.
    // 逗号运算符的结果是 最后一个表达式的结果
    
    int a = 10;
    int b = 5;
    //   6       a = 10 + 5 = 15 b = 5 + 1 = 6
    int result = ((a = a + 5), (b = b + 1)); // 仅仅作为了解
    printf("a = %i, b = %i, result = %i\n", a, b, result);
    return 0;
}

关系运算符

int main(int argc, const char * argv[]) {
    /*
     关系运算符:
     >
     <
     >=
     <=
     ==
     !=
     关系运算符的返回值只有两种, 要么真, 要么假. 1(真)和0(假)
     */
    
    // 1.基本使用
    /*
    int a = 10;
    int b = 5;
    int result = a > b;// 吗? 大于, 真, 非0即真.
    printf("result = %i\n", result);
     */
    
    /*
    int a = 10;
    int b = 8;
    int result = a != b;
    printf("result = %i\n", result);
     */
    
    // 2.关系运算符注意点
    /*
    // 关系运算符也有优先级. > < >= <= 优先级大于 == !=
    //             1 == 1
//    int result = 1 == 10 > 5;
    
    // 算术运算符的优先级 大于 关系运算符
    //             2 <  4
//    int result = 1 + 1 < 2 + 2;
    
    // 关系运算符的结合型: 从左至右
    //              1 > 1
//    int result = 10 > 3 > 1;
    
    // 如果优先级和结合性同时存在, 先优先级再结合性
    //              11 > 9 == 3 > 1
    //               1 == 3 > 1
    //               1 == 1
    int result = 10 + 1 > 5 + 4 == 3 > 1;
    
    printf("result = %i\n", result);
     */
    
    // 3.练习
//    int result = 3 > 4 + 7; // 3 > 11 = 0
//    int result = (3>4) + 7; // 0 + 7
//           5 != 4 + 14 > 3 == 10
//           5 != 18 > 3 == 10
//           5 != 1 == 10
//           1 == 10
    int result = 5 != 4 + 2 * 7 > 3 == 10;
    printf("result = %i\n", result);
    return 0;
}

逻辑运算符

int main(int argc, const char * argv[]) {
    /*
     逻辑运算符的返回值只有两种: 要么真要么假, 要么是1(真), 要么是0(假)
     &&(与运算)
     格式: 表达式1 && 表达式2
     结合性: 从左至右
     只有表达式1和表达式2都为真的时候, 逻辑与表达式才返回真
     如果表达式1或者表达式2中有一个是假, 那么逻辑与表达式返回的结果都是假
     口诀: 一假则假
     
     ||(或运算)
     格式: 表达式1 || 表达式2
     结合性: 从左至右
     只要表达式1或者表达式2中有一个是真的, 逻辑或表达式返回的值就是真
     只有表达式1和表达式2的值都是假, 逻辑或的值才是假
     口诀: 一真则真
     
     !(非运算)
     格式: !表达式
     结合性: 从右至左
     如果表达式是真, 就返回假
     如果表达式是加, 就返回真
     取反
     */
    
    // 1.基本使用
    /*
    //             1 &&  0
    int result = 10 > 8 && 5 > 8;
    printf("result = %i\n", result);
     */
    
    /*
    //              1 ||    0
    int result = 10 > 18 || 15 > 8;
    printf("result = %i\n", result);
     */
    
    /*
    int result = !(10 > 18);// !0
    printf("result = %i\n", result);
     */
    
    // 2.注意点:
    /*
    // 由于C语言规定, 任何数值都有真假性, 非0即真. 所有逻辑运算符可以直接与数值进行计算
//    int result = 0 && 11;
//    int result = 0 || 0;
    // 逻辑非结合性: 从右至左
    int result = !!!!!!1;// 0
    printf("result = %i\n", result);
     */
    
    // 由于逻辑与有一个特点: 一假则假, 所以如果前面的表达式的值为假, 那么后面的表达式没有必要参与运算
//    int result = 10 > 18 && 9 > 5;
    
//    int a = 10;
//    int result = 10 > 18 && ++a > 5;
//    printf("result = %i , a = %i\n", result, a);
    
    // 由于逻辑或有一个特点: 一真则真, 所以如果前面的表达式的值为真, 那么后面的表达式没有必要参与运算

    // 逻辑与和逻辑或的这个特点, 称之为逻辑运算符的短路
    int a = 10;
    int result = 10 > 18 || ++a > 5;
    printf("result = %i , a = %i\n", result, a);
    
    
    
    return 0;
}

三目运算符

int main(int argc, const char * argv[]) {
    /*
    int a = 20;
    int b = 15;
//    int result = a > b;
    // 三目运算符格式: 条件表达式 ? 结果A : 结果B
    // 结合性: 从左至右
    // 只要条件表达式为真就返回结果A, 如果条件表达式为假, 那么就返回结果B
    // a大于b吗? 如果a大于b就返回a, 否则返回b
//    int result = (a > b)? a : b;
    
    // 三目运算符的优先级: 低于关系运算符和算术运算符
    //             25 > 15 ? 20 : 15
    int result = a + 5 > b ? a : b;
    
    printf("result = %i\n", result);
     */
    
    
    // 从键盘输入3个整数, 利用三目运算符取出最大值并输出
    // 1.提示用于输出三个整数, 用逗号隔开, 以回车结束
    printf("请输入三个整数, 用逗号隔开, 以回车结束\n");
    // 2.定义三个变量保存用户输入的数据
    int num1, num2, num3;
    // 3.利用scanf函数接收用户输入的数据
    scanf("%i,%i,%i", &num1, &num2, &num3); // 17, 5, 88
    // 4.比较三个数的大小, 取出最大值
    // 4.1获取num1和num2中的最大值
//    int temp = num1 > num2 ? num1 : num2;// 17 > 5 ? 17 : 5; temp = 17
    // 4.2利用num1和num2中的最大值和剩下的num3比较
//    int result = temp > num3 ? temp : num3;// 17 > 88 ? 17 : 88; result = 88
    
    // youtube写法 usb写法
    // 1.阅读性比较差
    // 2.性能也比较差
    int result = (num1 > num2 ? num1 : num2) > num3 ? (num1 > num2 ? num1 : num2) : num3;
    // 5.输出结果
    printf("result= %i\n", result);
    
    return 0;
}

if

#include <stdio.h>

/*
 第一种格式:
 if (条件表达式)
 {
    语句...
 }
 只要 条件表达式 为真, 那么就会执行if后面大括号中的内容.
 
 第二种格式:
 if (条件表达式)
 {
    语句...
 }else
 {
    语句...
 }
 只要 条件表达式 为真,那么就会执行if后面大括号中的内容. 
 如果 条件表达式 不为真, 那么就会执行else后面大括号中的内容
 规律: 两个大括号中的内容, 一定会有一个会被执行
 
 第三种格式:
 if (条件表达式1)
 {
     语句...
 }
 else if (条件表达式2)
 {
     语句...
 }
 else if (条件表达式3)
 {
     语句...
 }
 .....
 else
 {
     语句...
 }
 只要 条件表达式1 为真,那么就会执行if后面大括号中的内容. 而其它大括号不会被执行
 如果 条件表达式1 不为真, 那么就会取判断条件表达式2, 如果 条件表达式2 为真就会执行 条件表达式2 后面大括号中的内容
 其它的else if一次类推, 
 当前面所有的if, else if的条件表达式都不为真, 就会执行else后面大括号中的内容
 规律:
 众多大括号只会执行其中一个
 如果执行到后面大括号中的内容, 代表前面的所有条件都不满足
 
 第4种格式:
 if (条件表达式)
      语句...
 
 如果省略if后面的大括号, 当条件表达式为真时, 只会执行if后面的第一条语句
 
 第5种格式:(if嵌套)
 if(添加表达式)
 {
     if(添加表达式)
     {
 
     }
 }else
 {
     if(添加表达式)
     {
     
     }else
    {
    }
 }
 */
int main(int argc, const char * argv[]) {
    /*
    int age = 12;
    // 判断年龄是否大于等于18岁
    if (age >= 18)
    {
        printf("开网卡\n");
    }
    printf("end\n");
     */
    
    /*
    int age = 19;
    if (age >= 18)
    {
        printf("开网卡\n");
    }else
    {
        printf("回家叫芭比\n");
    }
     */
    
    /*
    int age = 6;
    if (age == 1) {
        printf("该学走路了");
    }else if (age == 6)
    {
        printf("该上学了");
    }else if (age == 18)
    {
        printf("该谈朋友了");
    }else
    {
        printf("在家好好呆着");
    }
     */
    
    /*
    int age = 15;
    if (age >= 18)
        printf("开网卡\n"); // 只有条件满足才会执行
    printf("给钱\n"); // 无论条件是否满足, 都会执行
    */
    
    // 判断一个数, 是否在3~5之间
//    3<= a <= 5; // 错误
//    (a >= 3) && (a <= 5);
    int a = 10;
    if (a >= 3) {
        if (a <= 5) {
            printf("a是一个3~5之间的数\n");
        }else
        {
            printf("a不是一个3~5之间的数\n");
        }
    }else
    {
        printf("a不是一个3~5之间的数\n");
    }
    printf("end\n");
    return 0;
}

switch

#include <stdio.h>
/*
 if格式:
 
 if (条件表达式)
 {
    语句;
 }
 
 switch格式:
 
 switch (条件表达式)
 {
    case 整数: // case可以有一个或多个
        语句;
        break;
    case 整数: // case可以有一个或多个
         语句;
         break;
    default:
        语句;
        break;
 }
 1.拿到条件表达式的返回值(注意返回值必须是整型)
 2.利用条件表达式的返回值和switch后面大括号中的每一个case进行比较, 判断条件表达式的返回值和case后面的整数是否相等   条件表达式 == 整数
 3.如果条件表达式的返回值等于某一个case后面的整数, 那么就会执行该case后面的语句
 4.执行完语句之后如果遇到break就会结束整个switch语句, 也就是说后面的所有语句都不会被执行
 5.如果前面的case不满足, 也就是前面的case的整数和条件表达式的返回值不相等就会继续判断后面的case
 6.如果前面所有的case都不满足, 就会执行default后面的语句
 
 if (条件表达式)
 {
 }else if(条件表达式)
 {
 }else
 {
 }
 */

int main(int argc, const char * argv[]) {
    
    int age = 6;
    /*
     如何结束switch语句:
     1.遇到break
     2.执行到switch对应的大括号的结束 }
     */
    switch (age) {
        case 3: // 3 == 6
            printf("该上幼儿园了\n");
            break; // break的作用就是用于结束switch语句
        case 6: // 6 == 6
            printf("该上小学了\n");
            // 没有写break
            // 注意: 如果switch中有一个case满足了条件, 那么其它所有的case和default都会失效
        case 12: // 12 == 18
            printf("该上中学了\n");
            
        default:
            printf("该上高中了\n");
            break; // 某些情况下default后面的break可以省略, 因为default后面就是}
    }
    
    // 1.Switch得条件表达式注意点
    /*
    // Switch的条件表达式必须是返回整数的表达式, 也就是说()中只能放整数
    // 或者写能够转换为整数的类型也可以, 例如char, 因为char类型可以转换为int类型, 所以写char也可以
    switch ('A') {
        case 6:
            printf("6");
            break;
        case 8:
            printf("8");
            break;
        default:
            printf("other");
            break;
    }
     */
    
    /*************************华丽的分割线*******************************/
    
    // 2.case的取值
    /*
    // Switch中, case后面只能放返回值是整数的表达式或者整数, 或者能够转换为整数的类型
    // case的取值不能重复
    // case后面不能放变量, 因为系统在编译的时候会对Switch后面所有的case进行优化, 会根据case后面的常量值, 生成一张取值表
    int number = 7;
    switch (6) {
        case 3 + 3:
            printf("6");
            break;
        case 'A':
            printf("8");
            break;
//        case number:
//            printf("8");
//            break;
        default:
            printf("other");
            break;
    }
     
     */
    
    /*************************华丽的分割线*******************************/
    
    // 3.default的位置问题
    // Switch中的default可以随便放
    // 无聊default写到什么地方, 它都会最后执行(只有前面所有的case都不满足的情况才会执行)
    // 只要某一个case满足, 那么后面所有的case和default都会失效
    // 同理: 只要default满足, 那么所有的case都会失效
    switch (20) {
        default:
            printf("other\n");
//            break;
        case 6:
            printf("6");
            break;
        case 8:
            printf("8");
            break;
    }

    return 0;
}
/*
     什么时候用switch, 什么时候用if
     在开发中一般情况下用if, if比较灵活
     如果说是对一些固定的值进行判断, 并且这些值得数量不多的情况, 可以使用switch
     从理论上来说, switch的性能效率比if高
     */

while

/*
 if (条件表达式)
 {
    语句;
    ...
 }
 if是对给定的条件进行判断, 如果条件满足, 就执行if后面大括号中的内容
 
 while (条件表达式)
 {
     语句;
     ...
 }
 while是对给定的条件进行判断, 如果条件满足, 就执行while后面大括号中的内容, 执行完毕之后会再次判断条件表达式, 如果满足会再次执行while后面大括号中的内容, 直到条件表达式不满足位置
 术语: while后面大括号中的内容, 称之为 循环体
 
 while的应用场景: 当我们需要反复的执行某一段代码的时候就可以使用while
 */
int main(int argc, const char * argv[]) {
    
    /*
    printf("发射子弹\n");
    printf("发射子弹\n");
    printf("发射子弹\n");
    printf("发射子弹\n");
    printf("发射子弹\n");
    printf("发射子弹\n");
    printf("发射子弹\n");
    printf("发射子弹\n");
    printf("发射子弹\n");
    printf("发射子弹\n");
    */
    
    // 0.先将while的格式写好
    // 1.找到需要重复执行的代码, 将需要重复执行的代码放到循环体中
    // 2.确定结束条件(确定条件表达式)
    
    int count = 100;
    while (count > 0) {
        printf("发射子弹 %i\n", count);
//        count = count - 1;
//        count -= 1;
        count--;
    }
    // 注意点: 和if一样, 如果条件表达式一开始就不满足, 那么循环体就不会被执行
    return 0;
}
// 注意: break只能用在switch和循环结构中, 离开这两个东西没有任何效果

break

int main(int argc, const char * argv[]) {
    
    // 1.打十发子弹之后就不打了(打不动了)
    int number = 100;
    while (number > 0) {
        
        printf("发射子弹 %i \n", number);
        number--;
        if (number < 90) {
            printf("不打了\n");
            break;// break用于跳出循环
        }
    }
    return 0;
}

continue

int number = 100;
    while (number > 0) {
        
        printf("发射子弹 %i\n", number);
        number--;
        if (number % 10 == 0) {
            printf("休息一下\n");
            continue; // 跳过本次循环, 进去下一次循环
            // 只要看到continue 那么后面的所有语句都不会执行, 会执行再次去判断条件表达式
        }
        // 这些操作, 如果是休息就不做
        printf("还有很多复杂的操作\n");
    }

do while

/*
 while (条件表达式)
 {
    语句;
    ...
 }
 
 do
 {
    语句;
    ...
 }while (条件表达式);
 
 do while和while的区别: 
 while如果条件表达式一开始就不满足, 那么循环体永远不会被执行
 
 do while如果条件表达式一开始就不满足, 同样会执行一次循环体
 也就是说: 无论条件表达式是否满足, do while始终会执行一次循环体
 */
int main(int argc, const char * argv[]) {
    /*
    int i = 0;
    while (i < 0) {
        printf("%i\n", i);
        i++;
    }
    
    printf("------------\n");
    
    int j = 0;
    do{
        printf("j = %i\n", j);
        j++;
    }while (j < 0);
     */
    
    // 一般情况下, 能用while就用while
    // do while一般用于校验
    
    // 2.定义变量保存用户输入的整数
    int number = -1;
    // 无论如何都需要用户输入一次数据
    /*
    while (number <= 0) {
        // 1.提示用户输入一个整数
        printf("请输入一个整数\n");
        
        // 3.接收用户输入的整数
        scanf("%i", &number);// -5 -10 5
    }
     */
    do {
        // 1.提示用户输入一个整数
        printf("请输入一个整数\n");
        
        // 3.接收用户输入的整数
        scanf("%i", &number);// -5 -10 5
    } while (number <= 0);
    
    // 4.根据用户输入的整数计算结果
    int sum = 0;
    int count = 1;
    while (count <= number) {
        printf("%i + %i\n", sum , count);
        sum = sum + count;
        count++;
    }
    printf("sum = %i\n", sum);
    
    return 0;
}

for

/*
 while (条件表达式)
 {
    语句;
    ...
 }
 
 for (初始化表达式;条件表达式;循环后增量表达式)
 {
    语句;
    ...
 }
 条件表达式: 和while, dowhile一样, 只有条件满足才会执行循环体
 初始化表达式: 在整个for循环的生命周期中, 初始化表达式只会执行一次
 循环后增量表达式: 会在每次循环体之后完毕之后调用一次
 */
int main(int argc, const char * argv[]) {
    /*
    int count = 100; // 用于控制循环什么时候结束
    while (count > 0) {
        printf("发射子弹 %i\n", count);
        count--; // 控制循环的次数, 当每次循环执行完毕之后都会执行一次
    }
     */
    
    // 能用while做得用for都能做, for相当于while的升级版
    // 以后如果定义的便利仅仅用于控制循环什么时候结束, 循环结束之后就不在使用了, 那么可以使用for循环
    // 如果将用于控制循环的变量定义在for循环中, 那么循环结束了, 变量也释放了
//    int number = 100;
//            1            2/5/8/11        4/7/10
    for (int number = 100;number > 0;number--)
    {
        // 3/6/9
        printf("发射子弹 %i\n", number);
//        number--;
    }
//    printf("%i", number);
    return 0;
}

循环嵌套

int main(int argc, const char * argv[]) {
    /*
     
     ***
     **
     *
     
     */
    // 只要以后看到很多行很多列, 那么第一时间就要想到循环嵌套
    // 定义一个变量保存当前需要输出的星星的个数
    /*
    int couon = 3;
    for (int i = 0; i < 3; i++) {
//        printf("count = %i", couon);
        for (int j = 0; j < couon; j++) {
            printf("*");
        }
        printf("\n");
        couon--; // 每输出一行就让列数减一
    }
     */
    /*
    int count = 0;
    for (int i = 0; i < 3; i++) {
        printf("i = %i", i);
//        printf("count = %i", count);
        for (int j = count; j < 3; j++) {
            printf("*");
        }
        printf("\n");
        count++;
    }
     */
    /*
//    int count = 0;
    for (int i = 0; i < 3; i++) {
//        printf("i = %i", i);
        for (int j = i; j < 3; j++) {
            printf("*");
        }
        printf("\n");
//        count++;
    }
     */
    
    
    /*
     
     *
     **
     ***
     
     */
    /*
    int count = 0;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j <= count; j++) {
            printf("*");
        }
        printf("\n");
        count++;
    }
     */
    
//    int count = 0;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j <= i; j++) {
            printf("*");
        }
        printf("\n");
//        count++;
    }
    
    /*
     尖尖朝上: 修改内循环的 条件表达式
     尖尖朝下: 修改内循环的 初始化表达式
     */
    
    return 0;
}

函数

/*
将人类的思维 --> 代码
 1. C语言程序是由函数组成
 2. 什么是函数? 函数就是一段具备特定功能的程序段
     */
/*
     不使用函数的弊端:
     1.重复代码太多, 又臭又长
     2.当需求变更, 很多地方都需要修改代码
     */
    /*
     使用函数的好处:
     1.提高了代码的复用性, 代码更简洁
     2.当需求变更, 不用修改很多地方
     */
/*
 函数的定义格式:
 返回值类型 函数名称(形参列表)  
 {
    函数体; // 语句; ...
 
 }
 */
/*
 初学者如何定义函数:
 应该有4个确定:
 1.确定函数名称(给函数起一个有意义的名称, 让调用者一看到名称就知道这个函数是干什么的)
    1.1函数是标示符的一种 , 遵守规则 : 26个字母 , 数字 , _组成, 遵守规范: 驼峰命名
 2.确定形参列表
    2.1形参列表的格式 (数据类型 变量名称, ...)
    2.2如何确定形参列表? 就看看以后调用函数时, 是否需要传递一些辅助的数据给函数
 3.确定返回值
 4.确定返回值类型
    4.1return的是什么类型, 那么返回值类型就写什么类型
 */

int getMax(int v1, int v2)
{
    int max = v1 > v2 ? v1 : v2;
    /*
     return有两个作用:
     1.返回数据给函数调用者
     2.结束函数
     */
    return max;
}
int main(int argc, const char * argv[]) {
    
    // 需求: 要求获取两个数的最大值
    int a = 10;
    int b = 20;
//    int max = a > b ? a : b;
    
    int result = getMax(a , b);
    printf("result = %i\n", result);
    
    int m = 998;
    int n = 668;
    result = getMax(m, n);
//    int max2 = m > n ? m : n;
    printf("result = %i\n", result);
    
    return 0;
}
// 注意: 形参列表中的变量只有当函数被调用时才会分配存储空间
// 形参列表中的每一个变量我们称之为, 形式参数, 简称形参
//    函数的声明, 只需要在函数被使用之前告知系统就可以了, 它可以写在函数的外面也可以写在函数的里面
// 完整的实现了函数的功能的代码, 称之为函数的定义(函数的实现)

main函数

#include <stdio.h>

// main是函数的名称, 是系统规定的名称, 系统启动程序就会调用名称叫做main的函数
// main函数只能由系统调用 ,不能手动调用

// 其实系统在启动程序的时候, 会给我们的程序传递一些参数
//  argc : 就是系统传递进来的参数个数, 默认是1
//  argv : 就是系统传递进来的参数实际的值, 默认是程序的名称
int main(int argc, const char * argv[]) {
    
    printf("argc = %i\n", argc);
    printf("argv = %s\n", argv[0]);
    printf("argv = %s\n", argv[1]);
    
    // return 结束函数
    // 0 : 返回给操作系统, 如果返回0, 代表程序整除结束, 如果返回其它值代表程序非正常结束
    return 0;
}

include

/*
 include后面的 <> 和 "" 的区别
 >如果使用<>代表会先从开发工具的编译环境中去查找
    + /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/
 
 >如果编译环境中没有找到, 那么会再去系统的编译环境中找
    + /usr/include/stdio.h
 
 >如果使用""代表会先从当前文件所在的文件夹下面查找
 >如果在当前文件所在的文件夹下面没有找到, 那么就回去开发工具的编译环境中去查找
 >如果编译环境中没有找到, 那么会再去系统的编译环境中找
 
 注意: include后面不一定要写文件名称 也可以写路径(相对路径/ 全路径)
 
 */

进制

int main(int argc, const char * argv[])
{
//     1.默认就是10进制
    int num = 12;
//    2.在前面加上一个0就代表八进制
    int num1 = 014;
    
//    %d是以十进制的方式输出一个整数
    printf("%d\n", num1);
//    %o是以八进制的方式输出一个整数
    printf("%o\n", num);
    
//    在数值前面加上0b就代表二进制
    int num2 = 0b1100;
    printf("%d\n", num2);
//    在数值前面加上0x就代表十六进制
    int num3 = 0xc;
    printf("%d\n", num3);
//     %x是以十六进制的方式输出一个整数
    printf("%x\n", num);
    
//     口诀:不看你怎么存,只看你怎去取
    
    return 0;
}

进制间的转换

int main(int argc, const char * argv[])
{

    /*
     十进制  -> 二进制
     9
     转换原理:除2取余 倒序读取
     
     
     9/2  4  1
     4/2  2  0
     2/2  1  0
     1/2  0  1
     
     9 --> 1001
     
     
     二进制 --> 十进制
     1001
     转换原理:乘以2的幂数(幂数从0开始), 然后相加
     
     1 * 2(0) = 1
     0 * 2(1) = 0
     0 * 2(2) = 0
     1 * 2(3) = 8
     
     1 + 0 + 0 + 8 = 9
     
    1 1 1  1 1
   16 8 4  2 1

     
     N位二进制的取值范围
     1位  取值范围 0~1  0~2的1次方-1
     2位  取值范围 0~3  0~2的2次方-1
     3位  取值范围 0~7  0~2的3次方-1
     n位  取值范围  0~2(n)-1
     
     000
     001
     010
     011
     100
     101
     110
     111
     
     
     11111 0~ (32-1)
     
     
     二进制转换为八进制  进制越大表示的位数就越短
     规律:三个二进制位代表一个八进制位  
     因为3位的最大取值是7 而八进制是逢八进一
     
     1个字节 8位
     000   0
     001   1
     100   4
     
     014

     
     二进制转换为十六进制
     规律:四个二进制位代表一个十六进制位
     因为4位的最大取值是15, 而十六进制是逢十六进一
     
     0000 0
     1100 c
     
     0xc
     
     */
    printf("%d\n", 0b1001);
    
    return 0;
}

原码、反码、补码

int main(int argc, const char * argv[])
{
//    int占4个字节 1个字节8位
    int num = 12;
    /*
//     12的二进制
     12在内存中存储的是它的补码
     00000000  00000000 00000000 00001100
     正数的特点:(三码合一) 正数的原码就是TA的反码就是TA的补码
     
     
     -12
     二进制的最高位我们称之为符号位 
     如果符号位是0代表是一个正数,
     如果符号位是1代表是一个负数
     
     10000000  00000000 00000000 00001100 (-12的原码)
     11111111  11111111 11111111 11110011(反码, 符号位不变其它位取反)
     
     11111111  11111111 11111111 11110011
    +00000000  00000000 00000000 00000001
     _____________________________________________
     11111111  11111111 11111111 11110100(补码 , 反码+1)
     
     结论:无论正数负数在内存中存储的都是补码
     
     
     
     11111111  11111111 11111111 11110101 (补码)
    -00000000  00000000 00000000 00000001  (-1)
     _____________________________________________
     11111111  11111111 11111111 11110100 (反码)
     10000000  00000000 00000000 00001011
     
     
     */
    printf("%d\n", 0b11111111111111111111111111110101);
    return 0;
}

位运算

/*
 位运算都是针对二进制的
 &
 |
 ^
  ~
 
 
 <<
 >>
 */
int main(int argc, const char * argv[])
{

    /*
     & 按位与
     特点:只有对应的两位都是1才返回1 否则返回0
     口诀: 一假则假
     规律:任何数按位与上1结果还是那个数
     
     1001
     & 0101
     _______
     0001
     
     
      1001
     &1111
     ______
      1001
     */
    
    /*
     | 按位或
     特点:只要对应的两位其中一位是1就返回1
     口诀:一真则真
     
     1001
     | 0101
     ________
     1101
     */
    /*
     
     
     ^ 按位异或
     特点:对应的两位不相同返回1 相同返回0
     
     1001
     ^ 0101
     _______
     1100
     
     //     多个整数按位异或的结果和顺序无关
     1001
     ^ 0101
     _______
     1100
     
     1100
     ^ 0110
     _______
     1010
     
     1001
     ^ 0110
     _______
     1111
     
     1111
     ^ 0101
     _______
     1010
     
     
     //     相同整数按位异或结果是0
     1001
     ^ 1001
     _______
     0000
     
     //     任何整数按位异或上0结果不变
     1001
     ^ 0000
     _______
     1001
     
     //     任何整数按位异或上另一个整数两次结果还是那个数
     1001
     ^ 1001
     ____________
     0000
     
     0000
     ^0101
     ______
     0101
     
     */
//    int result = 9 & 5;
//    int result = 9 | 5;

//    int result = 9 ^ 5;
//     多个整数按位异或的结果和顺序无关
//    int result2 = 9 ^ 5 ^ 6;
//    int result2 = 9 ^ 6 ^ 5;
//    相同整数按位异或结果是0
//    int result3 = 9 ^ 9;
//    任何整数按位异或上0结果不变
//    int result4 = 9 ^ 0 ;
//    任何整数按位异或上另一个整数两次结果还是那个数
//    int result5 = 9 ^ 9 ^ 5;
//    int result6 = 9 ^ 5 ^ 9;
//    printf("result = %d\n", result6);
    
    /*
     ~ 按位取反
     特点: 0变1 1变0
     
     0000 0000 0000 0000 0000 0000 0000 1001
     ~1111 1111 1111 1111 1111 1111 1111 0110 (补码)
     0000 0000 0000 0000 0000 0000 0000 0001
     ______________________________________________
     1111 1111 1111 1111 1111 1111 1111 0101 (反码)
     1000 0000 0000 0000 0000 0000 0000 1010
     
     */
    
    //    int result = ~9;
    ////    printf("result = %d\n", result);
    //    printf("%d\n",0b11111111111111111111111111110110);
    return 0;
    
}

int main(int argc, const char * argv[]) {
    
    /*
     << 左移
     
     a << n 把整数a的二进制位往左边移n位
     移出的位砍掉,低位补0, 发现左移会把原有的数值变大
     9 << 1 = 18  9 * 2(1) = 18
     9 << 2 = 36  9 * 2(2) = 26
     9 << n =  9 * 2(n)
     左移的应用场景:当要计算某个数乘以2的n次方的时候就用左移,效率最高
     
     0000 0000 0000 0000 0000 0000 0000 0000
     100 0000 0000 0000 0000 0000 0000 10010
     
     注意点:左移有可能改变数值的正负性
     */
    
    /*
     
     >> 右移
     
     a >> n 把整数a的二进制位往右边移n位
     移出的位砍掉, 缺少的以为最高位是0就补0是1就补1(是在当前操作系统下)
     9 >> 1 = 4  9 / 2(1) = 4
     9 >> 2 = 2  9 / 2(2) = 2
     右移的应用场景:当要计算某个数除以2的N次方的时候就用右移,效率最高
     0000 0000 0000 0000 0000 0000 0000 0000
     000000 0000 0000 0000 0000 0000 0000 10
     
     */
    //    int result = 9 << 2;
    int result = 9 >> 2;
    printf("result =  %d\n", result);
    return 0;
}

int main(int argc, const char * argv[]) {
    // 要求交换两个变量的值
    
    int a = 10;
    int b = 5;
    printf("交换前:a = %i , b = %i\n", a, b);
    // 第一种方式
    /*
    int temp = a;
    a = b;
    b = temp;
     */
    /*
     a = a + b; // a = 15
     b = a - b; // b = 15 - 5 = 10
     a = a - b; // a = 15 - 10 = 5
     */
    
    /*
    a = a ^ b;
    b = a ^ b; // b = a ^ b ^ b = a
    a = a ^ b; // a = a ^ a ^ b == b
     */
    /*
    b = a ^ b;
    a = a ^ b;//  a = a ^ a ^ b == b
    b = a ^ b;//  b = a ^ b ^ b == a
    
    printf("交换后:a = %i , b = %i\n", a, b);
     */
    
    
//    用户密码 , 纯数字
    int pwd = 123456789;
//    对用户密码进行简单加密
    int result = pwd ^ 998;
    printf("加密后:%d\n", result);
//    对用户密码进行解密
    result = result ^ 998;
     printf("解密后:%d\n", result);
    
    return 0;
}

类型说明符

/*
 类型说明符:
 1.说明长度的(它可以用于修改类型所占用的存储空间的大小)
 short; short == short int  == 2个字节 == %hi/ %hd
 long; long == long int  == 8个字节 == %li / %ld
 long long; == long long int  == 8个字节 == %lli / %lld
 
 用于说明数据类型, 一般情况下和int配合使用
 
 2.说明符号位(它可以用于修改符号位是否用于正负数)
 unsigned; 无符号 , 取值正数和零 == %u
 signed; 有符号, 默认就是有符号 , 取值 正数和零以及负数
 
 3.不同类型的说明符可以混合使用
 unsigned short
 signed long
 // 注意: 相同类型不能在一起使用
 unsigned signed
 */

数组

/*
     数组的定义格式:
     数据类型 变量名称;
     数据类型 数组名称[数据的个数];
     元素类型 数组名称[元素个数];
     元素类型: 就是数组中需要存储的数据类型, 一旦指定, 数组中就只能存储该类型的数据
     元素个数: 就是数组中能够存储的数据(元素)的个数
     */
int main(int argc, const char * argv[]) {
    // 需求保持全班101个人的分数
    // 元素类型 数组名称[元素个数];
    /*
    // 先定义再初始化
    int scores[5];
    scores[0] = 99;
    scores[1] = 88;
    scores[2] = 77;
    scores[3] = 66;
    scores[4] = 100;
     */
    /*
    int num;
    num = 10;
    
    int num1 = 10;
     */
    // 依次将{}中的每一个值赋值给数组中的每一个元素
    // 并且从0开始赋值
    // 也称之为数组的初始化(完全初始化)
    int scores[5] = {99,88,77,66,100};
    
    // 部分初始化
    // 默认从0开始初始化, 依次赋值
    // 注意: 如果"在部分初始化中"对应的内存没有被初始化, 那么默认是0
    int scores1[3] = {11, 22};
    printf("0 = %i\n", scores1[0]);
    printf("1 = %i\n", scores1[1]);
    printf("2 = %i\n", scores1[2]);
    
    printf("-------\n");
    
    // 注意: 如果没有对数组进行初始化(完全和部门), 那么不要随便使用数组中的数据, 可能是一段垃圾数据(随机值)
    int scores2[3];
    printf("0 = %i\n", scores2[0]);
    printf("1 = %i\n", scores2[1]);
    printf("2 = %i\n", scores2[2]);
    
    printf("-------\n");
    
    // 注意: 定义数组的时候, 数组的元素个数不能使用变量, 如果使用变量, 那么数组中是一些随机值
    int num = 10;
    int scores3[num];
    printf("0 = %i\n", scores3[0]);
    printf("1 = %i\n", scores3[1]);
    printf("2 = %i\n", scores3[2]);
    
    // 注意: 不建议使用变量定义数组, 如果使用了变量定义数组, 作为数组的元素个数, 不初始化的情况下是随机值, 如果初始化会直接报错
//    int num2 = 10;
//    int scores4[num2] = {11, 12};
     printf("-------\n");
    
    // 注意: 如果定义的同时进行初始化, 那么元素的个数可以省略
    // 省略之后, 初始化赋值几个数据, 那么数组的长度就是几. 也就是说数组将来就能存储几个数据
    int scores5[] = {1, 3};
    printf("0 = %i\n", scores5[0]);
    printf("1 = %i\n", scores5[1]);
    printf("-------\n");
    
    // 注意; 如果定义数组时没有进行初始化, 那么不能省略元素个数
//    int scores6[];
    
//    0 1 2 3 4
//    int socres7[101] = {0, 0, 0, 1, 3};
//    int socres7[101];
//    socres7[99] = 1;
//    socres7[100] = 3;
    
    // 可以通过[索引] = 的方式, 给指定索引的元素赋值
    int socres7[101] = {[99] = 1, [100] = 3};
    printf("3 = %i\n", socres7[99]);
    printf("4 = %i\n", socres7[100]);

    
//    注意: 只能在定义的同时利用{}进行初始化, 如果是先定义那么就不能使用{}进行初始化
    // 如果先定义那么就不能再进行整体赋值, 只能单个赋值
//    int scores8[3];
//    scores8 = {1 , 4, 19};
//    scores8[0] = 1;
    
    return 0;
}

// 动态计算数组的元素个数
    int length = sizeof(scores) / sizeof(scores[0]);
    
    for (int i = 0; i < length; i++) {
        printf("scores[%i] = %i\n", i,scores[i]);
    }
// 注意: 数组的存储和变量有点不一样, 数组存储元素, 是从所占用的低字节开始存储
// 其实数组名就是数组的地址

数组和函数

// 基本数据类型作为函数的参数是值传递
// 如果形参是基本数据类型, 在函数中修改形参的值不会影响到实参的值
void change(int value)
{
    value = 55;
}

// 注意: 数组名作为函数的参数传递, 是传递的数组的地址
// 因为数组名就是数组的地址 &number = &number[0] == number
// 注意: 如果数组作为函数的形参, 元素的个数可以省略
// 如果形参是数组, 那么在函数中修改形参的值, 会影响到实参的值
//void change2(int values[2])
void change2(int values[])
{
//    values[0] = 88;
    values[1] = 99;
}
int main(int argc, const char * argv[]) {
    /*
    int num = 10;
    change(num);
    printf("num = %i\n", num);
     */
    
    int nums[2] = {1, 5};
    /*
    change2(nums); // 相当于传递了数组的地址
    printf("nums[1] = %i\n", nums[1]);
     */
    change(nums[0]);
    printf("nums[0] = %i\n", nums[0]);
    return 0;
}

选择排序

int main(int argc, const char * argv[]) {
    // 已知一个无序的数组, 里面有5个元素, 要求对数组进行排序
    int nums[8] = {99, 12, 88, 34, 5, 44, 12, 100};
    
    /*
    int a = 10;
    int b = 12;
    int c = 5;
    
    printf("%i, %i, %i\n", a, b, c);
    if (a > b) {
        int temp = a;
        a = b;
        b = temp;
    }
    
    if (a > c)
    {
        int temp = a;
        a = c;
        c = temp;
    }
    
    if (b > c) {
        int temp = b;
        b = c;
        c = temp;
    }
    
    printf("%i, %i, %i\n", a, b, c);
     */
    int length = sizeof(nums) / sizeof(nums[0]);
    printf("length = %i\n", length);
    for (int i = 0; i < length; i++) {
        printf("nums[%i] = %i\n", i, nums[i]);
    }
    
    // length - 1是为了防止角标越界
    // length - 1因为最后一个元素已经没有可以比较的了
    // 0, 1, 2, 3, 4
    for (int i = 0; i < length - 1; i++) {
        for (int j = i+1; j < length; j++) {
//            printf("*");
//            printf("i = %i, j = %i\n", i, j);
            if (nums[i] > nums[j]) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
            }
        }
//        printf("\n");
    }
    
    printf("--------------\n");
    for (int i = 0; i < length; i++) {
        printf("nums[%i] = %i\n", i, nums[i]);
    }
    
    return 0;
}

冒泡排序

int main(int argc, const char * argv[]) {
    /*
     思路: 
     1.先分析如何比较
     2.找出比较的规律比较完一次之后第二次比较会少一次
     3.打印倒三角
     4.打印需要比较的角标
     5.比较并交换位置
     6.将常量替换为变量(length)
     */
    // 已知一个无序的数组, 里面有5个元素, 要求对数组进行排序
    int nums[6] = {99, 12, 88, 34, 5, 7};
    int length = sizeof(nums) / sizeof(nums[0]);
    for (int i = 0; i < length; i++) {
        printf("nums[%i] = %i\n", i, nums[i]);
    }
    for (int i = 0; i < length - 1; i++) {
        for (int j = 0; j < length - 1 - i; j++) {
//            printf("*");
//            printf("%i == %i\n", j, j+1);
            if (nums[j] > nums[j + 1]) {
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
            }
        }
//        printf("\n");
    }
    printf("----------\n");
    for (int i = 0; i < length; i++) {
        printf("nums[%i] = %i\n", i, nums[i]);
    }
    return 0;
}

折半查找

#include <stdio.h>
#include <time.h>

int findKey(int nums[], int key, int length);
int findKey2(int nums[], int length, int key);
int findKey3(int nums[], int length, int key);

int main(int argc, const char * argv[]) {
    // 现在已知一个有序的数组, 和一个key. 要求从数组中找到key对应的索引的位置
    // 对该方法进行封装, 要求找到就返回对应的索引, 找不到就返回-1
    int nums[500000] = {1, 3, 5, 7, 9, [499999] = 99};
    int key = 99;
    int length = sizeof(nums) / sizeof(nums[0]);
    
    /*
     // 消耗了多少1181毫秒
    clock_t startTime = clock();
    int index =  findKey(nums, key, length);
    clock_t endTime = clock();
    printf("消耗了多少%lu毫秒\n", endTime - startTime);
    printf("index = %i\n", index);
     */
    
    // 消耗了多少1毫秒
    clock_t startTime = clock();
//    int index = findKey2(nums, length, key);
    // 消耗了多少2毫秒
    int index = findKey3(nums, length, key);
    clock_t endTime = clock();
    printf("消耗了多少%lu毫秒\n", endTime - startTime);
    printf("index = %i\n", index);
    
    return 0;
}

int findKey3(int nums[], int length, int key)
{
    int min, max, mid;
    min = 0;
    max = length - 1;
    
    // 只要还在我们的范围内就需要查找
    while (min <= max) {
        // 计算中间值
        mid = (min  + max) / 2;
        if (key > nums[mid]) {
            min = mid + 1;
        }else if (key < nums[mid])
        {
            max = mid - 1;
        }else
        {
            return mid;
        }
        
    }
    return -1;
}

int findKey2(int nums[], int length, int key)
{
    int min, max, mid;
    min = 0;
    max = length - 1;
    mid = (min + max) / 2;
    
    while (key != nums[mid]) {
        // 判断如果要找的值, 大于了取出的值, 那么min要改变
        if (key > nums[mid]) {
            min = mid + 1;
        // 判断如果要找的值, 小雨了取出的值, 那么max要改变
        }else if (key < nums[mid])
        {
            max = mid - 1;
        }
        
        // 超出范围, 数组中没有需要查找的值
        if (min > max) {
            return -1;
        }
        // 每次改变完min和max都需要重新计算mid
        mid = (min + max) / 2;
    }
//    printf("aaaaaa\n");
    
    return mid;

}

int findKey(int nums[], int key, int length)
{
    for (int i = 0; i < length; i++) {
        if (nums[i] == key) {
//            printf("%i\n", i);
            return i;
        }
    }
    return -1;
}

int main(int argc, const char * argv[]) {
    // 现有一个有序的数组, 要求给定一个数字, 将该数字插入到数组中, 还要保证数组是有序的
    // 其实就是找到需要插入数字的位置
    // 其实这个位置就是min的位置
    /*
     min = 0
     max = 4
     mid = 2
     */
//                 0  1  2  3  4  5
    int nums[5] = {1, 3, 5, 7,9};
    int key = 4;
    int length = sizeof(nums) / sizeof(nums[0]);
    printf("需要插入的位置是%i\n", insertValue(nums, length, key));
           
    return 0;
}

int insertValue(int nums[], int length, int key)
{
    int min , max, mid;
    min = 0;// 1 2
    max = length - 1;// 4  1
    while (min <= max) {
        mid = (min + max) / 2; // 2 0 1
        if (key > nums[mid]) {
            min = mid + 1;
        }else if (key < nums[mid])
        {
            max = mid - 1;
        }
    }
    return min;
}

进制查表法

#include <stdio.h>
void printfBinary(int value);
void printfBinary2(int value);
void printOct(int value);
void printfHex(int value);

void printfHex2(int value);
void printfOct2(int value);
void printfBinary3(int value);

int main(int argc, const char * argv[]) {
    /*
         0000000000000000000000000000
     00000000000000000000000000000111
     */
    int num = 10;// 1010;
//    printfBinary2(num);
//    printOct(num);
//    printfHex2(num);
//    printfOct2(num);
    printfBinary3(num);
    return 0;
}

void printfBinary3(int value)
{
    // 1.定义一个数组, 用于保存二进制中所有的取值
    char charValues[] = {'0', '1'};
    // 2.定义一个数组, 用于保存查询后的结果
    char results[32] = {'0'};
    // 3.定义一个变量, 用于记录当前需要存储到查询结果数组的索引
    int pos = 32;
    
    while (value != 0) {
        // 1.取出1位的值
        int res = value & 1;
        // 2.利用取出来得值到表中查询对应的结果
        char c = charValues[res];
        // 3.存储查询的结果
        results[--pos] = c;
        // 4.移除二进制被取过的1位
        value = value >> 1;
    }
    
    // 4.打印结果
    for (int i = pos; i < 32; i++) {
        printf("%c", results[i]);
    }
    printf("\n");

    
}

void printfOct2(int value)
{
    // 1.定义一个数组, 用于保存八进制中所有的取值
    char charValues[] = {'0', '1', '2', '3', '4', '5', '6', '7'};
    // 2.定义一个数组, 用于保存查询后的结果
    char results[11] = {'0'};
    // 3.定义一个变量, 用于记录当前需要存储到查询结果数组的索引
    int pos = 11;
    while (value != 0) {
        // 1.取出3位的值
        int res = value & 7;
        // 2.利用取出来得值到表中查询对应的结果
        char c = charValues[res];
        // 3.存储查询的结果
        results[--pos] = c;
        // 4.移除二进制被取过的三位
        value = value >> 3;
    }
    
    // 4.打印结果
    for (int i = pos; i < 11; i++) {
        printf("%c", results[i]);
    }
    printf("\n");
    
}

void printfHex2(int value)
{
    // 1.定义一个数组, 用于保存十六进制中所有的取值
    // 规律: 取出的4个二进制位得到的值, 正好是数组中角标对应的值
    char charValues[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
//                    '', '', '', '', '', '', '', ''
    char results[8] = {'0'};
    int pos = 8;
    
    while (value != 0) {
        // 取出4位的值
        int res = value & 15;
        // 利用这个值作为索引去数组中查询对应的十六进制的值
        char c = charValues[res];
//        printf("%c", c);
        // 将取出来得值放到用于存储结果数组中
        results[--pos] = c;
        
        // 每取完一次就干掉它最低的4位
        value = value >> 4;
//        printf("pos = %i\n", pos);
    }
    
    for (int i = pos; i < 8; i++) {
        printf("%c", results[i]);
    }
    printf("\n");
}

void printfHex(int value)
{
    for (int i = 0; i <= 8; i++) {
        int res = value & 15; // 1111
        // 对十六进制进行特殊处理
        if (res > 9) {
            //  11 - 10 1 + a
            char c = res - 10 + 'a';
            printf("%c", c);
        }else
        {
            printf("%i", res);
        }
        value = value >> 4;
    }
}

void printOct(int value)
{
    for (int i = 0; i <= 11; i++) {
        int res = value & 7; // 111
        printf("%i", res);
        value = value >> 3;
    }
}

void printfBinary2(int value)
{
    for (int i = 0; i <= 32; i++) {
        int res = value & 1;
        printf("%i", res);
        value = value >> 1;
    }
    printf("\n");
}

void printfBinary(int value)
{
//    int offset = sizeof(value) * 8 - 1;
    int offset = (sizeof(value) << 3) - 1;
    while (offset >= 0) {
        int res = (value >> offset) & 1;
        printf("%i", res);
        offset--;
    }
    printf("\n");
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容