C语言阶段性总结(一)
坦诚,是获取进步的前提。
——Some one
老实说,对于C语言,我很想把它学好,但在学习的开始,用错了方法,以为读下来就可以,后面才发现,这种不过脑子的学习方式能够带给自己的提升,少之又少。而现在马上就要实验班考核,考核结束之后还有一大堆之前埋下的“坑”等着去填。似乎C语言是时候告一个小小的段落了。
写这篇文章,主要是为了总结一下自己这半年的所学所得,以及经验和教训。
希望下一次的启程能够更加通畅一点。
一、 C语言带给我关于底层的思考
实话实说,学习C语言挺枯燥的,似乎只有在眼前的难题被解决了之后的那一小段时间才能感受到一点点的愉悦感。与此同时,所处的环境,这个社会,也愈来愈浮躁,似乎这些所谓的基础,变成了一件可笑的事情。唉,算了,不废话了,切入正题。
学习C语言的主要动机是,它能让人更深地理解计算机。俗话说“机器生汇编,汇编生A,A生B,B生C,C生万物”。C语言现在已然成为了晦涩难懂的汇编语言与功能强大但缺乏灵活性的高级语言之间的桥梁。
它让我理解了内存的结构,让我了解到计算机的“思维方式”。结构体、指针、链表、句柄,真的是一件蛮神奇的事情。
创新,是一件在对底层知识充分了解后的组装。或许,我不会是亲手创造的那个人,但至少,我觉得我会尊重他们。
二、CPP章节总结
将自己学习到的语法框架罗列出来,以防遗忘:
第一章:
① C的特性:高效、可移植、面向程序员。
② C有多个标准,C99/C11,但不同编辑器对于标准的支持不尽相同。就现在的分析来看,在windows系统上进行编程时VS似乎具有一些独特优势。
第二章:
① 基础语法,计算机的识别方式,比如2x不代表2乘x;
② 关键字和保留标识符,C语言内置了一些识别特殊词汇的机制,并拒绝程序员重新定义这些关键字。详细:p34
第三章:
① 不同编译器对于相同的数据类型解析出的占位也不同
② 不同进制有不同的数据表达
③ 计算机有计算机的缺点,实数的储存,往往带来精度问题。同样是乘和除,先乘后除的精确对就会比之前要高。
第四章:
① 字符串在内存中的储存和读取方式比较特殊,末尾会有一个’\0’字符,这也衍生了很多安全性问题。
② 字符串操作函数,都将以指针作为参数传递。数组名为一个指针常量。
第五章:
① 运算符优先级
第六章:
① 循环,让计算机去做重复的事情,这是编程的力量源泉。
第七章:
① 分支与跳转中,各有各的优势,if else、continue break、switch、index goto;
第八章:
① 缓冲区概念,getch和getchar的区别分析。
② 利用缓冲区提升用户体验。
第九章:
① 合理使用函数优化程序结构,减少重复代码。
② 不同操作系统对于多源代码文件的编译流程也不相同。
第十章:
① 数组与指针之间的联系。
② 变长数组(虽然没用过……)
第十一章:
① 字符串操作不当,会带来很多安全问题。
② 善用VS中的F1(逃……)
第十二章:
① 只是简单了解了一下变量及函数作用域的问题,还没有编写过多文件程序,之后编写的时候,可能需要更进一步的深入。
第十三章:
① 第一个定义的文件型指针,可以帮助理解复杂的数据体。
第十四章:
① 结构体相关优化,下学期数据结构应该有很大用。
第十五章:
① 位操作,只学会了简单的(逃……)
第十六章:
① 善用F1。
第十七章:
① 二叉树,下学期的开门砖。
三、日常学习碎片
一、考试过程中得到的一些琐碎的知识点:
1、任何字符都可以用转义形式给出,\后面1-3位8进制,\x后面两位十六进制,""包裹的是字符串,''包裹的才是字符 http://c.biancheng.net/cpp/html/22.html 2、标识符由大小写字母和_和数字组成,定义时不能与标准字冲突(C语言识别大小写),首字符不能为数字 3、C语言关键字:http://baike.baidu.com/item/C%E8%AF%AD%E8%A8%80%E5%85%B3%E9%94%AE%E5%AD%97(没有include) 4、实数的合法表达:http://c.biancheng.net/cpp/html/21.html其中指数形式中,阶码(e或E)后面必须跟整数, .123被视为0.123 5、逗号运算符是指,对多个运算语句进行求值,并将最后一个分表达式当做整个表达式的值。 6、运算符优先级:http://www.slyar.com/blog/c-operator-priority.html 7、没有return就没有返回值
小结:每一个编程语言都会有自己的语法特性,这些特性对于安全研究员来说,尤其重要。
二、static 和 const
static: static的作用为“更改声明周期,更改作用域” 应用到局部变量时,该局部变量的内存空间不会因函数或代码块的结束而结束,而是继续存在,但依然只能在函数或代码块中使用。 作用到全局变量或函数中时,声明了其作用域,及不允许在其它文件中直接使用该变量或函数,若要使用,前面需要加上extern说明,这一点特性在不同文件需要使用相同函数或变量名的时候比较有用。
const: const的作用为声明常量,其它的没什么好注意的,对于指针来说(以字符指针为例),char * const p; 指的是指针变量本身不能变;而 char const * p;指的就是指针所指向的数据不能变; http://www.cnblogs.com/hellocby/p/3543989.html
三、为什么不能void main
首先就是,return 0; 其实是在传递给程序调用者(如操作系统)程序运行的状态信息; 并且有些编译器并不会自动加上return 0,所以出于兼容性考虑,也应该加上return 0; 使用int 返回值,还可以通过在头文件中定义错误代码来帮助操作系统了解程序运行状态。
四、数据类型占用的储存空间
随着编译器位数不一样,相同数据类型所占的位数也会有所不同。 还没确定编译器的孩子伤不起……
整型 [signed]int -2147483648~+2147483648 长双精度 long double 1.7 x 10^(-308)~ 1.7 x 10^(+308)
16位编译器 double:8个字节
|
四、图形界面知识小结
不清楚是还没有入门,还是其它什么原因,总感觉C语言没有一个很好的路线指导。
在用的EsayX图形库,也不清楚是否适合C语言图形编程。报着“多学无坏处”的心态,将这两天的学习体会写一下:
1、一个项目可以由多个CPP组成,多个项目组成一个工作区。(早该掌握的,到现在才知道,惭愧……)
2、入门地使用库函数:
#include <graphics.h> #include <conio.h> void main() { initgraph(640, 480); line(200, 240, 440, 240); line(320, 120, 320, 360); getch(); closegraph(); }
3、常用的一些画图函数
line(x1,y1,x2,y2); //画直线(x1,y1)-(x2,y2),都是整形,单位为像素 circle(x,y,r); putpixel(x,y,c);画点,c为颜色
4、画图常用的颜色常量
BLACK 黑 DARKGRAY 深灰
BLUE 蓝 LIGHTBLUE 亮蓝
GREEN 绿 LIGHTGREEN 亮绿
CYAN 青 LIGHTCYAN 亮青
RED 红 LIGHTRED 亮红
MAGENTA 紫 LIGHTMAGENTA 亮紫
BROWN 棕 YELLOW 黄
LIGHTGRAY 浅灰 WHITE 白
也可以使用RGB(r, g, b)表示颜色
5、利用循环以及其它算法用简单的画图函数画出美观的图形
比如我喜欢这个图形
#include <graphics.h>
#include <conio.h>
#include <math.h>
#define PI 3.14
void main()
{
initgraph(640, 480);
int c;
double a;
int x, y, r = 200;
for(a = 0; a < PI * 2; a += 0.0001)
{
x=(int)(r * cos(a) + 320 + 0.5);
y=(int)(r * sin(a) + 240 + 0.5);
c=(int)(a * 255 / (2 * PI) + 0.5);
setlinecolor(RGB(c, 0, 0));
line(320, 240, x, y);
}
getch();
closegraph();
}
数学在图形编程中应用很广啊~
6、可以用黑色盖住之前的颜色
7、善用自定义函数减少工作量
8、位运算的应用,话说,还不知道XOR怎么关闭,不然就给贪吃蛇加上时间了~
#include <graphics.h> #include <conio.h> #include <math.h> #define PI 3.14159265359 void Draw(int hour, int minute, int second) { double a_hour, a_min, a_sec; // 时、分、秒针的弧度值 int x_hour, y_hour, x_min, y_min, x_sec, y_sec; // 时、分、秒针的末端位置 // 计算时、分、秒针的弧度值 a_sec = second * 2 * PI / 60; a_min = minute * 2 * PI / 60 + a_sec / 60; a_hour= hour * 2 * PI / 12 + a_min / 12; // 计算时、分、秒针的末端位置 x_sec = 320 + (int)(120 * sin(a_sec)); y_sec = 240 - (int)(120 * cos(a_sec)); x_min = 320 + (int)(100 * sin(a_min)); y_min = 240 - (int)(100 * cos(a_min)); x_hour= 320 + (int)(70 * sin(a_hour)); y_hour= 240 - (int)(70 * cos(a_hour)); // 画时针 setlinestyle(PS_SOLID, 10, NULL); setlinecolor(WHITE); line(320, 240, x_hour, y_hour); // 画分针 setlinestyle(PS_SOLID, 6, NULL); setlinecolor(LIGHTGRAY); line(320, 240, x_min, y_min); // 画秒针 setlinestyle(PS_SOLID, 2, NULL); setlinecolor(RED); line(320, 240, x_sec, y_sec); } void main() { initgraph(640, 480); // 初始化 640 x 480 的绘图窗口 // 绘制一个简单的表盘 circle(320, 240, 2); circle(320, 240, 60); circle(320, 240, 160); outtextxy(296, 310, _T("BestAns")); // 设置 XOR 绘图模式 setwritemode(R2_XORPEN); // 设置 XOR 绘图模式 // 绘制表针 SYSTEMTIME ti; // 定义变量保存当前时间 while(!kbhit()) // 按任意键退出钟表程序 { GetLocalTime(&ti); // 获取当前时间 Draw(ti.wHour, ti.wMinute, ti.wSecond); // 画表针 Sleep(1000); // 延时 1 秒 Draw(ti.wHour, ti.wMinute, ti.wSecond); // 擦表针(擦表针和画表针的过程是一样的) } closegraph(); // 关闭绘图窗口 }
9、鼠标控制
不同的获取按键信息方式: 获取按键: 获取鼠标: char c; MOUSEMSG m; if (kbhit()) if (MouseHit()) c = getch(); m = GetMouseMsg(); MOUSEMSG 结构体介绍 struct MOUSEMSG { UINT uMsg; // 当前鼠标消息 bool mkCtrl; // Ctrl 键是否按下 bool mkShift; // Shift 键是否按下 bool mkLButton; // 鼠标左键是否按下 bool mkMButton; // 鼠标中键是否按下 bool mkRButton; // 鼠标右键是否按下 int x; // 当前鼠标 x 坐标 int y; // 当前鼠标 y 坐标 int wheel; // 鼠标滚轮滚动值 }; 鼠标状态 WM_MOUSEMOVE 鼠标移动消息 WM_MOUSEWHEEL 鼠标滚轮拨动消息 WM_LBUTTONDOWN 左键按下消息 WM_LBUTTONUP 左键弹起消息 WM_LBUTTONDBLCLK 左键双击消息 WM_MBUTTONDOWN 中键按下消息 WM_MBUTTONUP 中键弹起消息 WM_MBUTTONDBLCLK 中键双击消息 WM_RBUTTONDOWN 右键按下消息 WM_RBUTTONUP 右键弹起消息 WM_RBUTTONDBLCLK 右键双击消息
10、加载背景图片
#include <graphics.h> #include <conio.h> void main() { initgraph(640, 480); IMAGE img; // 定义 IMAGE 对象 loadimage(&img, "C:\\test.jpg"); // 读取图片到 img 对象中 putimage(0, 0, &img); // 在坐标 (0, 0) 位置显示 IMAGE 对象 getch(); closegraph(); }
11、移动复杂图形
#include <graphics.h> #include <conio.h> void main() { initgraph(640, 480); // 定义 IMAGE 对象 IMAGE img; // 绘制内容 circle(100, 100, 20); line(70, 100, 130, 100); line(100, 70, 100, 130); // 保存区域至 img 对象 getimage(&img, 70, 70, 60, 60); // 将 img 对对象显示在屏幕的某个位置 putimage(200, 200, &img); getch(); closegraph(); }
12、利用位操作底片化照片#include <graphics.h>#include <conio.h>
void main() { initgraph(640, 480); // 读取图片 loadimage(NULL, "c:\\test.jpg"); int c; for(int x=0; x<320; x++) for(int y=0; y<480; y++) { c = getpixel(x, y); c = (0xff0000 - (c & 0xff0000)) | (0x00ff00 - (c & 0x00ff00)) | (0x0000ff - (c & 0x0000ff)); putpixel(x, y, c); } getch(); closegraph(); }
PS:可以自己利用灰度算法实现彩色照片复现程序哦~
13、通过操作句柄,更近一步进行操作,设计到windows编程,"坑~"
五、我的贪吃蛇
结构体设计参照网上一篇文章,使用数组以及黑框覆盖,即可模拟贪吃蛇体验,不需要使用链表(看来还是不熟练)。增加了:
① 良好介绍性界面:
② 简洁、美观的图形窗口:
③ 热血的背景音乐:
④ 无障碍处理任何输入:
⑤ 不同颜色转换:
⑥ 不同类食物带来的特效加成:
⑦ 重新开始:
等功能,算是一个自己比较满意的作品。
代码如下:
snake.h头文件
#define WIDTH 480 #define HEIGHT 480 #define ACTUAL_WIDTH 1200 #define GAME_WIN 555 #define BIT_SIZE 20 int begin_x,begin_y; int max_x,max_y; int had_key = 0,no_re = 0; typedef enum { left, right, up, down }D_IRECTION; typedef struct { int x; int y; }Coordinate; struct FOOD { int flag; //食物的状态,1表示没吃,0表示吃了 Coordinate crd; }food; struct SNAKE { int body_num; Coordinate body[GAME_WIN]; D_IRECTION D_IRECTION; //蛇头方向 }snake; void eat_food(); void snake_move(); void game_over(); void print_nake(); void keyboard(); void init(); void set_food(); void print_food(); void judge_impact();
#include <stdlib.h> #include <graphics.h> #include <conio.h> #include <time.h> #include <math.h> #include <stdio.h> #include <Windows.h> #include <mmsystem.h> #pragma comment(lib, "WINMM.LIB") #include "snake.h" int main(void) { init(); PlaySoundA( "Breath.wav", NULL, SND_FILENAME|SND_ASYNC );//参数一为文件名,参数二为应用程序句柄,参数三为模式(异步,文件名) while(1) { if_win(); while(!kbhit() || no_re)//no_re防止陷入死循环 { if (!food.flag) { set_magic(); set_food(); } print_food(); print_nake(); snake_move(); judge_impact(); eat_food(); } keyboard(); } getch(); return 0; } void if_win() { int x; char c; if(snake.body_num >= GAME_WIN) { settextstyle(40,40,_T("宋体")); x = (max_x-textwidth("GAME,WIN!!!"))/2; outtextxy(x,20,_T("GAME,WIN!!!")); settextstyle(20,10,_T("宋体")); x = (max_x-textwidth("输入R重新开始,输入ESC键结束程序;"))/2; outtextxy(x+260,560,_T("输入R重新开始,输入ESC键结束程序;")); settextstyle(40,20,_T("宋体")); x = (max_x-textwidth("你已经可以拯救世界了,带上你的绿帽子,踏上新的征程吧!"))/2; outtextxy(x,260,_T("你已经可以拯救世界了,带上你的绿帽子,踏上新的征程吧!")); while(c = getch()) { if(c == 82) { main(); }else if(c == 27) { break; } } closegraph(); } } void init() { int c, m, color; double a; int x, y; int r = 66, count = 0; initgraph(ACTUAL_WIDTH,HEIGHT+200); max_x = getmaxx(); max_y = getmaxy(); begin_x = 100; begin_y = 100; snake.body_num = 6; srand((unsigned)time(NULL)); snake.body[0].x = begin_x+240; snake.body[0].y = begin_y+180; food.flag = 0; int D_IRECTION; D_IRECTION = rand()%4; switch(D_IRECTION) { case 0: { snake.D_IRECTION=right; break; } case 1: { snake.D_IRECTION=left; break; } case 2: { snake.D_IRECTION=up; break; } case 3: { snake.D_IRECTION=down; break; } } setlinecolor(WHITE); rectangle(begin_x-1,begin_y-1,begin_x+WIDTH+1,begin_y+HEIGHT+1);//画游戏框 settextstyle(20,10,_T("宋体")); outtextxy(700,100,_T("操作说明:")); outtextxy(700,130,_T(" w:上")); outtextxy(700,160,_T(" S:下")); outtextxy(700,190,_T(" A:左")); outtextxy(700,220,_T(" D:右")); outtextxy(700,330,_T("吞食红色食物,获得状态:“狂暴”")); outtextxy(700,360,_T("吞食蓝色食物,获得状态:“冰封”")); outtextxy(700,390,_T("吞食绿色食物,获得状态:“忍辱负重”")); outtextxy(700,420,_T("吞食白色食物,获得状态:“纯洁如我”")); outtextxy(700,450,_T("状态功能请自行探索 ~V~ ")); outtextxy(700,450,_T("坚持555关,即可拯救世界")); outtextxy(710,550,_T("输入任意键,开始游戏:")); for(a = 0; a < PI * 2; a += 0.001) { x=(int)(r * cos(a) + 1000 + 0.5); y=(int)(r * sin(a) + 180 + 0.5); c=(int)(a * 255 / (2 * PI) + 0.5); setlinecolor(RGB(c, 0, 0)); line(1000, 180, x, y); } r = 9; for(m = 9; m <= 1191; m += 2*r) { color = count % 3; for(a = 0; a < PI * 2; a += 0.1) { x=(int)(r * cos(a) + m + 0.5); y=(int)(r * sin(a) + 38 + 0.5); c=(int)(a * 255 / (2 * PI) + 0.5); if(color == 0) { setlinecolor(RGB(c, 0, 0)); }else if(color == 1) { setlinecolor(RGB(0, c, 0)); }else { setlinecolor(RGB(0, 0, c)); } line(m, 38, x, y); } for(a = 0; a < PI * 2; a += 0.1) { x=(int)(r * cos(a) + m + 0.5); y=(int)(r * sin(a) + 638 + 0.5); c=(int)(a * 255 / (2 * PI) + 0.5); if(color == 0) { setlinecolor(RGB(c, 0, 0)); }else if(color == 1) { setlinecolor(RGB(0, c, 0)); }else { setlinecolor(RGB(0, 0, c)); } line(m, 638, x, y); } count++; } getch(); } void set_magic(void) { switch(magic) { case 0: { setlinecolor(WHITE); Time_speed = STD_TIME; break; } case 1: { setlinecolor(RED); Time_speed = STD_TIME/4; break; } case 2: { setlinecolor(BLUE); Time_speed = STD_TIME*2; break; } case 3: { setlinecolor(GREEN); green_food = 1; Time_speed = STD_TIME; break; } } snake_color = magic; } void set_food() { srand((unsigned)time(NULL)); food.crd.x=begin_x+rand()%(WIDTH/BIT_SIZE)*BIT_SIZE; food.crd.y=begin_y+rand()%(HEIGHT/BIT_SIZE)*BIT_SIZE; food.flag=1; count_num = rand(); } void print_food() { i = count_num%4; switch(i) { case 0: { setlinecolor(RED); magic = 1; break; } case 1: { setlinecolor(GREEN); magic = 3; break; } case 2: { setlinecolor(BLUE); magic = 2; break; } case 3: { setlinecolor(WHITE); magic = 0; break; } } rectangle(food.crd.x,food.crd.y,food.crd.x+BIT_SIZE,food.crd.y+BIT_SIZE);//画食物 } void print_nake() { switch(snake_color) { case 0: { setlinecolor(WHITE); break; } case 1: { setlinecolor(RED); break; } case 2: { setlinecolor(BLUE); break; } case 3: { setlinecolor(GREEN); break; } } for (i=snake.body_num-1;i>=0;i--) { rectangle(snake.body[i].x,snake.body[i].y,snake.body[i].x+BIT_SIZE,snake.body[i].y+BIT_SIZE); } setlinecolor(BLACK); rectangle(snake.body[snake.body_num].x,snake.body[snake.body_num].y,snake.body[snake.body_num].x+BIT_SIZE,snake.body[snake.body_num].y+BIT_SIZE); } void snake_move() { int i; for (i=snake.body_num;i>0;i--) { snake.body[i].x=snake.body[i-1].x; snake.body[i].y=snake.body[i-1].y; } switch(snake.D_IRECTION) { case right: { snake.body[0].x+=BIT_SIZE; break; } case left: { snake.body[0].x-=BIT_SIZE; break; } case up: { snake.body[0].y-=BIT_SIZE; break; } case down: { snake.body[0].y+=BIT_SIZE; break; } } had_key = 0; //防止按键冲突 no_re = 0; Sleep(Time_speed); } void judge_impact() { int i; if (snake.body[0].x < begin_x || snake.body[0].x > begin_x+WIDTH-BIT_SIZE || //判断撞击边界 snake.body[0].y < begin_y || snake.body[0].y > begin_y+HEIGHT-BIT_SIZE) { game_over(); } for (i = snake.body_num-1; i>0; i--) //判断撞击身体 { if ( (snake.body[0].x == snake.body[i].x) && (snake.body[0].y==snake.body[i].y) ) { game_over(); } } } void eat_food() { if ( (snake.body[0].x == food.crd.x) && (snake.body[0].y == food.crd.y)) { snake.body_num++; if(green_food) { snake.body_num++; snake.body_num++; green_food = 0; } food_sum++; count_num++; setlinecolor(RED); rectangle(food.crd.x,food.crd.y,food.crd.x+BIT_SIZE,food.crd.y+BIT_SIZE); food.flag = 0; } } void keyboard() { if(!had_key) { switch(getch()) { case 'w': case 'W': { if (snake.D_IRECTION!=down) { snake.D_IRECTION=up; had_key = 1; break; } break; } case 'a': case 'A': { if (snake.D_IRECTION!=right) { snake.D_IRECTION=left; had_key = 1; break; } break; } case 's': case 'S': { if (snake.D_IRECTION!=up) { snake.D_IRECTION=down; had_key = 1; break; } break; } case 'd': case 'D': { if (snake.D_IRECTION!=left) { snake.D_IRECTION=right; had_key = 1; break; } break; } } }else{ no_re = 1; } } void game_over() { int x,y; char c; settextstyle(40,40,_T("宋体")); x = (max_x-textwidth("GAME,OVER!!!"))/2; y = 10; outtextxy(x,y+10,_T("GAME,OVER!!!")); settextstyle(20,10,_T("宋体")); x = (max_x-textwidth("输入R重新开始,输入ESC键结束程序;"))/2; outtextxy(x+260,y+540,_T("输入R重新开始,输入ESC键结束程序;")); while(c = getch()) { if(c == 82) { main(); }else if(c == 27) { break; } } closegraph(); }
六、今后应该如何学习编程
编程是一项实用性,每学习一部分知识,一定要动手操作一下,或许这样速度会慢一些,但只有这样,才能真正学会那些知识。多消耗的时间并不会拖慢你的学习进度,而是会帮助你学得更加踏实和稳健。
不再为赶进度而学习,如果能够做到,这将是我这段时间以来的最大收获。
如果以后有需要: