#C语言
一基础知识
1 计算机基本结构

2 程序是什么
- 广义的讲,为了实现一个特定的目标而预先设计的一组可操作的工作步骤就可以称之为一个程序
找工作: 写简历-投简历-笔试-面试 -入职
- 对于计算机系统,简单的说,程序就是系统可以识别的一组有序的指令
- 程序存储在磁盘上,被加载到内存中,计算机系统从内存中逐条读取指令并执行
3 存储器的分类
存储器可以简单的分为主存储器,外存储器和寄存器
主存储器也称为内存储器、内存,是计算机的工作存储器。程序,待处理的数据和处理的结果都存储在内存中。内存通常以字节为单位对存储单元按顺序进行编址。内存读写速度比较快,但只能在加电时存储数据
外存储器是用来长期保存数据的大容量存储器,目前多为磁存储器或光存储器。读写速度慢于内存,可以在不加电的情况下长期保存数据
寄存器是CPU内部的高速存储器,速度快,数目少。
4 程序设计语言
机器语言
直接使用机器指令(0,1序列)来设计程序,可以 被计算机系统直接识别。和自然语言完全不同,难于记忆和理解,工作量大,效率低,无法移植
00011000
00110001
00011001汇编语言
把机器指令符号化,即通过一组简单的的符号来表示机器指令,更接近于自然语言,更容易理解和使用。 不能被计算机系统直接识别,要转化成机器语言后才能执行。和机器语言一样,开发工作量大,无法移植
如:MOV A, 1000 MOV 1010, A高级语言
更接近于人类自然语言。一条语句对应多条机器指令,工作量小,开发效率高。同样需要转换成机器代码后才能被计算机系统识别、执行。 BASIC , FORTRAN, PASCAL, C, C++,java,oc,javaScript, 很多情况下,高级语言可以通过编译器或解释器实现跨系统平台
5 高级语言分类
编译型:
- C,C++,oc,swift
解释型 脚本语言动态语言
- python,javaScript
编译解释型
- java
6 C库
在实际的程序设计中,经常需要使用到一些基本功能,例如输入输出,各种常用计算等。但是这些功能对于一般程序员来说难以自行开发,却要经常用到,因此编译系统通常将这些功能预先编制好,以程序库的形式提供给程序员使用

7 计算机数据表示
计算机中数据的概念是广义的, 数字,字母,符号,图像,声音,逻辑......都是根据特定规则以0、1形式保存于计算机中的。
8 十进制、二进制、八进制、十六进制
8.1 进制数的定义
十 进制逢10进1 基数为0,1,2,3,4,5,6,7,8,9
二 进制逢2进1 基数 为 0,1
八 进制逢8进1 基数为 0,1,2,3,4,5,6,7
十六 进制逢16进1 基础数0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
8.2 十进制数0到16与其它进制数对照表

8.3 不同进制数之间的相互转换

9 ASCII
ASCII码被化分成三部分
(1)“ASCII非打印控制字符表”,
数字0–31分配给了控制字符,用于控制像打印机等一些外围设备。
(2)“ASCII打印字符”,
数字 32–126 分配给了能在键盘上找到的字符,当您查看或打印文
档时就会出现。数字127代表 DELETE 命令
(3)“扩展ASCII打印字符”,
128—256 的128个字符


10 window中C语言开发环境
window上安装devcpp
简单的C示例的创建编译运行, 文件名以.c结尾
#include<stdio.h>
int main() {
printf("Hello World!\n");
return 0;
} 11 linux中C语言开发环境
11.1 安装vm
11.2 在vm中安装ubantu
11.3 打开终端
右键桌面-Open inTerminal
11.4 安装 net-tools
sudo apt-get install net-tools
有了net-tools后才能使用网查命令 ifconfig
11.5查看网卡信息
ifconfig
11.6查看操作系统位数
getconfLOGN_BIT
11.7查看网络是否连接正常
ping www.baidu.com
11.8Linux系统目录结构
https://www.runoob.com/linux/linux-system-contents.html
11.9 显示当前绝对路径
pwd
11.10 Linux文件系统结构
Linux以文件形式管理所有的软件与硬件资源
- / 根目录。
- /usr 这个是系统存放程序的目录,比如帮助文件等。
- /home 普通用户默认存放目录
- /bin 基础系统所需要的命令位于此目录。
- /boot 包含Linux内核及系统引导程序所需要的文件。
- /dev 设备文件存储目录,比如声卡、磁盘
- /etc 存放系统程序或者一般工具的配置文件。
- /home 普通用户默认存放目录
- /lib 库文件存放目录这里包含了系统程序所需要的所有共享库文件,类似于 Windows 的共享库 DLL 文件。
- /lost+found 在ext2或ext3文件系统中,当系统意外崩溃或机器意外关机,而产生一些文件碎片放在这里。当系统启动的过程中fsck工具会检查这里,并修复已经损坏的文件系统
- /media 即插即用型存储设备,比如USB盘
- /mnt 这个目录一般是用于存放挂载储存设备的挂载目录的,比如光驱 。
- /opt 也就是自定义软件包
- /proc 操作系统运行时,驻留在内存中的文件系统。
- /root Linux超级权限用户root的家目录;
- /sbin 超级权限用户root的可执行命令存放地
- /tmp 临时文件目录,有时用户运行程序的时候,会产生临时文件。有些系统关机时清空此目录。
- /var 这是用来存放系统日志的目录。
11.11 查看目录中的文件
ls 显示当前目录中文件
ls 目录 显示目录中文件
参数-l详细显示
11.12相对路径和绝对路径.
绝对路径,从根目录 / 开始的全路径。
相对路径
./ 当前目录
../ 当前目录的上级目录
../../ 当前目录的上上级目录,可以以至类推到更上一级目录
11.13 cd目录切换
cd 目录 进入目录
练习:
进入根目录 cd /
返回上级目录 cd ../
返回上上级目录 cd../../
进入etc目录 cd /etc
11.14创建文件
创建目录 mkdir test
创建目录及其子目录 mkdir
创建可编辑文件 touch test.c
11.15删除文件或目录
删除文件 rm hello.c
删除目录 rm mytest -r
11.16移动文件与目录, 或修改名称
mv 被移动的文件或目录 目录
mv 被移动的文件或目录 目录/
11.17查看文件内容
cat 文件
11.18 gedit编辑文件
gedit test.c 创建并打开test.c文件或打开test.c文件。
11.19 vim编辑文件

11.20 gcc
11.20.1 测试是否有gcc
gcc -v
11.20.2 安装gcc
sudo apt install gcc
11.20.3 简单的C示例
创建文件test.c
#include<stdio.h>
int main() {
printf("Hello World!\n");
return 0;
} 编译test.c
gcc test.c 会得到一个编译后的文件 a.out
gcc test.c -o test 会得到一个编译后的文件 test
运行a.out或test
./a.out ./test
11.21 文件权限管理
r:可读 数字为4
w:可写 数字为2
x:可执行 数字为1
查看文件权限 ls 文件 -l
查看到的文件权限依次是 管理员,当前用户,其它用户的权限
chmod 数字 文件 可修改文件的权限.
示例
chmod 327 a.c
3=1+2管理员权限修改为rw
2当前用户权限修改为w
7=1+2+4其它用户的权限为rwx
二 语法基础
1 主函数main
文件必须以.c为后缀, C语言的入口执行函数是main函数
int main() {
//......
return 0;
}
或
void main() {
//......
} 2 字符串
使用双引号引起来的一串字符
3 打印语句
打印字符串printf("打印一个字符串");
打印换换printf("换行打印\n");
使用printf是必须要导入系统库函数 #include<stdio.h>
4 注释
注释:对程序运行没有影响的东西, 只是给人看的.
当行注释 //
多行注释 /*这是注释*/
5 自定义无参无返回值函数
/**
void:表示无返回值声明
test:是标识符
函数体{任意多的语句}
*/
void test( ) {
printf("你好\n");
printf("Hello\n");
} 位置在后的函数可以调用位置在前的函数或位置在前声明过的函数
函数与函数之间可以相互调用, 但是不能调用main函数,
调用函数的语法是 函数();
extern void f2();//声明函数,extern可省
extern void f1();//声明函数, extern可省
void main() {
f1();//调用f1函数
f2();//调用f2函数
}
void f2() {//创建函数
f1();//调用f1函数
}
void f1( ) {
printf("你好\n");
printf("Hello\n");
} 6 局部变量入门
int 是一个整形类型关键字
在函数中声明的变量是局部变量
void f1() {
int x = 10;
int y;
y = 20;
int c = x + y;
printf("%d\n", c);
} 创建变量语法
type 变量名;(局部变量中默认值不可控,开发中都建议变量先赋值后使用) 或 type 变量名=值;
"%d"是打印整数格式控制符
x+y中"+"是算术运算符,表示相加
为变量赋值时,值可以是直接数、变量、表达式、函数返回值(后面讲)。
三 基本概念
1 关键字 (32个)
char :声明字符型变量或函数 short :声明短整型变量或函数 int :声明整型变量或函数 long :声明长整型变量或函数 float :声明浮点型变量或函数 double :声明双精度变量或函数 signed :声明有符号类型变量或函数 unsigned :声明无符号类型变量或函数 enum :声明枚举类型 struct :声明结构体变量或函数 union :声明联合数据类型 void :声明函数无返回值或无参数,声明无类型指针 if :条件语句肯定分支 else :条件语句否定分支(与 if 连用) switch :用于分支语句 case :开关语句分支 default :开关语句中的“其他”分支 for :一种循环语句(可意会不可言传) while :循环语句的循环条件 do :循环语句的循环体 goto :无条件跳转语句 break :跳出当前循环 continue :结束当前循环,开始下一轮循环 return :子程序返回语句(可以带参数,也看不带参数) auto :声明自动变量,在栈区开辟空间, 一般不使用 static :声明静态变量 extern :声明变量是在其他文件正声明(也可以看做是引用变量) register :声明寄存器变量,申请失败,默认转成auto类型 const :修饰变量为只读变量 volatile :修饰变量,读取最新的值(防止编译器优化;从内存读取最新的值) sizeof :计算数据类型占内存大小 typedef :用以给数据类型取别名(当然还有其他作用)
关键字分类
(1)数据类型(有符号,无符号): char, short, int, long, float, double, signed, unsigned, enum, (2)存储类型: auto, static, extern, register, (const, volatile,用于修饰) (3)控制语句: if, else, switch, case, default, for, while, do, goto, break, continue, return (4)构造: union, struct, (5)求字节: sizeof (6)取别名: typedef (7)空类型: void
总结
*static 总结 :* *修饰局部变量,延长生命周期,将变量放到静态区(全局变量区)。作用域没有发生改变* *修饰全局变量,限制作用域,限制变量在本文件内使用* *修饰函数,限制作用域,限制函数在本文件内使用* *extern 总结 :* *外部引用,引用在文件外定义的变量* *扩大了作用域,和static互斥,只能引用全局变量* *const 总结 :* *1.修饰指针,根据位置的不同,所限制的内容也不同* *const int * p;* *int * const p;* *const int * const p;* *2.修饰变量,将变量常量化,不能在后面被改变(需要初始化)* *3.修饰形参,表明只是输入参数,不能在函数内部改变。
2 标识符
标识符是由程序员按照命名规则自行定义的词法符号,用于定义宏名、变量名、函数名和自定义类型名等。 命名规则: *1.由字母、数字、下划线、$组成* 123fdsa$_ aabb *2.不能由数字开头* fdsa$_123 *3.不能和关键字重名
3 运算符
1.算数运算符 + - * / %2.关系运算符 > < >= <= != == 返回 0 或 13.逻辑运算符 逻辑:将数据看做一个整体,只区分真(1)和假(0),非零为真(1) &&(逻辑与) ||(逻辑或) !(逻辑非)4.位运算 将数据转换为二进制,计算每一位 &(位与) |(位或) ~(取反) <<(左移) >>(右移) ^(异或,相异为一) char 0 & 1 = 0000 0000 & 0000 0001 取反:对于负数,符号位要取反 <<(左移) char 0101 1011 << 2 = 0110 1100 >>(右移) unsigned char 1101 1011 >> 2 = 0011 0110 >>(右移) signed char 1101 1011 >> 2 = 1111 0110 >>(右移) signed char 0101 1011 >> 2 = 0001 0110 对于负数,右移时,左边补15. 赋值运算符=将=右边的值复制一份给左边,右边本身不变6.赋值复合运算符+= -= *= /= %/ &= |= ^= <<= >>=a+=b <==> a=a+b int r = 10;r += 2;// r = r + 2; //12r |= 3;// r = r | 3; 0000101000000011---------00001011r >>= 3; //r = r>>3;7.特殊运算符:sizeof() 求数据字节大小 ?: 条件运算符 c = (a>b)?100:33; 如果a>b为真,c=100; 反之,c=3 , 逗号运算符,括号内从左向右依次计算,取最右边的值,优先级最低c = (a=3, b=4, a+=b, a-1) c=6 ++ 自增运算符前加加 ++a <==> c=a+1 a=c 先运算(加一),后赋值后加加 a++ <==> a=a a=a+1 先赋值,后运算(加一) -- 自减运算符前减减 --a <==> c=a-1 a=c 先运算(加减一),后赋值后减减 a-- <==> a=a a=a-1 先赋值,后运算(减一) (类型) 强制转换运算符 () 括号运算符,先算括号内内容,优先级最高 & 取变量的地址 * 取地址中的内容
按照操作数分类
单目运算符:只有一个操作数 ~ ! ++ -- 双目运算符:有两个操作数 + - * / = && || & | ....三目运算符:有三个操作数 ? :
//非 算 关 与或 赋
! &&|| =
4 分隔符
分隔符是用来分隔其他的词法符号
空格 tab 换行 注释
通过对分隔符的恰当运用,使得代码的外观格式更为清晰易读,还可以帮助分析程序中的语法错误
5 标点符号
C语言中具有特殊意义的一些符号
逗号、分号、冒号、花括号、圆括号。
, : ; () {}
标点符号的作用与分隔符相似,但用法非常严格,有着明确的语法规定。
有些标点符号出现在表达式中时,当作运算符使用(, ! ?:)
6 语句 使用;结束
调用函数语句,赋值语句,创建变量语句
7 表达式
使用运算符进行运算的式子,如 a+b;
四 数据类型
1 数据类型分类
2 数值型和字符型介绍
| 类型 | 占有内存(单位字节) | 存储值范围 |
|---|---|---|
| short 或signed short | 2 | -215~215-1 |
| unsigned short | 2 | -0~216-1 |
| int 或 signed int | 4 | -231~231-1 |
| unsigned int | 4 | 0~232-1 |
| long 或 signed long | 4 | -231~231-1 |
| unsigned long | 4 | 0~232-1 |
| long long或signed long long | 8 | -263~263-1 |
| unsigned long long | 8 | 0~264-1 |
| float | 4 | -3.40E+38 ~ +3.40E+38 |
| double | 8 | -1.79E+308 ~ +1.79E+308 |
| char或signed char | 1 | -27~27-1 , (-128, 127) |
| unsigned char | 1 | 0~28-1 |
int a = 3423;
3 数值型变量
4 字符型变量
(1)取值范围和值形式
char变量可以保存ASCII[0~127]的字符**unsigned char变量可以保存ASCII[0~255]的字符
(2)字符值的形式
单引号引用字符整数(因为字符本质上是整数)转义字符
(3)转义字符
用\将字符原本的意义改变,赋予了新的含义1. 将看不见的字符,转换为看的见得 \n //换行(LF) ,将当前位置移到下一行开头 \t //水平制表符(HT)(跳到下一个TAB位置) \0 //0,空字符(NULL),字符串结束标志 \v //垂直制表(VT) \f //换页(FF),将当前位置移到下页开头 \a //响铃(BEL) \r //回车(CR) ,将当前位置移到本行开头 \b //退格(BS) ,将当前位置移到前一列2. 将一些有特殊含义的字符,转换为本身的含义 \\ //代表一个反斜线字符''\' \' //代表一个单引号字符 \" //代表一个双引号字符 3. 以8进制输出字符,\后跟八进制的3个数字 \ddd //1到3位八进制数所代表的任意字符 三位八进制 printf("\1011010"); //A1010 4. 以16进制输出字符,\后跟 x + 十六进制的2个数字 \xhh //1到2位十六进制所代表的任意字符 二位十六进制 printf("\x4111010"); //A1010 (3)格式控制符
%c 字符输出%d 10进制输出%x 无符号16进制输出
5 typedef
#include<stdio.h>
void main() {
typedef int myint;
myint x = 100;
printf("%d\n", x);
} 五 控制输入输出
1 格式控制符
格式控制:
%d //%i 有符号十进制整数 %u //无符号十进制整数 %x //%X 无符号十六进制整数 %o //无符号八进制整数 %c //单个字符 %s //字符串 %e //单精度浮点数,以指数形式 %le双精度 %f //单精度浮点数,以小数形式 %lf双精度
修饰符:
m //限制输入域宽,输入个数大于m,取m个数据,输入个数小于m,原样输入 * //抑制符,读取数据后不存入变量中 getchar(); 用于清除垃圾字符 h //用于d i u x o 前,指定输入为短整形 l %lf //用于f e前,指定输入为双精度 %ld //用于d i u x o前,指定输入为长整形
2 字符输入函数
getchar() 添加头文件: #include <stdio.h> //标准输入输出库 函数原型: int getchar(void); 参数: void //无参数 返回值: int //成功返回的到的字符,字符本质是ASCII,失败返回EOF(-1) char ch; ch = getchar();
3 字符输出函数
putchar()
添加头文件: #include <stdio.h> //标准输入输出库
函数原型: int putchar(int c);
参数: int //字符
返回值: 返回成功输出的字符,没有成功输出,返回EOF(-1)
char ch = 'S';
int ret = putchar(ch);
if(ret == EOF)
printf("error\n");
三种用法:
char a = 'A'; putchar(a);
putchar('A');
putchar(65); 4 格式输入函数
scanf()
功能:按指定格式从键盘读入数据,存入地址表指定存储单元中,并按回车键结束
添加头文件: #include <stdio.h> //标准输入输出库
函数原型: int scanf(const char *format, ...);
参数: 格式控制串,地址
返回值: 返回成功输入数据个数,没有成功输入返回EOF(-1)
格式: scanf("格式控制串",地址表)
scanf("hello%d", &a); //hello123
scanf("%d,%d,%d", &a, &b, &c); //123,456,789
//error : scanf("%d\n", &a); //123 回车 回车
scanf("%d%d%d", &a, &b, &c); //123 456 789 5 格式输出函数
printf()
功能:按指定格式向显示器输出数据
添加头文件: #include <stdio.h> //标准输入输出库
函数原型: int printf(const char *format, ...);
参数: 格式控制串, 变量名
返回值: 返回成功输出的字符个数,没有成功输出,返回EOF(-1)
格式1:printf("字符串"); //原样输出
格式2:printf("格式控制串",输出表); //printf("%d", a); 6 清除输入垃圾字符
1 使用输入函数可能会留下垃圾
int x;char ch;scanf(“%d”,&x);uprintf(“x=%d,ch=%d\n”,x,ch);
2 清除方法1:getchar()
int x; char ch; scanf(“%d”,&x); getchar(); ch=getchar(); printf(“x=%d, ch=%d\n”, x, ch);
3 清除方法2:"%*c"
int main() { int x; char ch; scanf("%d", &x); scanf("%*c%c", &ch); printf("x=%d,ch=%c\n", x, ch); return 0;} 六 负数补码存储
1 整数(包含char)在计算机以二进制补码形式存储,2 最高位是符号位,1表示负数,0表示正数3 正数的补码与原码相同4 负数的补码 = 负数反码+1 负数原码=负数绝对值原码,最高补1 负数反码=负数原码取反(符号位不可取反) 负数的补码 = 负数原码取反(符号位不变)+1 总结: 负数补码=原码取返+1示例:1.char x = 53; 原码为: 0 0 1 1 0 1 0 1 补码为: 0 0 1 1 0 1 0 12.char y = -53 原码为: 1 0 1 1 0 1 0 1 反码为: 1 1 0 0 1 0 1 0 补码为: 1 1 0 0 1 0 1 13.short z = 226 原码为: 0000 0000 1110 0010 补码为: 0000 0000 1110 00104.short a = -226 原码为: 1000 0000 1110 0010 反码为: 1111 1111 0001 1101 补码为: 1111 1111 0001 1110
七 类型转换
1 自动类型转换
使用变量b为变量a赋值时,如果b的存储范围在a的存储范围之内,则自动转换
使用直接值为变量a赋值是,如果值是b的存储范围在a的存储范围之内,则自动转换
2 强制类型转换
//存在范围在变大char, short, int, long, long long, float, double long double
使用变量b为变量a赋值时,如果b的存储范围在a的存储范围之外,则强制转换,使用直接值为变量a赋值是,如果值是b的存储范围在a的存储范围之外,则强制转换加()进行强制转换 short a = -226; char c = (char)a; 当值也是一个变量的时候,()可省 short a = -226; char c = a; 当值是一个直接数的时候,()不可省 char c = (char)-226;//()不可省
3 一步到位
当一个变量接收一个值, 不管是什么情况下,都会按照值的2进制存储到变量中, 所以: 只要该值 是在变量存储范围之内, 值就会正常.只要该值不是在变量存储范围之内, 就只能是二进制的底位存储到变量中,超过的高位被裁剪.示例: short a = -226;//补码: 1111 1111 0001 1110 char c = (char)a;//底8位赋给了c ,即 c接收到的是 0001 1110 printf("%d\n", c);//30, 因为30的补码刚好就是 0001 1110 七 变量
1.任何类型都可以定义变量
2.局部变量:
2.1函数中定义的变量是局部变量
2.2局部变量不赋值的情况下都会有默认的随机值
2.3局部变量的生命周期?作用域范围? 函数栈?
3.全局变量
//示例1#include<stdio.h>int x;//创建全局变量x int y=10; //创建全局变量y;extern void f1();void main() { f1();}void f1() { printf("%d,%d\n",x,y);} //示例2#include<stdio.h>//以下的extern都是可以省的. extern void f1();extern int x;//声明全局变量x extern int y; //声明全局变量y;void main() { f1();}void f1() { printf("%d,%d\n",x,y);}int x;//创建全局变量x int y=10; //创建全局变量y; 3.1 定义的位置: 全局区
3.2 默认值:全局变量创建时可初始化,如果没有初始化会有固定的默认值,具体是多少写代码测试.
3.2 作用域范围: 全局变量可以创建在使用之前, 也可以被声明在使用之前.
3.3 生命周期: 全局变量存储在数据段,所以创建之后就不会被销毁.
3.4 存储内存: 数据段
八 多文件组织
1 本小节要解决的问题: 一个程序往都是由多个.c文件组成的, 当有多个.c文件时,我们该怎么组件文件呢(这就要用到.h文件,在更高级部分会使用makefile处理) ? 有多个文件时,又该怎样编译呢?
2 组织文件提倡的方式:
(1)除main函数所在的.c文件之外,所有.c文件中都对应创建一个.h文件,并且把对应的.h在.c文件中使用#include导入.
(2)在.c文件中创建函数和全局变量,在.h文件中声明函数和全局变量, .h文件要使用预编译排除重复导入冲突.
(3)任何.c文件中要使用其它.c文件中的全局或函数时,#include导入.c文件对应的.h文件即可,使用相对路径.
示例:
//tool.h#ifndef __TOOL_H__#define __TOOL_H__extern int a;extern void f1();#endif
//tool.c#include"./tool.h"#include<stdio.h>int a = 100;void f1() { printf("tool f1 a=%d\n", a);} //test.h#ifndef __TEST_H__#define __TEST_H__extern int b;extern void f2();extern void f3();#endif
//test.c
#include"./test.h"
#include"./tool.h"
#include<stdio.h>
int b = 200;
void f2() {
f1();
f3();
}
void f3() {
printf("test.c f3 a=%d,b=%d\n",a,b);
} //main.c
#include"./tool.h"
#include"./test.h"
#include<stdio.h>
void main() {
f1();
f2();
f3();
} 九 运算符
1 括号运算符
2 算术运算符
+、-、*、/、% int a = 10; int b = 20; int c = (a+b)*2/2%(5+a) int d = 1+2*3+4; int e = (1+20)%(4*2) 0求余任何数结果都为0
3 自加自减运算符
++分前后自加--分前后自减前: 前计算再取值后: 先取值再计算 int a = 100;int b = a++; //b的值为100, a的值为101int x1 = 100;int x2 = --x1; //x2的值为99, x1的值为99int f() { int a = 100; int b = 200; int f = (++a+b); //101+200 int f2 = (a++ +b); //101+200 printf("f=%d\nf2=%d\n",f,f2); //f=301 //f2=301 printf("a=%d\nb=%d\n",a,b); //a=102 //b=200 return 0;} 4 赋值运算符
int a = 10;int c = a;int d = a + c + 10;1.赋值运算符的优先级很底2.赋值运算符先计算右边,再计算左边3.赋值表达式整体也有值,就是被赋的值, 赋值运算赋从右往左int a, b;a = b = 100+3;
5 赋值运算符扩展
+=: x+=y; x = x + y; -=: x-=y; x = x -y *=: x*y; x = x * y; /=: x/=y; x = x / y; %=: x%y; x = x % /y;
6 比较运算符
>、<、==、!=、>=、<= 数值型:整数与浮点数 数值型数据才能使用比较运算符运算 运算结果只有真(1)或假(0)。 c使用非0与0表示真假 对于js来说返回值是 true或false 对于C来说返回值是 1 或 0
7 逻辑运算符
对于C来说返回值是 1 或 0 &&(与)、||(或)、!(非)、^(异或) a&&b: 若a、b都为真则结果为1,否则为0 a||b: 若a、b至少其一个为真则结果为1, 否则为0 !a: 或a为真,则结果为0, 若a为假则结果为1 a^b. 或a与b其中任意一个为真,并且另一个假,则结果为1,否则为0
8 三目运算符
? : int a = 10;int b = 20;int r = a>b ? 10000 : -20;//r = -20
9 逗号运算符
组果为最后一个值int a = 1, 2, 2, 3; //a的值为3
10 位运算?
将数据转换为二进制,计算每一位
&(位与) |(位或) ~(取反) <<(左移) >>(右移) ^(异或,相异为一)
char 0 & 1 = 0000 0000 & 0000 0001
取反:对于负数,符号位要取反
<<(左移) char 0101 1011 << 2 = 0110 1100
>>(右移) unsigned char 1101 1011 >> 2 = 0011 0110
>>(右移) signed char 1101 1011 >> 2 = 1111 0110
>>(右移) signed char 0101 1011 >> 2 = 0001 0110
对于负数,右移时,左边补1
11 sizeof() 运算符
求数据字节大小了
12 地址相关运算符
& 取变量的地址
* 取地址中的内容\
13 运算符优先级
优先级口决: 非算关与或赋两个不一样类型进行计算,计算前会先把小类型自动转换为大类型再计算
| 括号 | 非 | 算 | 关 | 与、或 | 赋 |
|---|---|---|---|---|---|
| ( ) | ! | +、-、*、/、% | >、<、>=、<=、==、!= | &&、|| | = |
例0: int x = 3+2 > 6-9>23*23&&23 例1:int b = a + 2*a; 例2:int b = (a+2)*a; 例3:当 m =2,n =1,a=1,b =2,c=3时,执行完 d =(m =a!=b)&&(n = b >c)后;n的值为 例4: int a=3; int b=3; float x=0.35; float y=2.5 (float)(a+b)/2+(int)x%(int)y float x1 = 20.2; int x2 = 2; float r = x1 + x2;
十 函数返回值和参数
1 返回值
函数可以使用任何类型定义返回值,当没有返回值时使用void定义
函数休中最后一条执行的 return 值;语句中值就是函数的返回值.该值的类型要与返回类型相同和能自动类型转换,如果不同会报错或强转.
调用有返回值的函数可以接收到函数执行的返回值, 当然也可以不接收
2 函数参数
参数可以是任意多个
参数类型可以任意
参数也是局部变量, 函数执行时最先开辟的变量就是函数的参数.
十一 流 程 控 制
1 if...else...

2 Switch
1.没有default
void f5(int i2) {
switch(i2) {
case1:
printf("1");
break;
case2:
printf("2");
break;
case3:
if(i2==3) {
printf("3");
}elseif(i2 == 4) {
printf("4");
}
break;
case21:
printf("什么品种啊");
}
}
break表示结束分支,只能位于case分支的最后一条语句
每个case后至少有一个语句
default:可有可无
switch(xxx)中的xxx只能是char、short、int
2.有default, 如果没有符合i2的case, 那么就会执行default
void f5(int i2) {
switch(i2) {
case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
if(i2==3) {
printf("3");
}else if(i2 == 4) {
printf("4");
}
break;
case21:
printf("什么品种啊");
default:
printf("default");
}
} 3 do…while循环
4 break结束循环
5 continue结束本次循环
6 return结束方法
7 控制语句嵌套
8 goto语句无条件跳转
可读性差,开发中建议不用语法为定义一标记, 标记名:跳转到标记处,goto标记名:示例1:int main() { int x = 0; myxx: printf("x: %d", x); x++; if (x<6) { goto myxx; } } return 0;}结果:x: 0x: 1x: 2x: 3x: 4x: 5示例2:int main() { int i; int j; for (i = 0; i < 5; i++) { for (j = 0; j < 3; j++) { printf("i = %d, j=%d\n", i, j); if(j >= 1) { goto outer; } } } outer: printf("循环结束...\n"); return 0;} 结果:i = 0, j=0i = 0, j=1循环结束... 9 不可达代码
不能出现编译期的不可达代码
十二 数组
连续相同类型承储空间!
1 一维数组
(1) 一维数组定义
type arrayName[length];
int a[3]; int len = 3;int b[len];
每个元素默认值是随机的,每个元素都可通过下标来访问,元素下标从0开始。
(2)一维数组理解
下标从0开始

(3) 一维数组的引用
格式: 数组名[下标]通过引用可以对数组中元素进行获取和修改
(4)求长度
sizeof(a)/sizeof(a[0]);怎样遍历数组
(5)一维数组初始化
初始化语法只适用于定义数组时.
int a[3] = {1,2,3};//[1,2,3]int b[] = {1,2,3};//[1,2,3]int c[4] = {1,2};//[1,2,0,0]int d[3] = {0};//[0,0,0]int e[3] = {1};//[1,0,0] 2 二维数组
(1) 二 维数组的定义
数据类型 数组名[m][n];
int a[3][4]; float b[2][5];
(2) 二维数组理解
内存是一维的

(3) 二维数组的引用
通过两个下标可以对元素进行赋值和获取
(4) 求长度
一维长度: sizeof(a)/sizeof(a[0])二维长度: sizeof(a[0])/sizeof(a[0][0])怎样遍历数组?
(5) 二维数组初始化
初始化语法只适用于定义数组时.
1 降维给二维数组赋初值。每一组的初值都用大括号{ }括起来; 例如:int a[2][3]={{1,3,2},{3,5,4}}; int a[2][3]={{1,3,2},{3}};2 按线性存储形式给二维数组赋值; 例如:int a[2][3]={1,2,3,4,5,6}; int a[2][3]={1,2,3};3 可以省略左边下标范围的方式给二维数组赋初值。 例如:int a[][4]={{1,2,3,4},{4,3,5,2},{4,1,2,3}}; int a[][4]={{1,2,4},{4,3,5,2},{4,1}}; 3 多维数组
具有两个或两个以上下标的数组称为多维数组。例如: int a[2][3] ; //定义了一个二维整型数组a double b[3][4][5]; //定义了一个三维双精度实型数组b
十三 常量
1 常量: 值不能被改的变量 (还是属于变量)2 语法: const 类型 变量 = 值;或 类型 const 变量 = 值; (提示:常量可以是全局变量也可以是局部变量, 因为常也是变量)3 常量开发中不该使用的两语法定义常量时不赋值,那么常量会有一个值, 定义之后常量同样不能赋值示例: const int m;通过常量的指针能改变常量,但实计开发中千万别这样去做
十四 指针
1 指针的概念
指针==地址~=编号, 存储介质以 8个位 = 1字节 每个字节都有一个编号,即都有一个指针,即都有一个地址 指针是无符号整数, 在32位系统中值的范围是32位,在64位系统中值的范围是64位.
2 变量的指针
概念:变量的首字节指针当整个变量的指针。变量的指针也常叫变量的地址。
取地址:&变量
输入格式控制符: %p, 会以16进制输出
void f1() {
short age = 30;
short *p = &age;
printf("age=%d, p=%p", age, p);
} 3 指针变量
概念:
存储指针的变量
占用内存:
32位系统中占用4字节, 64位系统8字节。
创建指针变量:
type *变量名<=指针>;
指针变量的值:
int *(int指针变量用于存储int变量地址)
char *(char指针变量用于存储char变量地址)
double*(double指针变量用于存储double变量地址)
以此类推......
常用俗语:
指针指向变量
示例:
int main() {
int a=10;
char b='x';
double c=30.3;
int *p1 = &a;
int *p2 = &b;
int *p3 = &c;
printf("&a=%p\n&b=%p\n&c=%p\n", p1,p2,p3);
return 0;
}
多个指针变量指向同一内存
#include<stdio.h>
void f1() {
int x = 100;
int *p1 = &x;
int *p2 = &x;
int *p3 = p2;
printf("*p1=%d\n", *p1);//*p1=100
printf("*p2=%d\n", *p2);//*p2=100
printf("*p3=%d\n", *p3);//*p3=100
} 5 变量有四个很重要的属性
*1.变量名 2.变量保存的内容 3.变量的类型(决定了占用多少个字节) 4.变量的指针(所占的第一个字节的指针, 指针变量也有指针)
6 *指针取内存
"*指针": 取内存"&内存": 取指针
#include<stdio.h>int main() { int a = 23; //内存名字叫a, 保存的值是23, 它的地址&a int * p; //"int *" 为int 指针类型,只能保存int类型内存的指针 p = &a;//取出a的指针 int x = *p; //*为运算符,取出p指向的a printf("x=%d, *p=%d\n", x, *p);//x=23, *p=23 return 0;} 思考题1:int a = 100;*(&a)的值是多少&a指向a思考题2:怎么打印指针值? %p思考题3:“指针变量有指针”与“指针变量保存指针”两种说法是相同的意思吗?思考题4:指针的指针是什么意思?指针变量的指针
7 变量给变量赋值的本质
把变量存储的内容取出粘贴到另一个变量中.
8 指针偏移量、指针强转、大小端
1 指针的偏移量:
指针指多远, 与指针类型相关
2 指针强转.
#include<stdio.h>int main() { int x = 233445;//233445的被码: short *p1 = (short*)&x; short *p2 = p1; printf("%d\n", *p1);//-28699 return 0;}
3 大小端
从指针强转的代码执行结果,分析,该系统是小端存储系统。
9 形参类型为指针变量
#include<stdio.h>void f2(int *p) { *p = 200;}int main() { int x = 23; f2(&x); printf("x=%d\n", x);//x=200 return 0;} 10 指针常量与常量指针
1.指针常量:指针变量自身是常量,指针变量的值不可改变, 必需在定义的时候就赋值 语法: 类型 * const 变量 = 值;int a = 100;int b = 200;int * const p = &a; p = &b;//错,因为改变了指针变量自身的值*p = 300;//对,因为没有改变指针变量自身的值(提示: p是常量, p是名字,可修改) 2.常量指针:(不能通过指针修改指向的变量的值)常量指针是这样声明的:(下面种方式都是一样的意思, p是名字,可修改) 1)const 类型 *p; 2)类型 const *p;int * const p1 = &a; 指针常量int const * p2 = &a; 常量指针const int * p3 = &a; 常量指针示例: int a=10,b=20; const int *p = &a;//常量指针 **(****指针常量****int** ***** **const** **p = &a; )** *p = 8; //错,因为不能通过指针修改指向的变量的值 p = &b;//对3.总结: int a=10,b=20; int * const p = &a; // p不可改 指针常量 const int *p = &a;// *p不可改 常量指针 4. 指针常量又常量指针const int * const p = &a; // *p 和 p均不可改变了
11 指针运算只3种
1.指针加整数n与减整数n指: 指针向前或向后移动n个偏移量。
(提示:指针经常自加与自减,如 p++,++p,--p,p--)
2.指针减指针:得到多少个偏移量(提示:没有指针加指针的运算)
3.指针的比较运算:>, >=, <, <=, ==, != 返回值为0或1;
练习:打印出int型变量所占用的每1个和第3个字节的地址
12 指针的[n]运算
假设指向变量的指针变量p,则有
*(p+n) = p[n]
#include<stdio.h>void main() { int age = 233445; int *page = &age; char *pc = (char*)page; printf("%p,%p,%p,%p\n", pc,pc+1,pc+2,pc+3); short *p = (short*)page; printf("p[0]=%d, *(p+0)=%d\n", p[0],*(p+0)); printf("p[1]=%d, *(p+1)=%d\n", p[1],*(p+1));}执行结果为0028FEA0,0028FEA1,0028FEA2,0028FEA3p[0]=-28699, *(p+0)=-28699p[1]=3, *(p+1)=3 
十五 指针与一维数组
1 理解一维数组名字
(1) 是一个指针常量, 所以一维数组名代表一个值,该值是指针,该值还不能修改.
(2) 类型由一维数组类型决定,比如,int型一维数组,则一维数组名字就是int * 类型
(3)指向数组中第一个元素.偏移量就是一个元素字节数
(4)数组元素的引用
根据指针的 加减运算和指针的[n]运算,可知引用数组元素可采用下面两种方式:
数组名[下标] ==*(数组名+下标)
示例:

(5)数组名可以为指针变量赋值
思考题1?
1. 下面示例中a[3]的值是多少?
2. 代码p++;对码?
3. 实计上 a[i] 的底层就是 *(a+i)的说话对吗?
4. 能否使用int *p = a;
5. 写出多种遍历一维数组的代码?
6. 下面int *p=a可否改为 int *p=&a[0]?
a-->a[0]
a===&a[0]
int *p = a
int *p = &a[0];
void f1( ) {
int a[3] = {10, 30, 20};
int length = sizeof(a)/sizeof(a[0]);
int i;
for (i = 0; i<length; i++) {
printf("a[%d]=%d\n", i, a[i]);
printf("a[%d]=%d\n", i, *(a+i));
}
printf("----------------------------\n");
int *p = a;
for (i = 0; i<length; i++) {
printf("a[%d]=%d\n", i, p[i]);
printf("a[%d]=%d\n", i, *(p+i));
}
printf("----------------------------");
for (i=0; i<length; i++) {
printf("a[%d]=%d\n", i, *p++);//不能写成a++
}
} (6) 把数组名理解为整个数组
#include <stdio.h>
void main() {
int a[] = {1,2,3};
printf("%p\n", a);
printf("%p\n", &a);
printf("%p\n", a+1);
printf("%p\n", &a+1);
} (7)把数组名当作正个数组
<1>sizeof(a) 得到数组的总字节数
<2>&a 得到指向数组的指针,指针的偏移量等数组的总字节数
十六 字符串
1 字符串和字符串长度的概念
(1)字符串
字符串使用双引号创建,结尾自动补字符'\0’,如“very good"

(2)字符串长度:
一般所说的字符串的长度不包括'\0', 如“very good"的长度为9
2 字符串引用
字符串的引用要靠 char *型指针来指向字符串中的首字符.
char *p = "very good"; p指向字符串中的第一个字符
3 字符串常量池
无论在函数中还是全局区创建字符串, 都是在数据段的字符串常量池中创建的, 所以有两个特点:
(1)多次创建相同内容的字符串,只有第1次是创建了新的.其余都是使用第1次的.
void main() { char *s1 = "teacher";//第一次创建字符串"teacher" char *s2 = "teacher";//第二次不会创建字符串"teacher" //s1和s2的值是相同的,因为它们都指向同一个字符串-"teacher" printf("%p\n", s1); //00404000 printf("%p\n", s2); //00404000} (2)字符串一但创建,其中的字符就不能改
void main() { char *s1 = "teacher"; s[2] = 'x';} 4 %s与字符串输出
(1)%s格式打印字符指针机制:
从字符指针指向的第一个字节开始,一个字节一个字节的抠取出字节中的数据,把数据以字符形式输出. 抠出的数据为'\0'时停止输出和抠取.
#include <stdio.h>void main() { char a[10] = {'L','O','V','E','I', 'O', 'S', '\0', 'X', 'Y'}; printf("->%s\n", a);//->LOVEIOS printf("->%s\n", a+4);//->IOS printf("->%s\n", a+7);//-> //开发中不能写这样的代码,值是不可确定的,操作了不该操作的 printf("->%s\n", a+8);//->XYⅤI螅 内存} (2)%s格式打印字符串
void main() { char *s = "teacher"; printf("%s\n", s); char *s2 = "teac\0her"; printf("%s\n", s2); printf("%s\n","good");} 5 获取字符串长度
(1) 库函数strlen
原形: long strlen(char *)
从参数字符指针指向的字节开始一个一个的计算,当字节为'\0'时,计算结束.
void main() { char a[10] = {'L','O','V','E','I', 'O', 'S', '\0', 'X', 'Y'}; long len = strlen(a); printf("%d\n", len);//7} (2) strlen取字符串长度
void main() { char *s1 = "teacher"; printf("%d\n", strlen(s1));//7 char *s2 = "teac\0her"; printf("%d\n", strlen(s2));//4 printf("%d\n", strlen("good"));//4 printf("%d\n", strlen(s1+2));//5} 6 字符串初始化数组
(1)数组长度已经指定, 字符串长度小于数组指定的长度:
<1>使用字符串中字符为数组元素初始化
<2>没有被初始化数组元素得到默认值'\0'
<3>本质上并没有真正创建字符串
#include <stdio.h>void main() { //创建了名为a的数组,存储数据为['a','b','\0','\0'] char a[4] = "ab";//并不会创建字符中"ab" int i; for(i=0; i<4; i++) { printf("%c", a[i]); } printf("结束\n"); for(i=0; i<4; i++) { printf("%d", a[i]); } printf("结束\n"); char *s = "ab";//创建了字符串"ab" printf("%d", a==s);//0 }/*执行结果为:ab 结束979800结束 0*/ (2)数组长度已经指定, 字符串长度等于数组指定的长度:
<1>使用字符串中字符为数组元素初始化
<2>本质上并没有真正创建字符串
#include <stdio.h>void main() { //创建了名为a的数组,存储数据为['a','b','c','c'] char a[4] = "abcd";//并不会创建字符中"abcd" int i; for(i=0; i<4; i++) { printf("%c", a[i]); }} (3)数组长度已经指定, 字符串长度大于数组指定的长度:
<1>使用字符串中字符为数组元素初始化, 字符串尾部多余的字符无用.
<2>本质上并没有真正创建字符串
#include <stdio.h>void main() { //创建了名为a的数组,存储数据为['a','b','c','c'],后面的e和f是无效的. char a[4] = "abcdef";//并不会创建字符中"abcdef", int i; for(i=0; i<4; i++) { printf("%c", a[i]); } printf("%s", a);//abcd后面字符不可控 } (4)数组长度不指定, 字符串长度任意.
<1>数组长度为"字符串长度+1".
<2>使用字符串中字符为数组元素初始化
<3>本质上并没有真正创建字符串
#include <stdio.h>void main() { //创建了名为a的数组,存储数据为['a','b','c','d','e','f','\0'] char a[] = "abcdef";//并不会创建字符"abcdef", printf("%d\n",sizeof(a)/sizeof(a[0]));//7} 7 汉字字符
(1)汉字字符解决思路
因为字符占一个字节,只能存储ascii表中的字符,所以1个汉字没办法存储在一个字节中.解决方式,把汉字存储在两个字节中.
char c = '我';//编译直接报错
#include <stdio.h>
void main() {
char a[7] = "hi你好";
printf("%s\n", a);//hi你好
char *p = "hi,这是我们的世界";
printf("%s\n", p);
//一个汉字长度为2
printf("%d\n", strlen(p));//17
} (2)思考,下面代码执行结果是什么?
char a[6] = "hi!你好";
printf("%s\n", a); 8 字符串系统库函数
连接strcat
格式:strcat(char数组, char *)
功能:
返值:返回字符数组1的首地址
说明:字符数组1必须足够大
连接前,两串均以‘\0’结束;连接后,串1的‘\0’取消,
新串最后加‘\0’ 拷贝strcpy格式:strcpy(字符数组, char *)功能: 返值:返回字符数组1的首地址说明:字符数组1必须足够大 拷贝时‘\0’一同拷贝
比较strcmp格式:strcmp(char *, char *)功能: 比较规则:对两串从左向右逐个字符比较(ASCII码),直到遇到不同字符或‘\0’为止返值:返回int型整数a. 若字符串1< 字符串2, 返回负整数b. 若字符串1> 字符串2, 返回正整数c. 若字符串1== 字符串2, 返回零
长度strlen格式:strlen(char *)功能:返值:返回长度
十七 指针与二维数组
1 理解二维数组 int a[2][3]

2 计算数组长度
sizeof(a)把a当作整个数组,得到整个数组的字节数sizeof(a[0])把a[0]当作整个数组,得到整个数组的字节数第一维长度计算: sizeof(a)/sizeof(a[0])第二维长度计算: sizeof(a[0]/sizeof(a[0][0]))
3 练习
a当作值能给什么样的变量赋值? a-->a[0]
a[0]当作值能给什么样的变量赋值?a[0]-->a[0][0]
int *xp = a[0]--->&a[0][0]
&a当作值能给什么新的变量赋值?
作业:你能写出多少种遍历二维数组的方法?
#include<stdio.h>
void fun1() {
int a[2][3]={1,2,3,4,5,6};
int len1 = sizeof(a)/sizeof(a[0]);
int len2 = sizeof(a[0])/sizeof(a[0][0]);
int i;
int j;
for(i=0; i<len1; i++) {
for(j=0; j<len2; j++) {
printf("a[%d][%d]=%d\n",i,j,a[i][j]);
}
}
}
void fun2() {
int a[2][3]={1,2,3,4,5,6};
int len1 = sizeof(a)/sizeof(a[0]);
int len2 = sizeof(a[0])/sizeof(a[0][0]);
int len = len1*len2;
int *p = &a[0][0];
int i;
for(i=0; i<len; i++) {
//printf("%d\n", *(p+i));
printf("%d\n", p[i]);
}
}
int main() {
fun2();
return 0;
} 扩展: 分析三维数组?思考题?a当作值能给什么样的变量赋值?a[0]当作值能给什么样的变量赋值?&a当作值能给什么新的变量赋值?作业:你能写出多少种遍历二维数组的方法?扩展: 分析三维数组?
十八 多级指针
指针变量也占用内存,所以自身也有地址(指针)
int main( ) { int a =1133; int *p1 = &a; int **p2 = &p1; //二级指针 int ***p3 = &p2;//三级指针 printf("%d\n", *p1);//1133 printf("%d\n", **p2);//1133 printf("%d\n", ***p3);//1133 return 0;} 十九 static
修饰全局变量
修饰函数
修饰局部变量
1 static修饰的全局变量和static修饰的函数, 不能在其它文件中被使用,只能被当前文件使用。
2 static修饰的的局部变量,该变量创建在数据段中。第一次调用函数时,创建static修饰的局部变量,第二次调用同一个函数时,static修饰的局部变量的创建语句被跳过。作用域是当前函数.
二十 堆内存开辟 malloc free
char *p = (char *)malloc(100);
1.len是整数,在堆中开辟len个字节的内存
2.返回值为内存的首字节地址/指针,为void *类型的地址/指针, 一般都要强转指针类型。
3.开辟的内存属于堆内存
4.在堆中开辟的内存都需要使用free(void *)释放, 开辟多少就会释放多少。
5.堆中开辟内存时,只能得到内存的指针,而堆内存没有名字
6.如果开辟失败返回值为NULL
二十一 四大内存总结
1栈
函数执行时创建,函数执行结束后自动销毁。用于存储局部变量(static变量例外)。有名字
2 数据段
创建内存之后,就不会被销毁了。用于存储全局变量和static修饰的局部变量,字符串。有名字
3 堆
malloc free
创建内存之后不会被销毁,但是可以使用free手动销毁, 用于存储malloc开辟的空间。无名字
4 代码段
运行程序时,硬盘中代码会被考备到内存中,该内存就是代码段内存。代码段中的内存内容不能被修改。
运行区,运行内存: 就是指栈或堆。

二十二 函数指针
1 函数指针的概念
程序运行过程中,函数会被加载到代码段内存, 有内存当然就有指针,函数所占的内存指针就是函数指针.
2 函数名的指针概念
函数名就是一个指向函数的指针常量
3 创建函数指针变量,并使用函数指针变量调用函数
#include <stdio.h>
extern void f1();
extern void f2();
extern int add(int, int);
extern int jian(int, int);
void main() {
void (*p1)()=f1;
p1();
p1 = f2;
p1();
int (*p2)(int, int) = add;
int r = p2(1, 2);
printf("r=%d\n", r);
p2 = jian;
r = p2(3,4);
printf("r=%d\n", r);
}
void f1() {
printf("f1\n");
}
void f2() {
printf("f2\n");
}
int add(int a, int b) {
return a+b;
}
int jian(int a, int b) {
return a-b;
} 4 typedef定义函数指针类型
#include<stdio.h>
int add(int a, int b) {
return a+b;
}
void main() {
typedef int (*f)(int a, int b);
f myadd = add;
int r = add(1,2);
printf("%d\n", r);
} 5 函数回调
函数参数可以是任意类型, 使用函数指针可以实现函数回调.
int add(int a, int b) {
return a+b;
}
int jian(int a, int b) {
return a-b;
}
int test(int (*p)(int, int) , int a, int b) {
int r = p(a, b);
return r;
}
int main() {
int r1 = test(add, 10, 20);
int r2 = test(jian, 10, 20);
printf("r1=%d\nr2=%d\n", r1, r2);
return 0;
}
二十三 指针函数
1 基础
(1)函数返回值可以是任意类型,包括自定义类型等.....
(2)指针函数:返回类型为指针
(1)堆中内存指针(malloc)
(2)数据段中内存指针(全局变量、static局部变量)
(3)代码段中内存指针(函数指针)
(3)思考题:
为什么不要在开发中返回局部变量指针
2 返回函数指针
函数返回可以函数函数指针, 此时必须要把返回的函数指针类型使用typedef进行定义后使用.
int add(int a, int b) {
return a+b;
}
int jian(int a, int b) {
return a-b;
}
//定义函数指针的类型名为f
typedef int (*f)(int, int);
f get(int flag) {//不能把f改为 int(*)(int,int),也不能改为int (*f)(int,int);
if(flag == 1)
{
return jian;
}else {
return add;
}
}
void main() {
int r1 = get(1)(1,2);
int r2 = get(0)(1,2);
printf("r1=%d\nr2=%d\n", r1, r2);
} 二十四 复杂混合类型变量万能判断法
口决: 右看左看
1.概念:
(1)数组指针:数组的指针(指向一维数组的指针)
示例:

(2)指针数组:数组中每个元素的类型都是指针类型
(无素类型为指针的一维数组)
示例:

#include<stdio.h>int main() { int *p[3]; int x1 = 100; int x2 = 200; int x3 = 300; p[0] = &x1; p[1] = &x2; p[2] = &x3; int i; for(i=0; i<3; i++) { printf("*(p[%d])=%d\n",i,*(p[i])); } return 0;}//*(p[0])=100//*(p[1])=200//*(p[2])=3002.类型判断法
3.作业:为下面的变量赋值。
int a; // a=100; int *a; // int x = 100; a = &x; int **a; // int x = 100; int *p1 = &x; a=&p1;

int **a[2];

int (*a)[2];

void (*p)(int, int);
void fff(int, int) {
}
p = fff;
int (*p)();
int xx() {
}
p = xx; 二十五 函数深入
1 函数回调
使用函数指针当作函数的参数实现
2 把字符串传递给函数参数
参数声明有3种方式, 本质上都是创建了一个字符指针, 参数中char p[]或char p[num]就是char *p;其中num运行无意义
void f1(char *p, int len);
void f2(char p[ ]);//相当于char *p
void f3(char p[10]);//相当于char *p, 其中10无意义
3 在实战中建议
如果要求接收真实字符串,使用 void f1(char *p, int len)
如果要求接收长度不定的字符数组, 使用 void f2(char p[], int len);
如果要求接收固定长度的字符串数, 使用void f3(char p[10]);
#include<stdio.h>
//1接收真正的字符串
void f1(char *p) {
char c = *(p+1);//p[1];
printf("%c\n", c);
}
void f1test() {
char *p = "Hello";
f1(p);
f1("World");
}
//2 接收字符数组,字符个数可随意
void f2(char p[], int len) {//len是数组长度
//不能用该方式得到数组的长度
int x = sizeof(p)/sizeof(p[0]);//4/1==4
printf("len=%d\n", len);//4
//遍历
int i;
for(i=0; i<len; i++) {
printf("%c\n", p[i]);
}
}
void f2test() {
char a[] = "Hello"; //char a[6] = {'H','e','l','l','o','\0'};
f2(a, sizeof(a)/sizeof(a[0]));
char a2[] = {'H','e','l','l'};
f2(a2, sizeof(a2)/sizeof(a2[0]));
}
//3 接收字符数组,字符数组长度唯一
void f3(char p[5]) {
int len = 5;
int i;
for(i=0; i<len; i++) {
printf("%c\n", p[i]);
}
}
void f3test() {
char a[] = {'H','e','l','l','E'};
f3(a);
}
void main() {
// f1test();
// f2test();
f3test();
}
3 把数组传递给函数参数
1, 数把组传递给函数, 要传递包含数组的首元素地址和数组长度.
2, 形参 int p[]或 int p[num]就是int *p,其中num运行无意义
3, 形以中int p[][3]或int p[num][3]就是int (*p)[3], p的偏移量是3*int,即表示一个长度为3的int数组.其中3要求传递的数组的第二维也是3, 其中的num运行无意义.
#include <stdio.h>
//p是什么类型:指向长度为3的一维int数组
//void f1(int (*p)[3], int len1, int len2)
void f1(int p[][3],int len1, int len2) {
int i,j;
for(i=0; i<len1; i++) {
for(j=0; j<len2; j++) {
printf("p[%d][%d]=%d\n", i,j,p[i][j]);
}
}
}
//int (*p)[3]
void f2(int p[][3],int len1) {
//*p就是a
int i;
for(i=0; i<3; i++) {
printf("%d\n", (*p)[i]);
}
}
//int *p;
void f3(int p[], int len1, int len2) {
int i;
for(i=0; i<len1*len2; i++) {
//printf("%d,%d\n",p[i],*(p+i));
printf("%d\n",*p++);
}
}
void main() {
// int a[2][3] = {1,2,3,4,5,6,7};
// f1(a, 2, 3);
// int a[3] = {1,2,3};//a指向a[0]
// f2(&a, 3);//&a指向a
int a[2][3] = {1,2,3,4,5,6,7};
f3(&a[0][0],2,3);
f3(a[0],2,3);
} #include<stdio.h>
//void f1(int *p, len)
void f1(int p[], int len) {
int i;
for(i=0; i<len; i++) {
printf("%d,%d\n", p[i],*(p+i));
}
}
//void f2(int int(*p)[3], int len1, int len2)
void f2(int p[][3], int len1, int len2) {
int i,j;
for(i=0; i<len1; i++) {
for(j=0; j<len2; j++) {
printf("%d,%d,%d\n", p[i][j], (*(p+i))[j], *(*(p+i)+j));
}
}
}
void f1test() {
int a[] = {1,2,3,4,5};
f1(a, sizeof(a)/sizeof(a[0]));
}
void f2test() {
int a[2][3] = {1,2,3,4,5,6};
f2(a, sizeof(a)/sizeof(a[0]), sizeof(a[0])/sizeof(a[0][0]));
}
void main() {
//f1test();
f2test();
}
查看7道真题和解析