1.const并不真正表示常量。只是被修饰的变量无法赋值。所以程序虽然不能修改,但是硬件或其他方法上可以修改。
2.关键字volatile 有什么含意?并给出三个不同的例子。
一个定义为volatile 的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的
值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存
在寄存器里的备份。下面是volatile 变量的几个例子:
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量
2.extern作用:&static
extern:定义时可被外部文件extern调用,但是一个工程里只能出现一次。声明时,说明此变量或者函数是外部文件的,可出现多次,一般在头文件中引用。
换句话说,你要想使用其他文件的某个extern修饰的变量或函数,只要在本文件extern声明即可。
特别注意的是结构体extern:
在源文件中定义结构体并定义了变量,在其他文件申明此变量是错误的。故使用结构体时,最好将定义放在头文件中并定义(一般头文件没有定义)其他文件extern申明。
为什么?
因为当你在一个文件里定义了结构体后,其他文件申明后使用,编译器不清楚此结构体内部,故要求再定义一次(结构体定义不占内存)。所以最好将定义写道头文件中,要使用就include此头文件。
static:定义时,此变量或函数只可被本文件使用。
针对全局变量来说,仅仅是改变了作用域,数据放置在全局静态内存区。针对局部变量来说,将气从堆栈移至静态内存区,但作用域还是局部的。这样这个变量在程序结束前(而不是函数结束前)不会消失,但是作用域没变。
总结:1.隐藏全局可见 2.保持持久存在 3.初始化变量。
3.switch语句容易发生fall through现象,即case后语句执行完毕后如果没有break则会依次执行下一个case。
4.break:容易出现错误,到底是中断了哪个。break是跳出最近的循环或switch语句,而不包括for语句。
5.函数一般都默认缺省为extern全局可见,若不想全局可见加static(如一个函数使用了一些对他来说才使用的内部函数,但是C语言不允许“内部函数”,则只能定义在函数外,但是很多时候忘记加static,故也全局可见了)。
Linux系统下用C语言编写2D图形游戏
[日期:2012-01-23] | 来源:Linux社区 作者:liuchao | [字体:大 中 小] |
在Linux系统下,不要以为C语言就只能写那种只有字符的控制台程序,别忘了,Linux系统有FrameBuffer(帧缓冲),只要显示器是彩色的,并且是linux系统的,就可以用C语言代码通过读写FrameBuffer里的数据在屏幕上绘制图形;
图形嘛,有png库,用它的函数解码图片文件,得到图片数组,共有red、green、blue、alpha四种数组,alpha用于图片之间的组合,最终将RGB数组输出到FrameBuffer就能显示了;
想要动态图形效果,自己用算法实现。
目前,我正在为自己的嵌入式设备开发一个游戏,图形素材来源于互联网,游戏截图如下图所示:
如上图所示,主菜单中的红色光标有闪烁效果,游戏画面切换有淡入淡出效果。
下面有源码,现在使用的按键控制方法的效果不理想。
linux系统环境,按键判断,使用了getch()和kbhit()函数,函数是模拟实现的。
为了判断按键是否为按住状态,我使用一个变量count来计数,每循环一次自增,也就是按键检测延迟的最大时间,超过了这段时间,如果getch()函数没有再次返回接受到同样的按键的键值,那么就判断为这个按键已经释放,否则,该按键处于按住状态,并继续显示之前的动作。
还有图形显示,目前没掌握局部刷新的技术,游戏显示的每一帧图形都是全屏刷新的,效率低,每秒大概刷新4帧。
源码只提供部分:
- void next_frames(void)
- {//更新到下一帧
- frames[0] += speed[0];//玩家1的
- frames[1] += speed[1];//AI的
- }
- void init_player_data(void)
- //初始化各个玩家的数据
- {
- direction[0] = IS_RIGHT;//玩家朝向右边
- direction[1] = IS_LEFT;//AI角色朝向左边
- //各个角色的初始位置
- map_site_x[0] = 100;
- map_site_x[1] = 300;
- map_site_y[1] = map_hight - 20;
- map_site_y[0] = map_hight - 20;//在地图上的坐标
- speed[0] = 1;//每次循环帧frames自增的的值,之前是想做能设置游戏刷新速度的功能,现在已抛弃,这个变量貌似没用了。
- speed[1] = 1;
- need_move[0] = 0;//玩家是否需要移动
- need_move[1] = 0;//AI是否需要移动
- can_use_next[0] = 0;//可以使用下一个动作
- can_use_next[1] = 0;//AI可以使用下一个动作
- status[0] = STANCE;//玩家为站立状态
- status[1] = STANCE;//AI为站立状态
- shock_wave_num = 0;//冲击波的总数为0
- need_view_shock_wave = 0;//不需要显示冲击波
- }
- int play_game(void)
- {
- unsigned char **game_map;//游戏地图
- int map_id,temp_key = 0,count = 0,key = 0,man_id;//人物代号
- //分配内存
- man_id = ICHIGO;//人物动画选择黑崎一护
- init_player_data();//初始化玩家数据
- map_id = 1;
- if(map_id == 1) {
- game_map = load_map_1();//载入地图数据
- }
- while(1){
- update_graph();//规定镜头,显示玩家所在的区域的图形
- view_map_area(game_map[0],game_map[1],game_map[2]);//根据主角的位置,显示地图中的某个区域
- if(kbhit()){//调用kbhit()函数检测是否有按键输入
- key = getch();//有按键输入就用getch()函数获取键值并赋给key,下面开始判断key的值
- if(key == KEY_BACK) break;
- if(key == 'w' || key == 'W' || key == KEY_UP) {
- temp_key = key;
- }
- else if((key == 'd' || key == 'D' || key == KEY_RIGHT) && status[0] != JUMP_ATTACK){//如果按的是a键或者左键
- if((temp_key == 'd' || temp_key == 'D' || temp_key == KEY_RIGHT)&& count < 5) {
- next_action[0] = WALK;//下一个动作还是行走
- }
- else if(can_use_next[0] == 0){//如果之前没有按过a键或者左键,并且,可以使用下一个动作
- if(temp_status[0] != JUMP) {//如果之前不是跳跃状态
- status[0] = WALK;
- if(temp_status[0] != WALK) frames[0] = 0;//如果之前不是行走状态
- else if(temp_status[0] == WALK){//如果之前是行走状态
- next_action[0] = WALK;//下一个动作还是行走
- }
- }
- else need_move[0] = 1;//否则,需要移动
- }
- direction[0] = IS_RIGHT;
- need_move[0] = 1;
- temp_key = key;
- count = 0;//计数清零
- }
- else if((key == 'a' || key == 'A' || key == KEY_LEFT) && status[0] != JUMP_ATTACK){//如果按的是a键或者左键
- if((temp_key == 'a' || temp_key == 'A' || temp_key == KEY_LEFT)&& count < 5) {
- next_action[0] = WALK;//下一个动作还是行走
- }
- else if(can_use_next[0] == 0){//如果之前没有按过a键或者左键,并且,可以使用下一个动作
- if(temp_status[0] != JUMP) {//如果之前不是跳跃状态
- status[0] = WALK;
- if(temp_status[0] != WALK) frames[0] = 0;//如果之前不是行走状态
- else if(temp_status[0] == WALK){//如果之前是行走状态
- next_action[0] = WALK;//下一个动作还是行走
- }
- }
- else need_move[0] = 1;//否则,需要移动
- }
- direction[0] = IS_LEFT;
- need_move[0] = 1;
- temp_key = key;
- count = 0;//计数清零
- }
- else if(key == 's' || key == 'S' || key == KEY_DOWN){//如果按的是s键或者是下键
- if(can_use_next[0] == 0){
- status[0] = BLOCK;
- if((temp_key == 's' || temp_key == 'S' || temp_key == KEY_DOWN) && count <10){
- //如果之前按过s键或者是下键,并且在最大延迟时间内
- if(temp_status[0] == BLOCK && status[0] == BLOCK)//如果之前的动作是防御
- {
- frames[0] = 2;speed[0] = 0;
- }
- }
- else {
- frames[0] = 0;//帧数归零www.linuxidc.com
- speed[0] = 1;
- }
- }
- temp_key = key;//保存按键的键值
- count = 0;//计数归零
- }
- else if(key == 'j' || key == 'J'){//如果按的是j键
- if(temp_status[0] == JUMP || temp_status[0] == JUMP_ATTACK)//如果之前还处于跳跃状态
- {
- if(jump_attack_num == 0) {
- status[0] = JUMP_ATTACK;//跳跃攻击
- jump_attack_num = 1;
- }
- }
- else if(status[0] == DOWN_ATTACK || status[0] == BLOCK || temp_key == KEY_DOWN || temp_key == 's' || temp_key == 'S'){
- status[0] = DOWN_ATTACK;
- if(temp_status[0] != DOWN_ATTACK) frames[0] = 0;//帧数归零
- }
- else if(temp_key == KEY_UP || temp_key == 'w' || temp_key == 'W'){
- if(can_use_next[0] == 0){//如果可以使用下一个动作
- status[0] = UP_ATTACK;
- frames[0] = 0;//帧数归零
- }
- }
- else if((temp_key == 'j' || temp_key == 'J') && count <5){//如果之前按过j键,并且在最大延迟时间内
- if(temp_status[0] == FIRST_ATTACK && status[0] == FIRST_ATTACK)//如果之前处于第一段攻击状态下
- {
- status[0] = FIRST_ATTACK;
- if(can_use_next[0] == 0 && first_attack_complete[0] == 0){//如果可以使用下一个动作
- status[0] = SECOND_ATTACK;//开始进行第二段攻击
- frames[0] = 0;//帧数归零www.linuxidc.com
- }
- else{
- next_action[0] = SECOND_ATTACK;//保存下一个动作,等待之前的动作完成
- }
- }
- else if(temp_status[0] == SECOND_ATTACK || status[0] == SECOND_ATTACK)//如果之前处于第二段攻击状态下
- {
- status[0] = SECOND_ATTACK;
- if(can_use_next[0] == 0 && second_attack_complete[0] == 0){//如果可以使用下一个动作
- status[0] = THIRD_ATTACK;//开始进行第二段攻击
- frames[0] = 0;//帧数归零
- }
- else{
- next_action[0] = THIRD_ATTACK;//保存下一个动作,等待之前的动作完成
- }
- }
- else if(temp_status[0] == THIRD_ATTACK || status[0] == THIRD_ATTACK)//如果之前处于第二段攻击状态下
- {
- status[0] = THIRD_ATTACK;
- if(can_use_next[0] == 0 && third_attack_complete[0] == 0){//如果可以使用下一个动作
- status[0] = FIRST_ATTACK;//开始进行第一段攻击
- frames[0] = 0;//帧数归零
- }
- else{
- //next_action = FIRST_ATTACK;//保存下一个动作,等待之前的动作完成
- }
- }
- else{
- status[0] = FIRST_ATTACK;
- frames[0] = 0;
- }
- }
- else {
- status[0] = FIRST_ATTACK;
- if(temp_status[0] != FIRST_ATTACK) frames[0] = 0;
- }
- speed[0] = 1;
- count = 0;
- temp_key = key;//保存按键的键值
- }
- else if((key == 'k' || key == 'K') && can_use_next[0] == 0){//如果按的是k键,并且能使用下一个动作
- status[0] = JUMP;
- count = 0;
- jump_attack_num = 0;
- frames[0] = 0;
- }
- else if(key == 'U' || key == 'u'){
- if(status[0] == DOWN_Y_ATTACK || status[0] == BLOCK || temp_key == KEY_DOWN || temp_key == 's' || temp_key == 'S'){
- speed[0] = 1;
- status[0] = DOWN_Y_ATTACK;
- if(temp_status[0] != DOWN_Y_ATTACK) frames[0] = 0;//帧数归零
- }
- else if(can_use_next[0] == 0){//如果可以使用下一个动作
- status[0] = Y_ATTACK;//开始进行第二段攻击
- frames[0] = 0;//帧数归零
- }
- else{
- next_action[0] = Y_ATTACK;//保存下一个动作,等待之前的动作完成
- }
- count = 0;
- temp_key = key;
- }
- else if(key == 'L' || key == 'l'){
- if(can_use_next[0] == 0){//如果可以使用下一个动作
- status[0] = DASH;
- frames[0] = 0;//帧数归零
- }
- else{
- next_action[0] = DASH;//保存下一个动作,等待之前的动作完成
- }
- need_move[0] = 1;//需要移动
- }
- }
- else if(count > 5 && can_use_next[0] == 0) {//如果在计数大于5时还没有接收到按键输入,并且可以使用下一个动作
- status[0] = STANCE;//状态改为站立
- speed[0] = 1;
- temp_key = 0;
- count = 0;//计数清零www.linuxidc.com
- }
- if_need_move(0,count);//判断是否需要移动
- if(man_id == ICHIGO) ichigo(0);//使用黑崎一护的动作图形
- ichigo(1);
- next_frames();//更新到下一个帧
- write_to_fb(screen[0],screen[1],screen[2]);//显示图形
- //usleep(10000);//之前用过usleep(30000),牺牲帧的刷新速度,获得按键响应效果的提升,我认为这不值
- count++;//计数自增
- }
- //释放背景图占用的内存
- free(game_map[0]);
- free(game_map[1]);
- free(game_map[2]);
- free(game_map);
- return 0;
- }
- int main()
- {
- init_game();//初始化游戏,获取屏幕尺寸,分配内存
- game_boot();//显示游戏启动画面
- main_menu();//显示游戏主菜单
- system("stty echo");
- return 0;
- }
![linux](https://i-blog.csdnimg.cn/blog_migrate/23f987522c5046c33c7069971df59c81.gif)
6.strlen与sizeof区别
strlen取字符串长度即字符数量,会去掉结尾符'\0'。故用malloc获取空间存放字符串时要用malloc(strlen(str)+1)。
sizeof取的是内存空间存储大小。占用大小。
如:
p为一个字符串指针。strlen(p)代表从p指向地址到遇到'\0'间遇到的字符的数量。sizeof(p)仅表示p这个变量存储的地址的长度(跟操作系统有关)。
p[n]为字符串数组。strlen(p)等于m(m<=n),字符串在其中占多少位。sizeof(p)表示的p的地址空间大小等于n。
故字符串中一般用strlen好一些。
待续。。。。