函数
- 函数的声明
/*
* C语言规定:
* 如果将函数的敌营写在了函数调用之后,那么必须要在调用之前编写函数的声明
*
* 什么是函数的定义:
* 函数的定义就是函数具体实现的代码,真正实现了函数功能的代码
*
* 什么是函数的声明:
* 函数的声明就是在调用之前告诉编译器,有一个名字叫什么,接收什么参数,返回值是什么类型的一个函数
* 只需要将函数定义{前面的代码拷贝即可
*
*/
- 注意点
/*
* 注意点:
* 1. 如果不按照C语言的规定在函数调用之前编写函数的声明,那么在不同编译器下会有不同的运行结果
* GCC编译器可以编译通过,但是在其他编译器可能就会报错
* 2. 由于函数的声明仅仅是为了告诉编译器,我们有一个什么函数,所以函数的声明可以声明多次,但是函数的
* 实现只能一次
* 3. 由于函数的声明仅仅是为了告诉编译器,我们有一个什么函数,所以函数的声明只要写在调用之前即可
* 4. 由于函数的声明仅仅是为了告诉编译器,我们有一个什么函数,所以声明时候不用指定参数的名称
* 5. 如果函数的返回值类型是int类型,那么可以不用编写函数的声明,但是还是按规矩写上
*
* 总结: 函数定义编写在调用之后,在main函数外面编写函数声明,只要写上参数类型,参数名称可以不写
*
*/
test(int);//不需要写参数的名称
int main()
{
int a = 3;
test(a);
return 0;
}
test(int num){
return 0;
}
- main函数(了解)
#include <stdio.h>
/*
* 1. main函数是系统自动调用的函数,我们不能手动调用
* 2. 系统在调用main函数的时候,默认会传递两个参数
* argc: argv数组保存数据的个数
* argv: 默认保存了一个数据,这个数据就是当前文件的地址(数组)
* 3.argv中保存的元素是可以动态添加的
* 4. main函数的return 0的含义
* 告诉系统当前的程序是正常结束,如果return返回的不是0,就代表告诉系统程序不是正常结束的
*
*/
int main(int argc, const char *argv[])
{
argv数组保存数据的个数
printf("%i\n", argc); //1
//该文件的地址
printf("%s\n", argv[0]);
return 0;
}
- 函数的分类
test(int);
int test2();
int main()
{
/*
* 函数的分类:
* 没有返回值, 没有形参的函数
* 没有返回值, 有形参的函数
* 有返回值, 没有形参的函数
* 有返回值, 有形参的函数
*
*
* 注意点:
* 1. 如果没有写函数的返回值类型,默认就是int类型
* 2. 如果函数的返回值类型和实际返回的类型不同,那么会自动转换为返回值类型
*/
// int a = 10;
// int res = test(a);
// printf("%i\n", res);
int res = test2();
printf("%i\n", res); //返回类型是double自动转换为int类型
return 0;
}
test(int num){
return num;
}
int test2(){
return 3.15;
}
递归
递归就是自己调用自己,但是递归的性能与循环比较差,容易内存溢出
-
练习1
-内存分析
#include <stdio.h>
int pow(int, int);
int main()
{
/*
* 编写一个函数实现求b的n次方
* b(0) = 1
* b(1) = b
* b(2) = b * b
* b(3) = b(2) * b
* .
* .
* .
* b(n) = b(n-1) * b
*
*/
int res = pow(3, 3);
printf("%i\n", res);
return 0;
}
int pow(int num, int n){
if(0 == n){
return 1;
}else {
return pow(num, n-1) * num;
}
}
-
练习2
-
内存分析
-
#include <stdio.h>
int test(int);
int main()
{
/*
* 实现n!
* 1! = 1
* 2! = 1 * 2
* 3! = 1 * 2 * 3
* 4! = 3! * 4
* .
* .
* .
* n! = (n-1)! * n
*
*/
int a = 3;
int res = test(3);
printf("%i\n", res);
return 0;
}
int test(int n){
if(1 == n){
return 1;
}else {
return test(n-1) * n;
}
}
C语言的进制
- 基本概念
注意点: 0x和0X代表十进制, 0b和0B代表二进制,没有区别
int main()
{
/*
* 在C语言中,如果想要用十六进制的表示某个数,前面需要加上0x
* 在C语言中,如果如果想要用八进制的表示某个数,前面需要加上0
* 在C语言中,如果如果想要用二进制的表示某个数,前面需要加上0b
*
*/
//注意点:%i %d代表以十进制输出整数
int num1 = 12;
printf("%i\n", num1);
int num2 = 0xc;
printf("%i\n", num2);
int num3 = 014;
printf("%i\n", num3);
int num4 = 0b1111;
printf("%i\n", num4);
return 0;
}
- 进制输出方式
%o代表以八进制输出,%x代表以十六进制输出,以二进制输出需要自己实现
int main()
{
int num = 12;
//以八进制输出
printf("%o\n", num);
//以十六进制输出
printf("%x\n", num);
return 0;
}
- 其他进制的转换为十进制的简易方法
/*
* 二进制 -->十进制
* 规则: 系数 * 基数(索引次幂)
*
* 系数: 每一位对应的值就是系数
* 基数: 从二转换到十进制,那么二就是基数
* 从八进制转换到十进制, 八就是基数
* 索引: 从最低位以0开始,依次递增
*
* 1*2(3)+ 1*2(2)+ 0*2(1)+ 0*2(0)
* 1 1 0 0
*
*/
- 十进制转换为其他进制
/*
*
* 十进制转换二进制
* 规则: 除2取余数,余数倒叙
*
* 24
* 2
* ---
* 12 0
* 2
* ---
* 6 0
* 2
* ---
* 3 0
* 2
* ---
* 1 1
* 2
* ---
* 0 1
* 十进制转换八进制
* 规则: 除8取余,余数倒叙
*
* 十进制转换十六进制
* 规则:除16取余,余数倒叙
*/
二进制转八进制
注意点: 在八进制中最大的数字是7,三个二进制位最大能表示的数字就是7,把三位二进制数看最一个八进制
二进制转换为十进制
注意点: 在十进制中最大的数字是15,四个二进制位最大能表示的数字就是15,把四位二进制数看最一个八进制
在C语言中,不看数据怎么存,只看数据怎么取
源码反码补码
注意点: 在计算机当中无论是正整数还是负整数都是以补码的方式参与计算
- 正数的源码反码补码
/*
* 在计算机中,正数和负数的源码,补码,反码是有区别的
* 在计算机中所有存储的数据都是以0和1的方式存储的
*
*/
/*
* 正数的源码,补码,反码都是它的二进制
* 12---> 1100
*
* 1100 就是12的源码
* 1100 就是12的反码
* 1100 就是12的补码
*
* 注意点:
* 1. int类型在计算机中占4个字节,1个字节等于8位,所以int总共占32位
* 2. 在存储整数的时候,第一个(最高位)是符号位,0代表正数,1代表负数
*
* 12 --> 0000 0000 0000 0000 0000 0000 0000 1100
*/
- 负数的源码反码补码
/*
* 负数的源码,反码,补码
* -12
* 源码: 二进制,将最高位变为1
* 1000 0000 0000 0000 0000 0000 0000 1100
*
* 反码: 除了符号位以外, 0变为1, 1变为0
* 1111 1111 1111 1111 1111 1111 1111 0011
*
* 补码: 补码 = 反码+1
*
* 1111 1111 1111 1111 1111 1111 1111 0100
*/
- 练习
注意点: 参与运算的都是补码,所以计算出来的正确结果的是补码,要想知道正确结果是多少,需要将补码转换为源码
/*
* 注意点: 计算出来的正确结果的是补码,要想知道正确结果是多少,需要将补码转换为源码
*
* 4 - 6 = ?
*
* 0000 0000 0000 0000 0000 0000 0000 0100 4的补码
*
* 1000 0000 0000 0000 0000 0000 0000 0110 6的源码
* 1111 1111 1111 1111 1111 1111 1111 1001 6的反码
* 1111 1111 1111 1111 1111 1111 1111 1010 6的补码
*
*
* 运算:
*
* 0000 0000 0000 0000 0000 0000 0000 0100 4的补码
* 1111 1111 1111 1111 1111 1111 1111 1010 6的补码
* --------------------------------------------------
* 1111 1111 1111 1111 1111 1111 1111 1110 计算结果补码
*
* 1111 1111 1111 1111 1111 1111 1111 1101 计算结果反码
*
* 1000 0000 0000 0000 0000 0000 0000 0010 -2
*/
位运算
- 位运算符号
/*
* & 按位与
* | 按位或
* ~ 按位取反
* ^ 按位异或
*/
- 按位与
技巧: 任何一位和1相与,结果还是原来那一位
/*
* & 按位与
* 规则: 一假则假
* 4 & 3
*
* 0000 0000 0000 0000 0000 0000 0000 0100 4的补码 &
* 0000 0000 0000 0000 0000 0000 0000 0011 3的补码
* ---------------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0000 结果是0
*/
- 按位或
/*
*
* | 按位或
* 规则: 一真则真
*
* 4|3 = ?
*
* 0000 0000 0000 0000 0000 0000 0000 0100 4的补码
* 0000 0000 0000 0000 0000 0000 0000 0011 3的补码
* ---------------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0111 结果是7
*
*/
- 按位取反
注意点; 取反的时候符号位也一并取反
/*
* ~ 按位取反
* ~ 9 = ?
* 0000 0000 0000 0000 0000 0000 0000 1001 9的补码
* 1111 1111 1111 1111 1111 1111 1111 0110 取反后的补码
* -----------------------------------------------------
* 1111 1111 1111 1111 1111 1111 1111 0101 取反后的反码
* 1000 0000 0000 0000 0000 0000 0000 1010 -10
*
*/
- 按位异或
1. 任何两个相同的值异或,结果都是0
2. 任何一个数和0异或,结果还是那个数
3. 任何一个数异或另外一个数两次后,结果还是那个数本身
printf("%i\n", 4 ^ 4); // 0
printf("%i\n", 4 ^ 0); // 4
printf("%i\n", 5 ^ 4 ^ 4); // 5
/*
* ^ 按位异或
* 规则: 相同位0,不同为1
* 9 ^ 3 = ?
*
* 0000 0000 0000 0000 0000 0000 0000 1001 9的补码
* 0000 0000 0000 0000 0000 0000 0000 0011 3的补码
* ----------------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 1010 10
*/
- 面试题
判断奇偶数
int main()
{
/* 奇数按位与1,结果永远都是1,偶数按位与1,结果都是0
* 0000 0000 0000 0000 0000 0000 0000 0010
* 0000 0000 0000 0000 0000 0000 0000 0001
* -------------------------------------------
* 0000 0000 0000 0000 0000 0000 0000 0001
*
*/
/*
* 判断奇偶数的案例
*
*/
int num = -1;
if(1 == (num & 1)){
printf("奇数");
}else {
printf("偶数");
}
return 0;
}
将两个数的值相互交换的另一种方法
int main()
{
/*
* 交换两个数的值
*
*
*/
int a = 10;
int b = 20;
// 原理:就是利用一个数异或另一个数两次后,会得到它本身
a = a ^ b;
b = a ^ b; // b = (a ^ b) ^ b = a = 10
a = a ^ b; // a = a ^ a ^ b = b = 20
printf("a = %i, b = %i\n", a, b);
return 0;
}
- 左移和右移位(<< >>)
int main()
{
/*
* 左移 <<
* 规则: 一个数左移几位,就是这个数乘以2多少次幂
*
* 右移 >>
* 规则: 一个数左移几位,就是这个数除以2多少次幂
* 移动规则: 除符号位以外整体右移动,多出来的砍掉,缺少的用符号位填充
*
*/
printf("%i\n", 8 << 1); //左移1位
printf("%i\n", 8 >> 1); //右移1位
return 0;
}
- 练习
#include <stdio.h>
void printBinary(int);
int main()
{
/*
* 要求用户输入的整数,以二进制的形式输出
* 利用&1的特点:
* 每一位与1都能得到它本身,利用此特点可以输出
* 思路: 让该整数右移31位然后每次再往左走一位
*
*
*/
// int num = 10;
// for(int i = 0; i < 32; i++){
// int temp = (num >> 31 - i) & 1;
// printf("%i", temp);
// }
printBinary(10);
return 0;
}
// 封装方法让这个函数可以传其他基本数据类型的参数
void printBinary(int value){
//1. 判断字节数计算出所占位数
int len = sizeof(value) * 8;
//2. 让该值的二进制右移len -1然后每次再往左走一位
for(int i = 0; i < len; i++){
int temp = (value >> len - 1 - i) & 1;
printf("%i", temp);
}
}
内存存储
- 变量内存存储分析
int main()
{
/*
* 变量存储方式内存分析
* 1. 计算机分配内存的规则: 从大到小的分配
* 2. 变量名称对应的是变量占用内存最小的那个字节
*
* 由于计算机只能识别0和1,所以会把十进制的9转换为二进制的9
*
* 0000 0000 0000 0000 0000 0000 0000 1001
*
* 给变量分配内存的时候是从内存地址比较大的开始分配
* 所以计算机在存储数据的时候也会从内存地址比较大的开始存储
* 会从转换好的二进制高位开始依次从内存地址最大的字节开始存储
*
*/
// %p是专门用于输出变量地址的
// &是专门用于取出变量地址
int num1 = 9;
int num2 = 4;
printf("%p\n", &num1); //0028FEBC
printf("%p\n", &num2); //0028FEB8
return 0;
}
- 字符存储
int main()
{
/*
* 字符char存储分析:
* char = 'a';
* 1.由于计算机只能识别0和1所以不能直接将'a'存储到内存中
* 字符在计算机存储的其实是它对应的ASCII码值
* 2. ASCII码表是什么?
* ASCII码表是一套字符对应的数值表
*
* 注意点: C语言中不看怎么存,只看怎么取数据
* char类型只占用1个字符,所以只有8位可以存储数据
* ASCII码表中字符对应的ASCII码值是从0~127之间
*
*/
/*
*总结: 字符存储的顺序
* 1. 根据字符在 ASCII码表中找到对应的十进制数
* 2. 将找到的十进制数转换位二进制
* 3. 将转换好的二进制存储到内存中
*
*/
char ch = 'a';
printf("%i\n", ch); //97
return 0;
}
- 练习
在企业开发中,尽量不要把数据写死,这样便于后于修改
#include <stdio.h>
char upperCase(char);
int main()
{
/*
* 定义一个函数实现用户传入小写字母,将其转换为大写字母
* a~z 对应的是97~122 A~Z对应65~90
* 规律 小写字符减去大写字符 相差32
*/
char ch = 'r';
char res = upperCase(ch);
printf("%c\n", res);
return 0;
}
char upperCase(char ch){
// 限定输入的条件
// 在企业开发中尽量不要写数字,这种写法便于以后维护
if(ch < 'a' || ch > 'z'){
printf("请输入正确范围内的字母\n");
}else {
char res = ch - ('a' - 'A');
return res;
}
}