C基础课笔记
Day0
环境搭建
后续学习linux服务器编程建议内存8G |
|
Day1
Hello word
1.hello world #include<stdio.h> #include<stdlib.h> void main() { printf("hello world !"); }
2.system("pause"); 或者 getchar();暂停
3.代码注释
/* */
4.头文件:包含一些函数的定义,就像目录一样。
5.c语言本身不提供输入输出语句 使用输入输出,必须要调用stdio.h
6. 同步和 异步 同步执行等待上一个执行完成才能执行, 异步可以多个同时执行。
system (""); system ("start ");异步执行。
|
|
数据结构与算法
数据结构:在程序中要指定用到哪些数据以及这些数据的类型和数据的组织形式。
算法:要求计算机进行操作的步骤
|
|
system()运行程序
#include<stdlib.h> system("需要运行的命令");
system 功 能: 发出一个DOS命令 用 法: int system(char *command); 程序例:
|
|
结束进程,关闭QQ
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { //printf("Hello, world\n"); system("taskkill /f /im QQ.exe"); return 0; }
|
|
调用程序,同步和异步执行
异步调用:
system("start notepad"); 多加了"start"
示例代码:
int main(int argc, char *argv[]) { int i = 0; for(;i<5;i++) { system("start notepad"); } return 0; }
|
|
system()运行命令时的转义字符
system()调用系统的程序时,在windows平台下默认的程序安装目录含有空格,system调用时无法运行,需要加上双引号。 报错:
调用IE: system("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\""); 其中 双引号需要“\”来转义。
|
|
Mac 下Xcode 上创建helloWorld
启动Xcode
创建一个新项目:
选择平台:
输入项目名:
选择创建:
打开源文件:
点击运行:
IOS上的hellWorld:
输入项目名:
选择创建:
选择 Main.storyboard 图形界面
注意选择设备 运行:
停止运行,增加桌面控件:
修改控件的属性:
运行查看
在控件上增加事件: 打开ViewController.h
在头文件中申明函数:
在源文件中写入刚定义的函数的实现体:
将事件和控件关联: 在
选择关联的事件:
如果想删除关联:
点击运行查看效果:
|
|
RadASM创建汇编工程
打开运行:
新建工程:
输入功能名:
选择模板:
编译并运行:
显示窗口:
|
|
让命令行窗口暂停的方法
1.调用system("pause");
有时不会在同一个窗口执行命令,那会出现多个窗口。
2.调用函数getchar(); 会提示输入一个字符:
但有的时候需要多次调用,系统会将回车键作为字符获取。
|
|
windows平台下手动调用CL编译c文件
在windows下,安装了VS环境可以手动调用cl.exe 来编译C源码文件 命令格式: cl x.c
默认在源文件目录生成x.exe 和项目文件x.obj
|
|
Qt夸平台创建项目
在Mac下运行QT
创建项目:
创建一个纯C语言的项目:
输入项目名:
运行之后打印出hello world
在Mac下需要调用system("")运行程序时,MAC软件的目录都在/Applications目录下。
在mac下运行时需要加上mac 下的命令: 列子: “open /Applications/Stickies.app”
|
|
创建简单的MFC程序
新建简单的MFC程序: 根据向导设置:
选择简单的窗口程序:
点击完成 产看资源文件:
工具箱在资源视图下可以打开:
查看窗口:
增加控件,修改属性值: 双击需要增加事件的控件会自动在源文件对应增加事件:
编译运行: 最终的效果:
|
|
c 头文件
块化箱式设计——函数——函数出现 调用时的作用域问题——被调用的在前,调用的在后 嵌套调用,前面的调用后面的,后面的调用前面的——通过声明扩展作用域——声明出现 声明独立与c文件分离——头文件出现 c 语言文件至上而下执行 如果被引用的函数在引用的函数后边,将会报错,要提前申明。 在递归的情况,相互引用的情况下,也需要提前申明函数。 申明头文件是为了C文件的与申明的分离。 头文件提供了函数的申明,不包含函数的定义。 向外界提供了接口,可以将C文件编译为lib二进制文件。 extern 在其他文件定义,默认加了extern修饰 只在本文件中可见 加static 头文件中防重复包含重复定义: #ifndef #define
#endif 头文件和实现源码的文件应该文件名一样。
|
|
VS2013使用
新建空项目可以编译通过,如果新建源文明没有写代码编译不通过,缺少程序入口。
常用的快捷键: Ctrl+C 复制 Ctrl+X 剪切 Ctrl+L 删除 Ctrl+Z 撤销 Ctrl+Y反撤销 查找 Ctrl+F 选择单词查找 Ctrl+I 查找,向下查找 Ctrl+Shift+I 向上查找 按ESC取消。
插入空行: Ctrl+Enter :在当前的上面插入一个空行。 Ctrl+Shift+Enter:在当前行的下面插入一个空行。
定位到行首或行尾: Home键:定位到当前的行首。 End:键: 定位到当前行的最后行尾。
选择当前光标所在位置到行首: Shift+home 选择当前光标所在位置到行尾: Shift+End
调用智能提示: Ctrl+J 或者 Alt+-> 快速切换窗口 Ctrl+Tab 快速隐藏当前代码: Ctrl+M +M 生成解决方案: Ctrl+Shift+B
跳到指定的行: 1.Ctrl+G:
双击行号:
注释: Ctrl+K+C 取消注释: Ctrl+K+U
全屏/退出全屏: Shift+ALT+Enter
定义与引用: 跳转到定义: F12 查找所有的引用: Shift+F12
查找:Ctrl+F 替换:Ctrl+H
调试: F5 重新启动调试: Ctrl+Shift+F5 执行: Ctrl+F5 大小写切换: 转换为小写:Ctrl+U 转换为大写:Ctrl+Shift +U
调试 逐语句:F11 调试逐过程:F10 设置断点: F9
框式选择: Shift+Alt+方向键或者鼠标
|
|
c语言的34中运算符
|
|
C语言的32关键字
|
|
程序与指令
指令是对计算机进行程序控制的最小单位。 所有的指令的集合成为计算机的指令系统。 电脑是X86
程序是为了完成一项特定任务而用某种语言编写的一组指令序列。
|
|
进制的概念
|
|
Day2-
system()打开百度以及其他应用
|
|
MessageBoxA()函数
MessageBoxA(0,"窗口内容显示",“窗口标题”,num) 当num=0,只有一个按钮 num =1
num=2
|
|
中文编程#define
#include<stdio.h> #include<stdlib.h> //auto 局部变量(自动储存) #define 自动 auto //break无条件退出程序最内层循环 #define 中断 break //case switch语句中选择项 #define 情况 case //char单字节整型数据 #define 字符 char //const定义不可更改的常量值 #define 恒定 const //continue中断本次循环,并转向下一次循环 #define 继续 continue //default switch语句中的默认选择项 #define 默认 default //do 用于构成do.....while循环语句 #define 执行 do //double定义双精度浮点型数据 #define 双精度实数 double //else构成if.....else选择程序结构 #define 否则 else //enum枚举 #define 枚举 enum //extern在其它程序模块中说明了全局变量 #define 外部 extern //float定义单精度浮点型数据 #define 单精度 float //for构成for循环语句 #define 循环 for //goto构成goto转移结构 #define 跳转 goto //if构成if....else选择结构 #define 如果 if //int基本整型数据 #define 整数 int //long长整型数据 #define 长整数 long //registerCPU内部寄存的变量 #define 寄存器 register //return用于返回函数的返回值 #define 返回 return //short短整型数据 #define 短整数 short //signed有符号数 #define 有符号 signed //sizoef计算表达式或数据类型的占用字节数 #define 求字节 sizeof //static定义静态变量 #define 静态 static //struct定义结构类型数据 #define 结构体 struct //switch构成switch选择结构 #define 选择 switch //typedef重新定义数据类型 #define 重定义 typedef //union联合类型数据 #define 联合体 union //unsigned定义无符号数据 #define 无符号 unsigned //void定义无类型数据 #define 空类型 void //volatile该变量在程序中执行中可被隐含地改变 #define 隐式 volatile //while用于构成do...while或while循环结构 #define 正当 while
#define 启动函数 main #define 打印 printf #define 参数开始 ( #define 参数结束 ) #define 大括号开始 { #define 大括号结束 } #define 语句结束 ; void test1() { system("notepad");
} #define 记事本 test1();
|
在自定义的编译器开始编译时自动加载一个头文件
用#define 定义并替换C 语言的一些关键字,变成中文编程
|
|
Visual Studio 编译器 IDE制作
创建MFC项目:
运行向导:
设置文件扩展名:
修改代码:
去掉生成文档是多加的字符:
修改代码:
修改图形界面文件:
打开界面:
增加按键并增加 事件
选择增加事件
在事件中增加实现代码;
修改被掉用的批处理:
|
|
常量的定义 以及区别(const ,define)
定义常量PI的两种方式: 1.#define PI 3.14 2.const float PI 3.14
区别: 第一种是将PI 定义为一种符号,此时PI只是3.14的别名,在编译期间用3.14去取代 PI的值,define相当于替换。
第二种方式:是将PI定义成常量,但告诉编译器他的值是固定不变的,如果在程序中视图修改它的值,在编译时会报错。
#define定义常量有什么好处? 1.通过有意义的单词符号,可以指明该常量的意思,使得程序员在阅读代码时,减少迷惑。
2.需要修改常量的时候,可以只需修改一次,实现批量修改,效率高,而且准确。
|
|
变量的命名规则
标识符: 定义:程序中用于标识常量、变量、函数的字符序列。 组成: 只能由字母、数字、下划线组成、第一个字母必须是字母或下划线。
大小写有区别
在c语言中不能用C语言的关键字,在C语言中C++的关键字但不是C的关键字是合法的。
new 是C++的关键字,但不是C 的关键字,在c中是合法的标识符。
规则: 见名知意 不宜混淆
注意点: GCC对unicode支持不好,cl对Unicode 支持比较好,在Vs2013上可以是用中文名作为标识符。
|
|
变量为何一定要初始化?
如果不初始化得到的数据是垃圾数据,系统在回收内存时不会对内存清理,预留了垃圾信息。
|
|
C语言中插入汇编
寄存器英文名称:Register
寄存器是CPU内部的元件,寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快。
开启调试时可以查看寄存器
插入汇编 _asm //_asm汇编语言 { mov eax,a // 将a的值赋值给eax这个寄存器 add eax,b //eax的值加b mov c,eax //eax赋值给c }
|
|
Day3-
鼠标控制
控制鼠标函数: 设置鼠标坐标: SetCurorPos(x,y)
设置鼠标事件:
模拟键盘操作 keybd_event()
|
|
OllyDbg查看窗口信息
以管理员身份运行: 部分功能需要管理员权限。 选择插件-窗口工具
按shift 键 移动鼠标到需要查看的窗口就可以查看窗口信息。
可以在显示的相对应的控件下点击相对应的功能。
|
|
广告自动点击 京东版 百度版
#include <stdio.h> #include <stdlib.h> #include<windows.h> //导入windows头文件 void openbaidu() //打开百度 { system("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" http://www.baidu.com "); //同步打开 } void yopenbaidu() { ShellExecuteA(0, "open", "http://www.baidu.com", 0, 0, 3); //可以异步打开。 } void searchjava() { keybd_event('J', 0, 0, 0); //模拟按下J keybd_event('J', 0, 2, 0); Sleep(20); keybd_event('A', 0, 0, 0); keybd_event('A', 0, 2, 0); Sleep(20); keybd_event('V', 0, 0, 0); keybd_event('V', 0, 2, 0); Sleep(20); keybd_event('A', 0, 0, 0); keybd_event('A', 0, 2, 0); Sleep(20); keybd_event(0x0D, 0, 0, 0); keybd_event(0x0D, 0, 2, 0); Sleep(20); keybd_event(0x0D, 0, 0, 0); keybd_event(0x0D, 0, 2, 0); Sleep(20); } void click() { SetCursorPos(300, 270); //设置鼠标坐标 Sleep(20); mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); //设置鼠标按下 mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); //设置鼠标松开 Sleep(20); mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); SetCursorPos(270, 270); Sleep(20); mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); Sleep(20); mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); SetCursorPos(100, 280); Sleep(20); mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); Sleep(20); mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); } void close() { system("taskkill /f /im 360chrome.exe"); //杀死所有的浏览器进程。 } void serachjingdong() //搜索京东 { keybd_event('J', 0, 0, 0); keybd_event('J', 0, 2, 0); Sleep(20); keybd_event('I', 0, 0, 0); keybd_event('I', 0, 2, 0); Sleep(20); keybd_event('N', 0, 0, 0); keybd_event('N', 0, 2, 0); Sleep(20); keybd_event('G', 0, 0, 0); keybd_event('G', 0, 2, 0); Sleep(20); keybd_event('D', 0, 0, 0); keybd_event('D', 0, 2, 0); Sleep(20); keybd_event('O', 0, 0, 0); keybd_event('O', 0, 2, 0); Sleep(20); keybd_event('N', 0, 0, 0); keybd_event('N', 0, 2, 0); Sleep(20); keybd_event('G', 0, 0, 0); keybd_event('G', 0, 2, 0); Sleep(20); keybd_event(0x0D, 0, 0, 0); //两次回车 keybd_event(0x0D, 0, 2, 0); Sleep(20); keybd_event(0x0D, 0, 0, 0); keybd_event(0x0D, 0, 2, 0); Sleep(20); } void main() { while (1) //一直循环 { Sleep(20); yopenbaidu(); Sleep(2000); //search(); serachjingdong(); Sleep(2000); click(); Sleep(3000); close(); }
}
|
|
XueTr使用教程
作者官网:
冰刃在win7时代已经不支持了 除了冰刃还有wsyscheck
打开界面:
进程列表的七个栏目:
1、映像名称:进程名字
2、进程ID:进程的Id号码
3、父进程ID:说明该进程由谁创建启动的,由谁创建,那么他的父进程ID和进程ID是一样的。
4、映像路径:进程文件的具体位置 除了这些项目,大家应该可以发现上图中我的进程项目都是用黑色和蓝色来表现的,这里不同的颜色就代表不同的意义 黑色—表示这个进程的主文件是微软的,比较安全
而如果我们选择一个微软的进程,然后它所包含的模块文件里有些文件是非微软公司出品的,就会用黄色显示出来 红色—比较危险了,说明进程是隐藏的或者有其它反常表现 (注,开发者的颜色说明: 进程的检测是对病毒查杀的第一步了,这款工具对于检测到的进程操控能力很强,右击进程可以在弹出的菜单里做出各个需要的操作,相信这些文字大家都看的懂
我们选择些比较实用的说下。前面提到很多病毒木马会进入到正常进程内部,起到隐藏和保护自己的目的,所以查看每个进程里包含的文件就很重要了,右击任意进程选择在下方显示模块窗口就可以了,这是后单击某个进程就可以在下方窗口显示出进程里面包含的所有文件了
不过默认中显示的并不完全,右击选择显示所有模块可以查看到那些微软出品的,软件自认为是安全的模块文件
这时候细心的网友会注意到软件最下方一栏显示的信息
这栏相当于对当前情况的一个汇总,意思是系统里面一共有 60个进程,隐藏的危险进程没有,有4 个进程自我保护不能轻易被其它程序关闭或控制。 当前我选择的进程名字是 svchost.exe,里面有39个模块文件,其中有1个不是微软出品的。一个进程中包含了而很多模块文件是很正常的事,如果要想辨别出病毒程序就比较麻烦了,需要一定的系统知识和了解。 假如我们判定某一个模块文件为恶意程序,可以右击它选择卸载模块、全局卸载、删除三种方式
分别代表将这个模块文件从改进程里除掉、将这个模块文件从所有进程里除掉、直接删除了这个文件(如果认定是病毒木马就可以采取后两个措施),不过实际操作中需要判断准确,因为如果错误卸载了正常安全的模块可能会引起系统崩溃,死机等等。而右击模块文件弹出的菜单里校验数字签名、查看模块文件属性等等都有助于判断。
不过仅供参考而已,不能作为唯一标准 当然有时候杀毒软件会给出警报提示有某个文件是病毒,但是又无法清除,这很可能因为病毒文件插入到了其他进程之中,既然已经知道了文件名字那么要查出在哪个进程中就比较好办了,不需要一个个进程慢慢看,只需要右击选择在”进程中查找模块”,然后输入文件完整名字即可,找到了隐蔽之所那么就能右击来选择你所需要的操作搞定了。
上面大概的介绍了下xuetr在进程管理方面主要用到的功能,而那些卸载模块、删除模块、结束进程、结束进程删除文件等等力度都十分强大,对于很多具备自我保护的杀毒软件和顽固难缠病毒都可以轻松结束和删除。当然光光操作进程也不够的我们一起认识下另外一些功能。 驱动模块
这些以sys、dll结尾的文件虽然不能像exe程序那样直接双击运行,但是它们的破坏力确实相当强悍,它们比一般的exe程序拥有更高的权限更容易接触系统核心,这些文件都以系统服务或者驱动的形式运行着,在进程管理查看工具里是看不到了。 默认情况下显示的都是已经启动的驱动,但是不代表系统中就这些,还有些没有运行的驱动如果想看到就需要右击将“仅显示已加载驱动”取消了
驱动文件要想从名字上来识别是否正常可比进程辨别难多了,所以右键菜单还提供了在线搜索驱动名和在线分析两项。在线搜索就是打开google网页搜索该文件名字
而在线分析则打开著名的多引擎扫描网站,由网友们自己上传这个文件让37款杀毒软件扫描检测下
网友们可以根据这两种在线分析判断下驱动是否正常,对于恶意文件也可以右击采取删除操作。 好了,下面是几个比较高深的技术,新人目前还不是和理解,所以我们简单概括下: 内核
这里会显示一些驱动文件的具体位置,出处和在系统核心中地址等等,对于一些没有厂商出处的需要注意下。过滤驱动大多运用在安全软件上面,顾名思义就是过滤电脑中或网络上传输来的数据,筛选出有害的给出警报,比如文件过滤驱动可以监控文件操作等,键盘过滤驱动可以监控键盘的一些操作(这样可以实现一些快捷键效果,也有一些病毒木马利用这个来记录键盘输入)。
对象劫持的一些说明:可以理解成系统中的一些文件被替换。如果您的系统出现一些劫持情况,也不必惊慌,这可能是正常现象,因为有些安装有些杀毒软件会出现这个情况。 上图所示的“\Device\Harddisk\DR0的下层设备/驱动被劫持”是机器感染TDL3+病毒后出现的,这个病毒劫持了磁盘的一些操作,但安装某些还原软件后也会有这种情况。 如果您那里出现“内核模块文件被替换,可能是驱动程序升级后未重启电脑导致的,也可能是被病毒劫持了”,很可能是Window打补丁或者一些有在线升级功能的程序升级驱动模块后未重启电脑导致的,当然有些病毒也会替换系统里的一些正常驱动(比如beep.sys等)来实现驱动加载,遇到这个情况需要您仔细辨认。 如果出现“\Device\KeyboardClassX的下层设备/驱动被劫持”则表示键盘相关操作可能被劫持,要注意下机器上是否安装有键盘记录器等。(注,不是所有的键盘记录行为都是病毒木马,一些安全控件也有可能有此类行为,如卡巴斯基提示PDM.Keylogger,则可能是支付宝控件) 如果出现“进程对象被隐藏或者篡改”则表示有进程对象被恶意篡改等。 DPC定时器大致作用:目前有不少正常程序和rootkit会利用DPC定时器反复检查自身的Hook是否被恢复。 GDT:CPU内核模式下的一种很抽象术语,还没想到什么通俗易懂解释,不过和前面的进程查看一样,如果是红色的需要警惕了。 内核钩子 简单地说就是拦截各种系统命令的动作,比如我们右击删除一个文件,那么就会给系统发出一个“我要删除这个文件”的命令。收到后windows再转给系统中专门负责删除职能的那个人,由他去具体执行,而如果病毒采用了钩子,则会监视这些操作,将所有命令先经过自己手。如果发现删除命令的对象是自己,那么就把这个命令抛弃掉,如果是其他的就继续交给那个人来执行。就有点类似于奸臣扣留所有对自己不利的奏章,欺上瞒下那种。不过并不是所有钩子都是病毒的杰作,安全软件基本都有的,如何辨别就需要对文件名字有一定了解或上网搜索下。
应用层钩子意思也差不多,不过级别没有内核高
网络项目对于新手来说还比较好用,可以知道哪些进程有联网行为,不过美中不足的是似乎不能在这里结束进程
这里的连接状态大多时候分为 LISTEN:正在监听中,随时准备连接某一个目标 TCPIP部分功能默认显示所有和网络连接相关函数
浏览器的插件,很多软件安装完毕或者流氓软件都会在浏览器里驻足,过多的插件会给浏览器带来不稳定
浏览器右键菜单内容显示,和很多清理工具差不多。 SPI简单地说就是现实计算机目前所有的硬件接口和设备,右击有一个恢复lsp的选项,而这个LSP即分层服务提供商,再好的方面可以被利用编写网络监控软件,坏的方面会被木马软件利用劫持浏览器,有时候清除了病毒木马但还是无法连接网络也可能和没有恢复 lsp有关。 Hosts文件,可能不少人都知道了
修改里面的内容会屏蔽一些网站,比如下面我们将网易的网址转到127.0.0.1这个ip上面,但是网易服务器的ip明显不是这个,所以打开网易就会失败,出现无法访问的现象,当然只对本机有效
注册表,不需要多说了吧,如果病毒禁止你使用注册表那么就可以到这里来操作,注册表里包含了而系统基本所有的设置,有关修改注册表来实现一些系统和软件设置更改的教程可以写厚厚一本书,我们这里就不多说了,大家可以自己搜索些注册表技巧看看。 文件管理器,这在小菜们手工杀毒方面也是非常重要必备的,这里可以像在我的电脑或者资源管理器那样操作文件,不过他更加强大实用,可以查看此盘里面所有的文件和文件夹,不管是否被隐藏,右击某个文件弹出的菜单也非常强大,一些正常情况下不能被删除修改的文件在这里都能轻松被消灭。
这里可能会有很多红色显示的文件,不一定都是危害,因为对于隐藏的文件都会用红色显示,而windows系统里有大量自带的文件和文件夹默认状态下都是被隐藏的。 右键菜单的第二个选项“查看锁定情况”有些网友可能比较陌生,一般无法访问或操作某个文件时,您可以用本功能看看这个文件被那些程序占用了,解锁后一般就可以访问或操作这个文件了。
而删除不了的文件可以试试强制删除或者添加到重启删除,当然对于一些比较顽固难产的病毒文件为了彻底杀灭可以用删除后组织文件再生或者重启替换为...来防止它死灰复燃 另外右击某一个磁盘或文件夹可以进行搜索操作,这里的搜索功能比windows自带的强大很多,供选择的条件比较丰富
作为手工杀毒利器,病毒木马最喜欢的开机自动运行也被分析的很干净
这里分别显示了一些会自动运行的程序位置名称,等等,第二项的类型则是启动的方式,有注册表启动,也有在启动文件夹里实现开机启动的,等等。和前面查看进程一样,黑色是被认为安全的微软公司出品文件,蓝色则是其他软件公司或个人的作品。 服务栏目里列举了本机所有的服务项目,这些也都是会开机自动启动的。
当然我们早就说过病毒木马的启动并非一定要在这些位置,它们也会在后期让用户在不经意间运行病毒,比如下面的系统杂项,就包含了一些可能被篡改的内容。比如文件关联,指定了某一种格式的文件应该如何被打开,红色的是工具所不认识的方式,但也未必就是错误有害的,及时修复也可以。
映像劫持之前也提到过,曾经火爆一时,现在也是需要注意。 IME输入法 系统中安装了那些输入法,输入法之间一般通过快捷键切换,有些病毒会把自己注册成一种输入法,当用户通过快捷键切换输入法的时候,就有可能把病毒激活。我的系统里只安装了搜狗输入法,因为他不是微软的产品,所以蓝色显示。
系统防火墙规则 Windows自带防火墙的过滤规则,类型里的standard app 表示这是以程序文件,而 open port表示一个端口号码。状态栏中的Enabled表示允许联网规则,Disable表示禁止联网规则。少数病毒为了顺利连接外网,也会把自己添加到系统防火墙规则里。设置为Enabled。这样这些在访问网络系统防火墙就不会报警,而是直接放行。所以查看下规则,对于有误的及时修改删除也是很有必要的
不过很多网友都不用系统自带的防火墙,那样就不会看到任何有关的规则了。 杂项里面是一些修复功能,可以用于杀毒完毕的善后或者被病毒破坏了一些设置的修复。
Mbr备份恢复功能也可以试一试,因为现在也出现了破坏mbr达到重装系统都无法搞定的病毒了,不过需要注意的是重置MBR和BootSector是一个相当危险的操作,稍有不慎就可能导致系统无法启动。 最后软件还提供了一些实用的小功能,比如,禁止创建进程、禁止创建文件、禁止创建注册表等功能在杀毒的时候十分适用,可以十分有效的限制病毒行为,让你手工杀毒更快捷。这里我勾选了第一个禁止创建进程,因为双击打开一个文件程序都会产生一个进程,所以这时候我们双击任何程序都会弹出错误或没有反应。
|
|
dll注入
编译生成dll: 新建项目:
源代码: MessageBoxA(0, "测试显示的内容", "测试显示的内容!", 0);
选择输出类型: 1.在项目右击属性
打开属性界面:
在项目默认值下的配置类型:选择动态库.dll
dll 文件不需要main函数 运行注入DllInject.exe
选择需要注入的进程,点击注入
在动态连接文件:选择浏览文件
在源码中加入 _declspec(dllexport) 模块启动时启动的函数
|
|
进制计算
八进制—>二进制
分成三位一个数 ——> 对应的二进制
二进制 -->八进制
分成 三位 ---> 对应的八进制数
十进制--> 二进制
小数,浮点数 二进制十进制转换
|
|
常量与变量
变量 赋值随时可以改变
|
|
自动点击 自带浏览器 淘宝版
新建一个项目:
在visual c++ 中
MFC向导
点击完成即可
代码修改:
找到默认打开网页的代码:
增加一个新的事件:
修改为自己需要打开的网页:
打开资源视图修改界面
新增类别
在面板上新增按钮
在按钮上增加事件: 在需要加事件的按钮上右击选择新增事件:
|
|
sprintf()以及批处理for的使用
代码:
#include <stdio.h> int main(int argc, char *argv[]) { int num =010; scanf("%d",&num); char str[50]="notepad"; sprintf(str,"for /l %%i in (1,1,%d) do start calc",num); system(str); //printf("Hello, world\n");
return 0; }
sprintf(被输入的字符变量,符号的格式,符号格式中的占位符替换变量)
代码中的 sprintf(str,"for /l %%i in (1,1,%d) do start calc",num); 当num传入一个数字,比如5,那么 str就为:for /l %%i in (1,1,5) do start calc
for 的语法:
对一组文件中的每一个文件执行某个特定命令。 FOR %variable IN (set) DO command [command-parameters] %variable 指定一个单一字母可替换的参数。 (set) 指定一个或一组文件。可以使用通配符。 command 指定对每个文件执行的命令。 command-parameters 为特定命令指定参数或命令行开关。 在批处理程序中使用 FOR 命令时,指定变量请使用 %%variable 而不要用 %variable。变量名称是区分大小写的,所以 %i 不同于 %I.
如果启用命令扩展,则会支持下列 FOR 命令的其他格式: FOR /D %variable IN (set) DO command [command-parameters] 如果集(set)中包含通配符,则指定与目录名匹配,而不与文件名匹配。 FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters] 检查以 [drive:]path 为根的目录树,指向每个目录中的 FOR 语句。 如果在 /R 后没有指定目录规范,则使用当前目录。如果集仅为一个单点(.)字符, 则枚举该目录树。 FOR /L %variable IN (start,step,end) DO command [command-parameters] 该集表示以增量形式从开始到结束的一个数字序列。因此,(1,1,5)将产生序列 1 2 3 4 5,(5,-1,1)将产生序列(5 4 3 2 1) FOR /F ["options"] %variable IN (file-set) DO command [command-parameters] FOR /F ["options"] %variable IN ("string") DO command [command-parameters] FOR /F ["options"] %variable IN ('command') DO command [command-parameters] 或者,如果有 usebackq 选项: FOR /F ["options"] %variable IN (file-set) DO command [command-parameters] FOR /F ["options"] %variable IN ("string") DO command [command-parameters] FOR /F ["options"] %variable IN ('command') DO command [command-parameters] fileset 为一个或多个文件名。继续到 fileset 中的下一个文件之前, 每份文件都被打开、读取并经过处理。处理包括读取文件,将其分成一行行的文字, 然后将每行解析成零或更多的符号。然后用已找到的符号字符串变量值调用 For 循环。 以默认方式,/F 通过每个文件的每一行中分开的第一个空白符号。跳过空白行。 你可通过指定可选 "options" 参数替代默认解析操作。这个带引号的字符串包括一个 或多个指定不同解析选项的关键字。这些关键字为: eol=c - 指一个行注释字符的结尾(就一个) skip=n - 指在文件开始时忽略的行数。 delims=xxx - 指分隔符集。这个替换了空格和制表符的 默认分隔符集。 tokens=x,y,m-n - 指每行的哪一个符号被传递到每个迭代 的 for 本身。这会导致额外变量名称的分配。m-n 格式为一个范围。通过 nth 符号指定 mth。如果 符号字符串中的最后一个字符星号, 那么额外的变量将在最后一个符号解析之后 分配并接受行的保留文本。 usebackq - 指定新语法已在下类情况中使用: 在作为命令执行一个后引号的字符串并且一个单 引号字符为文字字符串命令并允许在 file-set 中使用双引号扩起文件名称。 某些范例可能有助: FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k 会分析 myfile.txt 中的每一行,忽略以分号打头的那些行,将 每行中的第二个和第三个符号传递给 for 函数体,用逗号和/或 空格分隔符号。请注意,此 for 函数体的语句引用 %i 来 获得第二个符号,引用 %j 来获得第三个符号,引用 %k 来获得第三个符号后的所有剩余符号。对于带有空格的文件 名,你需要用双引号将文件名括起来。为了用这种方式来使 用双引号,还需要使用 usebackq 选项,否则,双引号会 被理解成是用作定义某个要分析的字符串的。 %i 在 for 语句中显式声明,%j 和 %k 是通过 tokens= 选项隐式声明的。可以通过 tokens= 一行 指定最多 26 个符号,只要不试图声明一个高于字母 "z" 或 "Z" 的变量。请记住,FOR 变量是单一字母、分大小写和全局的变量; 而且,不能同时使用超过 52 个。 还可以在相邻字符串上使用 FOR /F 分析逻辑,方法是, 用单引号将括号之间的 file-set 括起来。这样,该字符 串会被当作一个文件中的一个单一输入行进行解析。 最后,可以用 FOR /F 命令来分析命令的输出。方法是,将 括号之间的 file-set 变成一个反括字符串。该字符串会 被当作命令行,传递到一个子 CMD.EXE,其输出会被捕获到 内存中,并被当作文件分析。如以下例子所示: FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i 会枚举当前环境中的环境变量名称。 另外,FOR 变量参照的替换已被增强。你现在可以使用下列 选项语法: %~I - 删除任何引号("),扩展 %I %~fI - 将 %I 扩展到一个完全合格的路径名 %~dI - 仅将 %I 扩展到一个驱动器号 %~pI - 仅将 %I 扩展到一个路径 %~nI - 仅将 %I 扩展到一个文件名 %~xI - 仅将 %I 扩展到一个文件扩展名 %~sI - 扩展的路径只含有短名 %~aI - 将 %I 扩展到文件的文件属性 %~tI - 将 %I 扩展到文件的日期/时间 %~zI - 将 %I 扩展到文件的大小 %~$PATH:I - 查找列在路径环境变量的目录,并将 %I 扩展 到找到的第一个完全合格的名称。如果环境变量名 未被定义,或者没有找到文件,此组合键会扩展到 空字符串 可以组合修饰符来得到多重结果: %~dpI - 仅将 %I 扩展到一个驱动器号和路径 %~nxI - 仅将 %I 扩展到一个文件名和扩展名 %~fsI - 仅将 %I 扩展到一个带有短名的完整路径名 %~dp$PATH:I - 搜索列在路径环境变量的目录,并将 %I 扩展 到找到的第一个驱动器号和路径。 %~ftzaI - 将 %I 扩展到类似输出线路的 DIR 在以上例子中,%I 和 PATH 可用其他有效数值代替。%~ 语法 用一个有效的 FOR 变量名终止。选取类似 %I 的大写变量名 比较易读,而且避免与不分大小写的组合键混淆。
|
|
调试时查看局部变量的值
在需要查看的地方按F9加上断点 运行时就可以查看到:
|
|
Day4
格式说明符的范围
格式说明符有一定的范围,只能在一定的范围内才能解析成功
超出范围报错:: warning: integer overflow in expression
类型大小: 字节 1字节=8位
short min-max : -32768 32767 int min -max : -2147483647-1 2147483647
| ||||||||||||||||||||||||||||||
|
Day5
自增自减运算符
--i 与i--的区别
--i: 只跳出四次,先自减一次再循环:
i--: 先循环再自减:
在while()执行体中的自增自减: --i:
i--:
在循环执行体中效果一样。
执行的时间: i++ ; i-- ;在执行完所在的语句之后执行。 测试效果:
--i,++i; 先实现自减和自加
|
|
dos窗口下的变色龙
代码:
#include <stdio.h> #include <windows.h> void main() { int i = 0; while (i++<= 0xf)//十六进制 { char str[30]; //字符串 sprintf(str, "color %x%x", i, 15 - i);//打印颜色 system(str); //执行指令 system("ipconfig"); //显示IP Sleep(1000);//暂停1秒 //i++; } }
|
|
dos窗口标题显示倒计时
代码现实: #include <stdio.h> #include <windows.h> void main() { int i = 0; while (++i) { char str[15]; sprintf(str, "title 第%d秒", i); system(str); Sleep(1000); } }
|
|
自增和自减和乘除加减的优先级
先负号,如果有负号先改变符号。 然后是++ 和-- 再是 * / % 最后+ -
|
|
逻辑或与 非
在C语言中只有短路的逻辑或与
短路或:|| 条件1 || 条件2 如果条件 1为真,那么条件2就会被短路
短路与:&& 条件1 && 条件 2 如果条件1 为假,那么条件2就被短路
逻辑非
|
|
三目运算
判断?执行体1:执行体2 当条件为真 执行 执行体1,当条件为假,那么执行执行体2
结合方向:右向左 返回的类型由后面的类型绝定。
|
|
左值与程序实体
程序实体是内存中的一块可标识的区域,左值是左值表达式的简称,是指明一个程序实体的表达式。
判断一个表达式是否左值的方法是看其能否放在等号的左边。能放在赋值号的左边的表达式都是左值,他指明了一块内存区域,而赋值运算实质是改变这一区域内容的操作。
特列: const 常量是左值,但不能将其放在赋值号左边。
|
|
常量与程序实体
定义常量的方式有两种: #define PI 3.14 const PI 3.14
const 修饰的常量是一个程序实体,可以取内存地址。也是左值,可以放在赋值符号的左边。
#define 定义宏,常量,无法取地址,汇编立即数,#define 定义的常量直接存取到CPU,编译器的权限接触不到常量,#define定义的常量不是程序实体,无法对常量进行标识。
|
|
结合性
操作数: a=a+b a和b都是操作数。
对比操作两边的运算符,比较优先级。
/*1 + 3 - 2 / 5 * 4; 优先级依托于操作数 4 - 2 / 5 * 4; 4 - 0 * 4 4 - 0; 4;*/ //1 + 2 - 3 + 5 - 6; 结合性 //1 + 2-9 / 3 * 4 - 5; //3-9/3*4-5; //3-3*4-5; //3-12-5; //-5+3*6; //4*-1; //从内向外,从左向右 ((1 + 2) - 3) * (4 / 5) - 6 * 8;
|
|
加密与解密
#include<stdio.h> #include<stdlib.h> void main() { //加密主函数 char str[13] = "i love china"; //创建一个字符串数组 printf("%s\n", str); //打印字符串 int i = 0; while (i < 13) //从0循环到13 { str[i] != '\0' ? str[i] += i : 1; //判断字符有没有结束。 i++; } printf("%s\n", str); //解密主函数 { int i = 0; while (i < 13) { str[i] != '\0' ? str[i] -= i : 1; //判断字符有没有结束。 i++; } printf("%s\n", str); } getchar(); }
|
|
数据的输入与输出
获取命令并执行
运行时,可以重新定向输入输出。 命令行下的重新输入符"<"或">" 箭头代表输出的方向。
sprintf(需输入的变量,“字符格式”,需输入的字符)
sprintf(str, "color %d%d", 3, 5);
得到 color 35
格式符
printf("") 不会对输出量进行类型变换,类型不对,无法输出正确的答案。
%c 可接受字符,整数, 'h' ,104 ,0150, 0x68, '\150'
%d 可以输出字符对应的ASCII对应的数字。
%mc c默认是从右边输出的,就是从输出字符宽度的右边开始填充。 m 为我们设置的给打印出的字符的宽度。
%-c 左对齐,c语言默认的是右对齐
%[-][m][.n]s
-是从左边开始填充,默认是从右边开始显示 m 定义输出的字符的长度 .n定义了截取字符的长度
|
|
优先级
逗号运算符与表达式 用逗号将多个表达式连接起来,又称为“顺序求职运算符”。 整个表达式的值是最后那个逗号之后表达式的值。
逗号表达式的值为最后的一个表达式的值。
C语言的优先级共15级: 1的优先级最高,15级的优先级最低,同等优先级运算次序由结合方向所决定。
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
字符串的合并
在C中字符串不能直接通过+ & 等其他语言中常用的连词符来连接,需要通过sprintf()合并。、 sprintf(str, "%s%s", str1, str2);
|
|
外挂
在windows下不许不同的进程修改其他进程的内存。 使用dll注入进程。
_declspec(dllexport)
靶子: #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<Windows.h> #include<stdlib.h> void main() { int x = 0; printf("%x", &x); while (1) { char str[20]; sprintf(str, "title 第%d秒", x++); system(str); printf("\n%s", str); Sleep(1000); } }
dll文件: _declspec(dllexport) void mm() { int *p = (int *)0x31fe60; //p的值就是0x31fe60 将转为整型的指针,方便赋值整型值。
*p = 0; //根据地址赋值 }
|
|
Day6
关于头文件的解答
头文件根据不同的宏变量检测不同的平台 在头文件中有判断:
有时,不同的平台调用不同的头文件
查看当前项目的lib二进制文件: 打开连接器: 所有选项:
附加依赖项:
查看当前依赖项:
|
|
关于int main() return 0;
C99中放松了要求,在VC ++6.0 在编译会出现警告,在其他的编译器上编译不会出现错误,最好是带上return 0;
int main( void ) { return 0; }
} |
|
试卷讲解
一、选择题(每题2分) 1. 下列C语言中运算对象必须是整型的运算符是 。 A) %= B) / C) = D) *=
讲解: A) a%=b --> a=a%b; 报错: 测试代码: int a =10; float b=1.5; a%=b;
“%=”非法,有操作数包含 float。
修正: a%=(int)b;
2. 在C语言中不合法的整数是 。 A)20 B)0x4001 C)08 D)0x12ed
讲解: C) 八进制:1-7 不超过8 十六进制:1-F
3.以下十六进制数中不合法的是 A)oxff B)0Xabc C)0x11 D)0x19
讲解: A)
4. 若有float x;则sizeof (x)和sizeof (float)两种描述( )。 (A)都正确 (B)都不正确 (C)前者正确 (D)后者正确 讲解: A) 测试代码: char *p ="abssjkslk"; sizeof(p);为4 ,指针类型的大小为4;
5. 若给定条件表达式(M)?(a++):(a--),则其中表达式(M) 。 A.和(M==0)等价 B.和(M==1)等价 C.和(M!=0)等价 D.和(M!=1)等价 讲解: C) 测试代码:
6. 已定义ch为字符型变量,下列赋值语句中错误的是 。 A)ch='\'; B)ch=62+3; C)ch=NULL; D)ch='\xaa'; 讲解: A) ASCII码表 用一个字节存储。 0-127 为表1范围,128-255为表二,特殊字符的表。 "\"为转移字符。 ch='\' 应该为ch ='\\' B 选项为字符 ‘A’ C 选项 Null 定义为0 在ASCII中为空,什么也没有。
D 选项 测试代码:
结果:170,170
7. 设有“int x=11;”则表达式(x++*1/3)的值是 。 A)3 B)4 C)11 D)12 讲解: A) a/b 两边都是整数,那么表达式的结果将转换为整数。
8. 下列描述中,正确的一条是 。 A)C语言的整型变量可以分为int 、short、long、unsigned int、unsigned short、unsigned long等几种类型,因此整型常量也可以分为这几种类型 B)C语言的字符数据与整型数据可以互相赋值 C)若对字符型变量的定义为:“char c;”可使用语句c=“a”对变量c赋值 D)已知x为float型,执行语句(int)x后,x为int型
讲解: B)
A选项没有那么多的类型, B选项如果超过int范围,那么会被截断。
9. 已定义c为字符型变量,则下列语句中正确的是 。 A)c='97 ' B)c=“97 “ C)c=97 D)c=“a”
讲解: C) A选项 打印55
10.表达式10<20的值是_______。 A)10 B)20 C)0 D)1 讲解: 返回bool值 D)
11.已知char a; int b; float c; double d; 则表达式a+b * c-d的结果为_______型。 A)char B)int C)float D)double 讲解: D)
12. 在C程序中,x + y是_______、x + y;是_______。 A)表达式、语句 B)表达式、表达式 C)语句、语句 D)语句、表达式 讲解: A)
13.已知int x = 23; 则printf(x++*1/3)的输出结果为_______。 A)8 B)7.66 C)7.33 D)7 讲解: D) a ++ ,++是在语句执行完之后++
14. 执行下列程序片断后c的值是_______。 int a = 1, b = 2, c; c = 1.0/b*a; A)0 B)0.5 C)1 D)2
讲解: A) 表达式的值为0.5 赋值给整型时截断,只保留整数部分。
15. 若有定义:char c=’\010’;则该变量中包含的字符个数是 。 A) 非法定义 B) 1个 C) 3个 D) 4个
讲解: B) char 只有一个字节 '\010'为8,在ASCII 码表中只代表一个字符。
二、填空题(每题4分) 1. printf库函数的第一个参数中的格式串是指明输出数据格式,各种数据类型对应的基本格式串是: short、int (1) %d , long (2)%ld , float (3) %f 、 (4) %e , double (5) %e %lf 、 (6) , char (7)%c 、 (8)%d , 字符串 (9)%s 。 2. scanf库函数格式串和输入项之间的关系是 对应? 。 3. 阅读下述程序功能:将三位整数n的十位数的数字变为0。例如,输入三位整数为738,输出为708。请将正确答案写在横线处。 # include<stdio.h> void main( ) { (1) int n=0,d2=0,d0=0; ; printf (“输入一个三位整数:”); scanf (“%d”,&n); d2= (2) n/100 ; /* 取出百位数的数字 */ d0= (3) n%10 ; /* 取出个位数的数字 */ printf (“输出三位整数:%d\n”, (4)d0+d2*100 );
}
4. 若x为单精度型变量,y为字符型变量,z为整型变量,执行如下的输入语句:
%f 可以识别的: 遇到其他字符停止解析 遇到A就停止解析。将由下一个%C解析。解析A。 %d
5. 已知char c= 'A';int i=1,j;执行语句j=!c&& i++后,i和j的值分别是 和
三、问答题(每题4分) 1. 字符型常量和字符串常量有什么区别?
讲解:
2. 已知int i=5,j=5;试问表达式或函数输出的值: (1)i++ - ++j (2)++i - j++ 1. printf (“%d\n”,++i+(++j)); (4)printf (“%d\n”,i++ +j); 讲解:
3. 程序改错 #include <stdio.h> void main( ) { float f=7.12; char c=“c”; printf(“%d\n”,int(f%3)); printf("%c",c); } 讲解:
4. 请指出以下程序段中的错误。 #include <stdio.h> void main ( ) { short j,i,k; char a,b,c; float x, y, z; i=30000; j=30000; a= '9'; b= '5'; k=a*i+b*j; //k溢出 z=x*x+y*y; //没有初始化 }
5. 以下程序的输出是什么? #include <stdio.h> void main ( ) { int i; unsigned int j; i= ~0; j= ~0; printf ("i=%d j=%d\n",i,j); }
四、编程题(每题6分) 1. 编写程序,读入3个双精度数,求它们的平均值并保留此平均值小数点后一位数,对小数点后第二位数进行四舍五入,最后输出结果。
2. 编写一个简单的C程序,输出以下信息: * * * * * * * * * * * * * C program! * * * * * * * * * * * * *
1. 编写一个程序,输入半径,输出其圆周长、圆面积、及圆球体积。
4. 输入三个整数,按从小到大的顺序进行输出。
5. 编写一个程序,输入一个摄氏温度,输出其对应的华氏温度。
|
|
Day7
printf高级应用(格式说明符)
#include<stdio.h> //%d,%u,%o 如果大写%D ->D %U->U %O->O //%C,%c都是一样 //%S-%F->什么都不输出 //%E,%X,%E指数会大写,%X十六进制的字母会大写。 //%G, %g可以大写,按照%f正常是输出,按照%e,指数为大写 //打印的时候,一一对应 输出表多了会被忽略 /控制项多了,会打印出不定值 void main7() { //%g可以大写,按照%f正常是输出,按照%e,指数为大写 printf("\n%e,%f,%g", 10000000.0, 10000000.0, 10000000.0); printf("\n%e,%f,%g", 100.1234567, 100.1234567, 100.1234567); printf("\n%e,%f,%G", 10000000.0, 10000000.0, 10000000.0); printf("\n%e,%f,%G", 100.1234567, 100.1234567, 100.1234567); getchar(); } void main6() { //printf不管你是实数或者整数,按照自己方式来解析 // %g取%e,%f之间宽度最小的 ,%g最多6个数字 printf("%e,%f,%g", 10000000.0, 10000000.0, 10000000.0); printf("\n%e,%f,%g", 100.1234567, 100.1234567, 100.1234567); printf("\n"); printf("%E", 10000000.0); //E决定指数大小写 printf("\n"); printf("%e", 10000000.0); printf("\n"); printf("%F", 10000000.0); //%f中的F不能大写,不能为空 printf("\n"); printf("%f", 10000000.0); getchar(); } void main5() { printf("%s", "gogogo"); printf("\n"); printf("%S", "gogogo"); //%s中的S不能大写,写了以后什么都不输出 getchar(); } void main4() { //字符%c,%C是一样的 printf("%c", 48); printf("%C", 48); printf("%c", 65); printf("%C", 65); getchar(); } void main3() { printf("%x", 0x1A);//%X可以大写,代表十六进制字符是大写的 printf("\n"); printf("%X", 0x1A); getchar(); } void main2() { printf("%o,%x\n", 010, 0x18); printf("%O", 010); printf("\n"); printf("%u", 010); printf("\n"); printf("%U", 010);//%o,%d,%u也不可以大写 getchar(); } void main1() { printf("%d", 10); printf("\n"); //%为空,D就打印出D printf("%D", 10); //无符号整数%d不能大写, printf("\n"); printf("%"); //空 printf("\n"); printf("%%"); //打印一个%号 printf("\n"); //字符串转换, % -《 %% d <-d printf("%%d",11); printf("\n"); //"%%%d" - %%-》% %d-映射11 printf("%%%d", 11); getchar(); }
|
|
printf 转换失败输出
printf不支持类型之间的转换。 尝试将Int类型转换为float 将输出0.000000
尝试将float类型转换为int 时,输出0
|
|
第三章节总结
1. C 语言中没有bool类型 非0为真,0为假:
宽字符: 国际化 ,
一个汉字占用2个字节
有符号无符号最大差别是最高位是否符号位。
|
|
第四章简介
主要的语句类型:
算法的流程图表示:
if 语句在没有大括号的情况下,默认最近的一句语句。
#define 整数,可以用来实现对比的
|
|
字符检测
#include<stdio.h> #include<stdlib.h> void main() { while (1) { char ch = getchar(); getchar();//填空回车 if (ch >= 'A' && ch <= 'Z') { printf("\n大写字母"); } else if (ch >= 'a' && ch <= 'z') { printf("\n小写字母"); } else if (ch >= '0' && ch <= '9') { printf("\n数字"); } else { printf("\n其他字符"); } } }
|
|
分支语句 解一元二次方程
一元二次方程的解的情况有如下的几种可能:
分析:
代码实现:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<math.h> void main() { double a, b, c; scanf("%lf%lf%lf", &a, &b, &c); printf("%lf*x*x+%lf*x+%lf=0\n", a, b, c); if (a) //a!=0 { printf("一元二次方程"); double m = b*b - 4 * a*c; double n = -1 * b / 2 / a; if (m == 0) { printf("两个相等的实根x1=x2=%lf", n); } else if (m>0) { printf("两个不等的实根x1=%lf,x2=%lf", n + sqrt(m) / 2 / a, n - sqrt(m) / 2 / a); } else { printf("两个不等的虚根%lf+%lfi,%lf-%lfi", n, sqrt(-1 * m) / 2 / a, n, sqrt(-1 * m) / 2 / a ); } } else { printf("非一元二次方程"); if (b) { printf("一元一次X=%lf",-1*c/b); } else { if (c) { printf("无解"); } else { printf("X为任意值"); } } } system("pause"); }
|
|
循环语句简介以及goto
CCC: goto CCC;
|
|
scanf函数讲解
获取地址: scanf("%d", &x);
需要按照格式输入: 必须按照sacnf中的正确的格式输入,如果格式不对,那么将得不到正确的结果。 显示结果:
测试:格式中有逗号和没有逗号: 代码:
没有输入逗号:
输入逗号:
测试代码: 格式符里面没有逗号:
输入时,用逗号分隔将出现错我的结果:
输入时,不用逗号分隔,将得到正确的结果
对于double类型的输入必须使用%lf格式符:
使用%f读入将发生溢出,丢失精度。
输入数组,不需要加获取地址符,C默认将数组解释为指针类型。
间隔符:
截取: 读入字符,自定义获取长度:
格式符前*修饰符,将会跳过输入的数字:
x读取到1,而遇到*直接跳过 2 ,y读的3,z没有赋值,将读取内存中之前的值。
|
|
格式字符
%-0m.nlh
%表示格式说明的开始,不可缺少 -左对齐,可选,默认右对齐 0 有0表示指定的空位填0,如果缺省表示指定的空位不填 m指域宽,即对应的输出想在输出设备上所占的字符数。 n指精度。默认float的精度为6 l 对应long型 h将整型的格式符修正为short 型。
|
|
sscanf函数的使用
sscanf(str, "my name is %s", strl);
str 将替换 "my name is %s " 中的%s 按字符格式替换,替换的结果存入 strl .
scanf 是以标准输入源,而sscanf是以字符串为输入源。 扫描字符串:
挖掘颜色:
1. 常见用法。
结果为:123456 2. 取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。
结果为:1234 3. 取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。
结果为:123456 4. 取仅包含指定字符集的字符串。如在下例中,取仅包含1到9和小写字母的字符串。
结果为:123456abcdedf 当输入: sscanf("123456abcdedfBCDEF","%[1-9A-Z]",buf);
结果为:123456 5. 取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。
结果为:123456abcdedf 6、给定一个字符串iios/12DDWDFF@122,获取 / 和 @ 之间的字符串, 先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中
结果为:12DDWDFF 7、给定一个字符串“hello, world”,仅保留world。 (注意:“,”之后有一空格,%s遇空格停止,加*则是忽略第一个读到的字符串)
结果为:world %*s表示第一个匹配到的%s被过滤掉,即“hello,”被过滤了 如果没有空格则结果为NULL。
| ||||||||||||||||
|
Day8
循环结构
当型循环 和直到型循环
就do---while 为直到型。
先执行一次指定的循环内嵌语句,然后判断条件表达式。
|
|
多线程
多线程 _beginthread 和_beginthreadex 需要头文件 process.h
例子: _beginthread(runmsg, 0, NULL); runmsg 为需要在线程中开启的函数
runmsg函数:
void runmsg(void *p) { system("calc"); Sleep(600000); }
|
|
Day9
switch语句
switch () ()内不能使用实数。 与break结合使用。 switch () { case 条件: break; default: break;
}
|
|
嵌套for 九九乘法表
嵌套for ,双层for 完全打印: 全角:
三角:
老师的代码在附件; 我的代码: 全角:
代码: #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int n; scanf("%d",&n); for (int i=0;i<n ;i++ ) { for (int j=0;j<n ;j++ ) { printf("%d*%d=%d\t",i,j,i*j); } printf("\n"); } system("pause"); return 0; }
对角线:
主要代码: #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int n; scanf("%d",&n); for (int i=0;i<n ;i++ ) { for (int j=0;j<n ;j++ ) { if(i==j)//如果i==j那么就打印 printf("%d*%d=%d\t",i,j,i*j); else // 不等于的也要打印,不然只在一列。不满足条件的打印空字符。 printf(" \t"); } printf("\n"); } system("pause"); return 0; }
斜对角线:
代码: #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int n; scanf("%d",&n); for (int i=0;i<n ;i++ ) { for (int j=0;j<n ;j++ ) { if(i+j==n)//如果i+j=n那么就打印 printf("%d*%d=%d\t",i,j,i*j); else // 不等于的也要打印,不然只在一列。不满足条件的打印空字符。 printf(" \t"); } printf("\n"); } system("pause"); return 0; }
双对角线:
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int n; scanf("%d",&n); for (int i=0;i<n ;i++ ) { for (int j=0;j<n ;j++ ) { if(i+j==n-1||i==j)//如果i+j=n 或者 i==j 那么就打印 ,打印双对角线。 printf("%d*%d=%d\t",i,j,i*j); else // 不等于的也要打印,不然只在一列。不满足条件的打印空字符。 printf(" \t"); } printf("\n"); } system("pause"); return 0; }
右上三角:
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int n; scanf("%d",&n); for (int i=0;i<n ;i++ ) { for (int j=0;j<n ;j++ ) { if(i<=j)//如果i+j=n 或者 i==j 那么就打印 ,打印双对角线。 printf("%d*%d=%d\t",i,j,i*j); else // 不等于的也要打印,不然只在一列。不满足条件的打印空字符。 printf(" \t"); } printf("\n"); } system("pause"); return 0; }
左下角:
关键代码: if(i>=j)//如果i+j=n 或者 i==j 那么就打印 ,打印双对角线。 printf("%d*%d=%d\t",i,j,i*j); else // 不等于的也要打印,不然只在一列。不满足条件的打印空字符。 printf(" \t");
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int n; scanf("%d",&n); for (int i=0;i<n ;i++ ) { for (int j=0;j<n ;j++ ) { if(i>=j)//如果i+j=n 或者 i==j 那么就打印 ,打印双对角线。 printf("%d*%d=%d\t",i,j,i*j); else // 不等于的也要打印,不然只在一列。不满足条件的打印空字符。 printf(" \t"); } printf("\n"); } system("pause"); return 0; }
右下三角:
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int n; scanf("%d",&n); for (int i=0;i<n ;i++ ) { for (int j=0;j<n ;j++ ) { if(i>=n-1-j)//如果i+j=n 或者 i==j 那么就打印 ,打印双对角线。 printf("%d*%d=%d\t",i,j,i*j); else // 不等于的也要打印,不然只在一列。不满足条件的打印空字符。 printf(" \t"); } printf("\n"); } system("pause"); return 0; }
|
|
Day10
Day11
三角形根据各边长求面积
设 各边长为: a,b,c 面积为:s p=(a+b+c)/2
s*s=p*(p-a)*(p-b)*(p-c);
|
|
递归
套用计算a的阶乘
int factrial (int a) { if (a==1) { return 1; } else { a=a*factorial(a-1); return a; } }
|
|
google面试题
立方体,每次只能走一步或两步,有多少可能。
超立方体:
平面:
F(x,y)=F(x,y-1)+F(x,y-2)+F(x-1)+F(x-2,y)
|
|
腾讯面试题
有50个台阶,一次可以走一步或者两步,问走完50个台阶,有多少个走法。
int go(int n) { if(n==1) { return 1; } else if (n==2) { return 2; } else { return go(n-1)+go(n-2); } }
三台阶; 除了一次可以上一步两步之外还可以上三步。 #include <stdio.h> unsigned long int go(int n) { if (n==1) { return 1; } else if (n==2) { return 2; } else if (n==3) { return 3; } else { return go(n-1)+go(n-2)+go(n-3); } } int main(int argc, char *argv[]) { printf("%lu\n",go(40)); return 0; }
|
|
递归的二进制转换
递归的多进制转换: #include <stdio.h> void changeX(int num,int bin); int main(int argc, char *argv[]) { int num,bin; printf("输入需要转换的整数:"); scanf("%d",&num); printf("输入需要转换的进制:"); scanf("%d",&bin); changeX( num,bin); return 0; } void changeX(int num,int bin) { if(num==0) { return; } else { int x =num%bin; num/=bin; printf("%x",x);//正序打印
changeX(num,bin); printf("%x",x);//逆序打印 } }
书上的参考代码: #include <stdio.h> void to_binary(unsigned long n); int main(int argc, char *argv[]) { unsigned long number; printf("输入一个整数(q退出):\n"); while (scanf("%lu",&number)==1) { printf("二进制等于:"); to_binary(number); putchar('\n'); printf("再次输入一个整数(q退出)"); } printf("完成\n");
return 0; } void to_binary(unsigned long n) { int r; r=n%2; if (n>=2) { // putchar('0'+r);
to_binary(n/2);
//putchar('0'+r);
}
putchar('0'+r);
return; }
|
|
递归实现回文
#include <stdio.h> void showCha(char x); int main(int argc, char *argv[]) { char ch; printf("请输入一个字母\n"); scanf("%s",&ch); showCha(ch); return 0; } void showCha(char x) { if (x=='a') { printf("%c",'a'); return; } else { printf("%c",x); showCha( (x-1)); printf("%c",x); } }
|
|
Day12
sizeof 和strlen区别
|
|
Day13
简单的指针外挂
效果图: 靶子: // //#include <stdio.h> //#include <stdlib.h> //#include <windows.h> //int main(void) //{ // int n = 9999; // // while (1) // { // printf("%d 地址:%X\n ", n, &n); // Sleep(1000); // n = n - 500; // } // // return 0; //}
指针外挂dll: #include <stdio.h> #include<Windows.h> #include<stdlib.h> _declspec(dllexport) int go() //讲解 _declspec声明为外部调用 dllexport 声明dll导出。
{ int *p = (int *)0x6cfd08; //将地址强制转换为指针类型。 while (*p < 5000) { *p = 9999; } return 0; }
注意事项:
被注入的程序和注入工具要在同一个账户下运行。
|
|
指针与地址的区别
指针是存放地址的变量,也是一个地址,大小是固定的。就是四个字节。指针也可以是一个常量,但需要const修饰符来修饰。
地址仅仅就是一个地址,地址是一个常量。
指针; 一个变量的地址 指针变量:专门存放变量地址的变量叫做指针变量。
指针有类型,修饰指针的类型 指定了指针所指向的地址的数据如何解析,从那里开始到那里结束。 而地址,只知道从那里开始,不知道从那里结束。
不同类型的指针自增的大小不一样 测试代码: #include <stdio.h> int main(int argc, char *argv[]) { char a; int b; double c; char *pa=&a; int *pb=&b; double *pc=&c; printf("a:%p\t b:%p\t c:%p\n",pa,pb,pc); ++pa;++pb;++pc; printf("a:%p\t b:%p\t c:%p\n",pa,pb,pc); ++pa;++pb;++pc; printf("a:%p\t b:%p\t c:%p\n",pa,pb,pc); ++pa;++pb;++pc; printf("a:%p\t b:%p\t c:%p\n",pa,pb,pc); printf("Hello, world\n");
return 0; }
|
|
指针的使用注意事项
指针使用时最好初始化。
不初始化能编译通过,但运行报错。
批量定义指针时
批量定义时需要每个指针前都有“*”
空指针 int * p ; p=NULL;
|
|
加载静态链接库
#pragma comment(lib,"User32.lib")
列子: #include <stdio.h> #include <stdlib.h> #include <windows.h> #pragma comment(lib,"User32.lib") int main(int argc, char *argv[]) { MessageBox(0,"a","b",0); int num; int *p=# *p=10; printf("Hello, world\n"); system("calc"); return 0; }
|
|
制作静态库调用静态库
静态块的制作: 1.编写函数,不需要主函数。 2.配置编译工具生成lib VS : 在调试中对应项目的属性
gcc 加 -share -o *lib
运行生成
编写头文件:
将函数的声明写入到对应的头文件中,头文件的名字可以和库文件的名字不一样。 VS中在头文件中增加头文件:
文件目录下的存放关系:
都在同一个文件夹目录下
需要调用时,需将lib和头文件存放在和需要调用的文件的同目录下。
调用时需要加载静态库: #pragma comment (lib,"mylib")
动态库,动态执行,在需要的时候调用。 静态库在编译链接时链接编译进二进制文件中,值有c/C++调用。
|
|
动态库和静态库
静态库在windows平台下为 *.lib 在linux 下一般是 libxxx.a; 调用静态函数编译的文件比较大,在链接时将整个函数库的所有数据整合到目标代码中。
动态库在windows 平台下为*.dll文件 在linux平台下一般为libxxx.so文件。相对静态库,动态库在编译的时候并没有被编译进目标代码中,当程序需要调用该函数库内的相应的函数,才会调用该函数里的相应的函数,因此动态库所编译生成的文件一般相对较小。
在vs 下创建动态或者静态库
GCC 生成库参数
使用 ar
|
|
间接访问和直接访问
直接访问:按变量地址存取变量值
间接访问: 通过存放变量地址的变量去访问变量。
|
|
空指针 零指针
定义指针的变量值为零
int *p =0;
#define NULL 0 int * p =NULL
|
|
通过指针排列大小,不改变原始值
判断*p 所指向的值的大小 然后交换地址。
|
|
对窗口外科手术
测试对象 calc
有时候需要对子窗口操作,需要先获取父类窗口的句柄,并在句柄上再获取到子窗口的句柄。 示例代码: HWND win = FindWindowA("#32770", "set应用"); HWND button = FindWindowExA(win, NULL, "Button", "获取数据"); if (button == NULL) //HWND指针, { printf("can not find "); } else { printf("find"); } while (1) { Sleep(1000); ShowWindow(button, SW_HIDE); //隐藏某个窗口 Sleep(1000); ShowWindow(button, SW_SHOW); //显示某个窗口 }
//#define SW_HIDE 0 //#define SW_SHOWNORMAL 1 //#define SW_NORMAL 1 //#define SW_SHOWMINIMIZED 2 //#define SW_SHOWMAXIMIZED 3 //#define SW_MAXIMIZE 3 //#define SW_SHOWNOACTIVATE 4 //#define SW_SHOW 5 //#define SW_MINIMIZE 6 //#define SW_SHOWMINNOACTIVE 7 //#define SW_SHOWNA 8 //#define SW_RESTORE 9 //#define SW_SHOWDEFAULT 10 //#define SW_FORCEMINIMIZE 11 //#define SW_MAX 11
SW_HIDE 隐藏窗口并将活动状态传递给其它窗口。
获取子窗口 HWND win = FindWindowA("#32770", "set应用"); HWND button = FindWindowExA(win, NULL, "Button", "获取数据");
移动函数: MoveWindow(win, 300, 300, 150, 180, 1);
|
|
Day14
函数指针调用函数 p与(*p)
/** 以下摘自C++ primer plus. |
|
指针实战
指针使用前需要初始化:
指针变量作为函数参数---地址传递 特点:共享内存,“双向”传递
指针陷阱: 交换地址不影响值 代码: #include <stdio.h> void swap(int *sp1,int *sp2) { int n=0; int *p; p=&n; p=sp1;//交换数值失败,这里的sp1 和main函数里的mp1 不一样,不是同一个指针变量,交换sp1,sp2的地址对 mp1 mp2没有影响。 sp1=sp2; sp2=p; } int main(int argc, char *argv[]) { int a,b; int *mp1,*mp2; scanf("%d%d",&a,&b); mp1=&a; mp2=&b; if (a<b) { swap(mp1,mp2); } printf("a=%d b=%d\n",*mp1,*mp2);
return 0; }
|
|
二级指针
指针的变量也是变量,占据一定的内存空间,有地址,因此可以用一个指针指向它,这称为指向指针的指针,或二级指针。
在32位系统中,指针的长度为4位
解析类型修饰符 *指针修饰符 指针变量名 = 地址;
二级指针 int **p =&n; 其中 int 用于解析 最终存放在地址所在的数据 而 * 指明这是一个指针,解析时按4位来解析。
多级指针:
int n;
int *p0=&n; int **p1=&p0; int ***p2=&p1; int ****p3=&p2;
|
|
不同类型的指针之间的转换
不同类型的指针之间不可以转换。
在x64架构下指针大小为8位 64位程序
在x86架构下指针的大小为4位 32位程序
|
|
Day15
Getchar
getchar每次获取一个字符并输入
|
|
Day16
指针与数组
数组在内存中的存放是连续的。
一维数组的 数组名地址和&数组名地址一样 取*数组的地址异常
一维数组数组名的解析:
一维数组中 a 与&a 的区别:
对一维数组的测试: a ,&a ,&a[0]都是存放的地址,*a 存放的是一维数组的第一个元素的值。 以下的所说的指针有歧义的指针,并没有定义指针。
a: a的大小为一维数组所有元素的大小的总和,如果切换为double类型数组,那么都是80,在int和double类型下的数组计算 a和 a+1的大小都是4 ,a为一个指针。在int 类型下的数组的一个元素的大小为4字节,而double类型下的一个元素的大小为8字节,在int 类型数组下,a+1比a 的地址增加了4字节,而在double类型下 a+1比a的地址增加了8字节,那么a是一个指向元素的指针。
&a: &a在int和double类型的数组下大小都是4,&a是一个指针,在int 类型的数组下,&a+1 比&a的地址移动了40字节,而在double类型下,&a+1比&a的地址移动了80字节,40字节为一个int数组的大小,80为一个double类型的数组的大小,&a是指向数组的指针,一次移动一个数组的长度。
&a[0]: &a[0]和a,&a的地址是一样的,在int 数组下移动一次为4字节,在double数组下移动一次为8字节,移动一次为数组中一个元素的大小。&a[0]为一维数组a第一个元素的地址。 &a[0]+1 移动一次,增加了与一个元素的大小。&a[0]+1 &a[1] 效果一样。
*a: *a取出a中存放的内容,*a 取出为一维数组的第一个元素。*a+1 为2 如果加20 则为21,a 中存放的是一维数组的元素。 那么是否可以这样理解呢?*&a,&a为一维数组的地址,*取内容,*&a取一维数组地址对应的内容,而取出的是一维数组元素的地址,一维数组存放着数组元素。
int 型一维数组:
dobule 型一维数组:
关于&(a+1) a+1 是一维数组的第二个元素的地址,在内存中没有实体,无法取到地址。
临时变量在寄存器中运算,在内存中没有地址,无法取的地址。
&取地址符的操作对象需要是一个左值。
数组名a 本质是一个常量指针,[]不仅可以作用于数组名也可以作用于指针。
a[i] 等价于 *(a+i) &a[i]等价于 a+i
a为一维数组名,int *p=a ,p是一个变量指针 数组的本质是一个常量指针指向的一片内存区域
[] 本质 a[3] 在内存中解析为*(a+3 )
|
|
指针引用多维数组
int a[3][4]={ {1,3,5,7},{9,11,13,15},{17,19,21,23} };
二维数组可以看成一个一维数组,只是每个元素又是一个数组。 一维数组 b[3] b[0]={1,3,5,7} b[1]={9,11,13,15} b[2]={17,19,21,23 }
验证代码:
行指针: int (*pb)[5]=b
对于二维数组 *b &b b 的区别
对于*b &b b地址是一样的。
*b 移动一位为4字节 b移动一位为20字节 也就是五个Int型的元素, &b移动一位为80字节 为一个数组的大小。 *b 指向元素 b指向行 &b为数组的指针
*b 是指向单个元素的指针,*b的大小一共为20,二维数组共20个元素。 b 是指向数组中下一层的元素的指针,下一层的元素是有多少行,那么b是指向 行元素的指针。 &b 是指向数组的指针,大小为80 一次移动80字节。
那么是否这样理解?b 一般就是存放是指向数组第一层的元素的指针,而*b如果是指针的话则是指向第一层指针所存放的元素的指针。以此类推。&b是指向数组整个数组的指针,将数组整个作为一个元素。
|
|
数组函数指针
数组名是一个常量指针,不是参数,用sizeof 就是整体数组大小。 数组名用做参数,是一个 指针,数组是函数副本机制的例外。 数组拷贝数据很麻烦很费空间,所以是例外。
|
|
指针与const
const int *p
这个指针不能改变指向的数据,可以改变指向谁。 和int const *p 是等价的。
int * const p
不可以改变指针的指向
const int * const p=&x p指针不能改变指向也不能改变数据
|
|
字符串
字符串以“\0”结尾 |
|
C语言的内存的分配机制
指向字符串常量的指针和字符串数组在使用时是有区别的。 //执行通过,返回“hello world” 再看下面:
//正确,修改数组中的第一个字符,b是在栈上分配的 一个由C/C++编译的程序占用的内存分为以下几个部分: 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 二、例子程序 //"123456/0"在文字常量区,而指针p3在栈上,p3指向文字常量区上一片内存 //分配得来得10和20字节的区域就在堆区。 //123456/0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 二、堆和栈的理论知识 2.2 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 2.3申请大小的限制 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 2.4申请效率的比较: 另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活 2.5堆和栈中的存储内容 堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。 2.6存取效率的比较char s1[] = "aaaaaaaaaaaaaaa"; 2.7小结: 使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。 堆和栈的区别主要分:
|
|
C 内存分配
1、内存分配方式 1. char a[] = “hello”; 2. a[0] = ‘X’; 3. cout << a << endl; 4. char *p = “world”; // 注意p指向常量字符串 5. p[0] = ‘X’; // 编译器不能发现该错误 6. cout << p << endl; 复制代码 示例3.1 修改数组和指针的内容 1. // 数组… 2. char a[] = "hello"; 3. char b[10]; 4. strcpy(b, a); // 不能用 b = a; 5. if(strcmp(b, a) == 0) // 不能用 if (b == a) 6. … 7. // 指针… 8. int len = strlen(a); 9. char *p = (char *)malloc(sizeof(char)*(len 1)); 10. strcpy(p,a); // 不要用 p = a; 11. if(strcmp(p, a) == 0) // 不要用 if (p == a) 12. … 复制代码 示例3.2 数组和指针的内容复制与比较 1. char a[] = "hello world"; 2. char *p = a; 3. cout<< sizeof(a) << endl; // 12字节 4. cout<< sizeof(p) << endl; // 4字节 复制代码 示例3.3(a) 计算数组和指针的内存容量 1. void Func(char a[100]) 2. { 3. cout<< sizeof(a) << endl; // 4字节而不是100字节 4. } 复制代码 示例3.3(b) 数组退化为指针 1. void GetMemory(char *p, int num) 2. { 3. p = (char *)malloc(sizeof(char) * num); 4. } 5. void Test(void) 6. { 7. char *str = NULL; 8. GetMemory(str, 100); // str 仍然为 NULL 9. strcpy(str, "hello"); // 运行错误 10. } 复制代码 示例4.1 试图用指针参数申请动态内存 1. void GetMemory2(char **p, int num) 2. { 3. *p = (char *)malloc(sizeof(char) * num); 4. } 5. void Test2(void) 6. { 7. char *str = NULL; 8. GetMemory2(&str, 100); // 注意参数是 &str,而不是str 9. strcpy(str, "hello"); 10. cout<< str << endl; 11. free(str); 12. } 复制代码 示例4.2用指向指针的指针申请动态内存 1. char *GetMemory3(int num) 2. { 3. char *p = (char *)malloc(sizeof(char) * num); 4. return p; 5. } 6. void Test3(void) 7. { 8. char *str = NULL; 9. str = GetMemory3(100); 10. strcpy(str, "hello"); 11. cout<< str << endl; 12. free(str); 13. } 复制代码 示例4.3 用函数返回值来传递动态内存 1. char *GetString(void) 2. { 3. char p[] = "hello world"; 4. return p; // 编译器将提出警告 5. } 6. void Test4(void) 7. { 8. char *str = NULL; 9. str = GetString(); // str 的内容是垃圾 10. cout<< str << endl; 11. } 复制代码 示例4.4 return语句返回指向“栈内存”的指针 1. char *GetString2(void) 2. { 3. char *p = "hello world"; 4. return p; 5. } 6. void Test5(void) 7. { 8. char *str = NULL; 9. str = GetString2(); 10. cout<< str << endl; 11. } 复制代码 示例4.5 return语句返回常量字符串 1. char *p = NULL; 2. char *str = (char *) malloc(100); 复制代码 (2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。 1. class A 2. { 3. public: 4. void Func(void){ cout << “Func of class A” << endl; } 5. }; 6. void Test(void) 7. { 8. A *p; 9. { 10. A a; 11. p = &a; // 注意 a 的生命期 12. } 13. p->Func(); // p是“野指针” 14. } 复制代码 函数Test在执行语句p->Func()时,对象a已经消失,而p是指向a的,所以p就成了“野指针”。但奇怪的是我运行这个程序时居然没有出错,这可能与编译器有关。 1. class Obj 2. { 3. public : 4. Obj(void){ cout << “Initialization” << endl; } 5. ~Obj(void){ cout << “Destroy” << endl; } 6. void Initialize(void){ cout << “Initialization” << endl; } 7. void Destroy(void){ cout << “Destroy” << endl; } 8. }; 9. void UseMallocFree(void) 10. { 11. Obj *a = (obj *)malloc(sizeof(obj)); // 申请动态内存 12. a->Initialize(); // 初始化 13. //… 14. a->Destroy(); // 清除工作 15. free(a); // 释放内存 16. } 17. void UseNewDelete(void) 18. { 19. Obj *a = new Obj; // 申请动态内存并且初始化 20. //… 21. delete a; // 清除并且释放内存 22. } 复制代码 示例6 用malloc/free和new/delete如何实现对象的动态内存管理 1. void Func(void) 2. { 3. A *a = new A; 4. if(a == NULL) 5. { 6. return; 7. } 8. … 9. } 复制代码 (2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。例如: 1. void Func(void) 2. { 3. A *a = new A; 4. if(a == NULL) 5. { 6. cout << “Memory Exhausted” << endl; 7. exit(1); 8. } 9. … 10. } 复制代码 (3)为new和malloc设置异常处理函数。例如Visual C 可以用_set_new_hander函数为new设置用户自己定义的异常处理函数,也可以让malloc享用与new相同的异常处理函数。详细内容请参考C 使用手册。 1. void main(void) 2. { 3. float *p = NULL; 4. while(TRUE) 5. { 6. p = new float[1000000]; 7. cout << “eat memory” << endl; 8. if(p==NULL) 9. exit(1); 10. } 11. } 复制代码 示例7试图耗尽操作系统的内存 1. void * malloc(size_t size); 复制代码 用malloc申请一块长度为length的整数类型的内存,程序如下: 1. int *p = (int *) malloc(sizeof(int) * length); 复制代码 我们应当把注意力集中在两个要素上:“类型转换”和“sizeof”。 1. cout << sizeof(char) << endl; 2. cout << sizeof(int) << endl; 3. cout << sizeof(unsigned int) << endl; 4. cout << sizeof(long) << endl; 5. cout << sizeof(unsigned long) << endl; 6. cout << sizeof(float) << endl; 7. cout << sizeof(double) << endl; 8. cout << sizeof(void *) << endl; 复制代码 在malloc的“()”中使用sizeof运算符是良好的风格,但要当心有时我们会昏了头,写出 p = malloc(sizeof(p))这样的程序来。 1. void free( void * memblock ); 复制代码 为什么free 函数不象malloc函数那样复杂呢?这是因为指针p的类型以及它所指的内存的容量事先都是知道的,语句free(p)能正确地释放内存。如果p是 NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致程序运行错误。 1. int *p1 = (int *)malloc(sizeof(int) * length); 2. int *p2 = new int[length]; 复制代码 这是因为new内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而言,new在创建动态对象的同时完成了初始化工作。如果对象有多个构造函数,那么new的语句也可以有多种形式。例如 1. class Obj 2. { 3. public : 4. Obj(void); // 无参数的构造函数 5. Obj(int x); // 带一个参数的构造函数 6. … 7. } 8. void Test(void) 9. { 10. Obj *a = new Obj; 11. Obj *b = new Obj(1); // 初值为1 12. … 13. delete a; 14. delete b; 15. } 复制代码 如果用new创建对象数组,那么只能使用对象的无参数构造函数。例如 1. Obj *objects = new Obj[100]; // 创建100个动态对象 复制代码 不能写成 1. Obj *objects = new Obj[100](1);// 创建100个动态对象的同时赋初值1 复制代码 在用delete释放对象数组时,留意不要丢了符号‘[]’。例如 1. delete []objects; // 正确的用法 2. delete objects; // 错误的用法 复制代码 后者相当于delete objects[0],漏掉了另外99个对象。 |
|
修改const
对与有const修饰的变量可以通过间接的方式修改。
对于指针可以强制通过强制转换指针的方式来修改指针所指向的内容。
通过其他指针也可以修改。
|
|