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)!
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所指向的变量,其中的“”表示指向。
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");
}