编写C语言跨平台函数(以清屏和休眠函数为例)

支持C语言的平台有许多,常见的编译器如VC、gcc、Clang等。不同的编译器共同点是都支持标准C(ANSI C),但是各自却又有自己独立的、平台相关的功能以及函数接口。这通常为程序的移植性带来很多问题。这里我简单谈一下解决方案。

常见思路

常见的解决跨平台移植的思路就是利用 宏。不同编译器有各自不同的宏,宏有很多,具体可以参考编译器的相关手册。通过判断一个宏是否存在来选择性的包含头文件或调用函数,其本质就是一种条件编译。

比如一些平台相关的函数,在不同平台要包含不同文件。

#if defined __GNUC__
#include <unistd.h>
#elif defined _MSC_VER
#include <windows.h>
#endif

#if defined 在本例中也可以用#ifdef 替换,但是如果是复杂的条件编译(比如if后面同时判断多个宏),就只能用#if defined 实现了。

__GNUC__是在gcc编译器中定义的宏,_MSC_VER是Windows中VC编译器中的宏。当然了你还可以选择这两个编译器中的其他宏来实现这一功能,只要能确保是两个编译器中平台相关的宏就行。

_MSC_VER这个宏还能判断VC的版本。比如:

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
...

它的意思是如果VC的版本大于VC 6.0,那么...

该宏的详细参考,很容易百度到:

  • MS VC++ 12.0 _MSC_VER = 1800 (Visual C++ 2013)
  • MS VC++ 11.0 _MSC_VER = 1700 (Visual C++ 2012)
  • MS VC++ 10.0 _MSC_VER = 1600(Visual C++ 2010)
  • MS VC++ 9.0 _MSC_VER = 1500
  • MS VC++ 8.0 _MSC_VER = 1400
  • MS VC++ 7.1 _MSC_VER = 1310
  • MS VC++ 7.0 _MSC_VER = 1300
  • MS VC++ 6.0 _MSC_VER = 1200
  • MS VC++ 5.0 _MSC_VER = 1100

跨平台函数的栗子

清屏函数

同样的思路。我们也可以把条件编译用在函数内部。比如要实现控制台的清屏功能,VC中的通常做法是这条语句system("cls");调用控制台的cls命令来清屏。而Linux环境下,给终端清屏是没有cls这一命令的,取而代之的是clear命令。那么如何实现跨平台的清屏功能呢?很简单,依样画葫芦:

void clear()
{
    #ifdef __GNUC__
    system("clear");
    #elif defined _MSC_VER
    system("cls");
    #endif
}

这样就有了跨平台的清屏函数clear。但是其实关键都是system函数,两个平台都提供这个函数(system是标准 C里面的函数)。所以可以再改一版。

#if defined __GNUC__
char *clear = "clear";
#elif defined _MSC_VER
char *clear = "cls";
#endif
...
//当需要清屏的时候
system(clear);

休眠函数

无论是gcc还是VC都有休眠函数,不过具体函数接口却不同。

编译器 头文件 函数名 参数类型 描述
VC windows.h Sleep unsigned 参数为要休眠的毫秒数
gcc unistd.h sleep unsigned 参数为要休眠的秒数
gcc unistd.h usleep unsigned 参数为要休眠的微秒数

可见,两种环境下的休眠函数的函数名并不相同。并且gcc的两个sleep函数并没有没有提供毫秒级的分辨率。也就是说,比如要休眠200毫秒:

  • Windows中:Sleep(200);
  • Linux中:usleep(200000);

1秒=1000毫秒,1毫秒=1000微秒

那么来实现一下跨平台的休眠功能,我提供一种我的方案:

#if defined __GNUC__
#include <unistd.h>
#define Sleep(x) usleep(x##000)
#elif defined _MSC_VER
#include <windows.h>
#endif

这样即使在gcc的编译环境下,我们也有和VC中一样的Sleep函数可以用了。这里我是用宏函数来实现的。#define
Sleep(x) usleep(x##000)
##是宏定义里面的连接符,这样就默认给参数后面多加了三个0,即扩大了1000倍。

gcc 的宏连接符(##)后面不支持连接+ - * /,所以不能写成

#define Sleep(x) usleep(x##*1000)

不过VC支持##后面接+ - * /符号。

掷骰子游戏

现在,来综合一下刚才的两个函数:清屏和休眠。写一个掷骰子小游戏的demo。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#ifdef __GNUC__
#include<unistd.h>
char *clear = "clear";
#endif
#ifdef _MSC_VER
#include<windows.h>
char *clear = "cls";
#endif

int a[3];
//判断是否为顺子
int isShunzi()
{
    int max=a[0],min=a[0],sum=a[0];
    for(int i=1;i<3;i++)
    {
        if(a[i]>max)
            max=a[i];
        if(a[i]<min)
            min=a[i];
        sum+=a[i];
    }
    int mid=sum-max-min;
    if(mid-min==1&&max-mid==1)
        return 1;
    return 0;
}

int main()
{
    srand((unsigned)time(NULL));
    int t=40;//随机40次后结束
    while(t--)
    {
        Sleep(200);
        system(clear);
        a[0] = rand()%6+1;
        a[1] = rand()%6+1;
        a[2] = rand()%6+1;
        printf("骰子1的点数为:%d\n",a[0]);
        printf("骰子2的点数为:%d\n",a[1]);
        printf("骰子3的点数为:%d\n",a[2]);

    }
    if(a[0]==a[1]&&a[0]==a[2])
        printf("您掷出的是豹子\n");
    else if(isShunzi())
        printf("您掷出的是顺子\n");
    else if(a[0]+a[1]+a[2]>=11)
        printf("您的点数是大\n");
    else
        printf("您的点数是小\n");

}

时间: 2024-10-09 23:06:01

编写C语言跨平台函数(以清屏和休眠函数为例)的相关文章

c语言清屏、等待、随机函数

清屏函数 #include<conio.h> system("CLS");或system(cls); 等待函数 #include<windows.h> Sleep(5000);//等待5秒,Sleep()以毫秒为单位 随机数函数 #include <time.h> #include<stdlib.h>(NULL的说明)   srand(time(NULL));(如果没有这个每次产生的随机数的顺序都一样) rand()%100+1;//产生

【C语言】编写一个函数reverse_string(char * string)(递归实现),将参数字符串中的字符反向排列,不能使用C函数库中的字符串操作函数。

//编写一个函数reverse_string(char * string)(递归实现) //实现:将参数字符串中的字符反向排列. //要求:不能使用C函数库中的字符串操作函数. #include <stdio.h> #include <assert.h> void reverse_string(char const * string) { assert( string != NULL ); if( *string != '\0' ) { string++; reverse_stri

java——JNI(例子控制台(64位)清屏

因为java的最底层是jvm,所以单纯的控制台java程序不能感知jvm再下面的操作系统的情况, 可以通过JNI(Java Native Interface)技术实现java后台调用C++/C的dll(Dynamic Linkable Library,动态链接库)文件. 不过,对Java外部的调用通常不能移植到其他平台,在applet中还可能引发安全异常.实现本地代码将使您的Java应用程序无法通过100%纯Java测试.但是,如果必须执行本地调用,则要考虑几个准则: 1.将您的所有本地方法都封

Qt ,mac osx ios x11 高清屏,视网膜的支持

Qt 5.0中添加了对于retina显示的基本支持.即将到来的Qt 5.1中提供了新的API和缺陷修复,对于这一问题进行了改进.Qt 4.8也获得了良好的支持,我们反向移植了一些Qt 5的补丁. 尽管这些实现的努力和Mac以及iOS程序员最为相关,但是来看一看其它平台是如何处理高DPI显示这一问题,也是很有趣的.这里主要有两种方式: 基于DPI缩放--Win32 GDI和KDE.在这种方式中,应用程序在全物理设备分辨率下工作,使用系统提供的一个DPI设定或者缩放因子,用于缩放布局.字体通常会被操

android 涂鸦(清屏,画笔,粗细,保存)以及canvas源码学习

更新:本文的内容只是一部分,这段时间添加了橡皮擦这个新功能,于是问题接二连三的来,比如说:如果用本文的内容去做橡皮擦的话,难!(至少我没解决,不是没背景图,就是有背景图但是更新要在下一下刷橡皮擦的时候才能更新效果),然后有个setbackgroundresource的函数,这个函数就可以了,但是问题又来了,比如说保存,清屏,但是我都解决了(清屏的话就是重新构造一个图,当clear的时候就把这张图赋值给以前的图片.保存的话我就是把绘下个图放到一张有背景的canvas上面,至是分辨率的问题自己去解决

特殊用途语言特性:默认实参,内联函数和constexpr函数,调试帮助

重点: 1.三种函数相关的语言特性:默认实参,内联函数和constexpr函数. 2.默认实参:某些函数有一种形参,在函数的很多次调用中它们都被赋予一个相同的值. 3.一旦某个形参被赋予了默认值之后,它后面的所以形参都必须要默认值. 4.若想使用默认形参,只要在调用函数时省略该实参即可. Tip:Window = screen ( , , ‘?’ );//错误:只能省略尾部的实参! 5.对于函数的声明来说,习惯将其放在头文件当中,在给定的作用域中一个形参只能赋予一次默认实参. NOTE:通常,应

python idle 清屏问题的解决

在学习和使用python的过程中,少不了要与python idle打交道.但使用python idle都会遇到一个常见而又懊恼的问题——要怎么清屏? 我在stackoverflow看到这样两种答案: 1.在shell中输入 1 import os 2 os.system('cls') 这种方法只能在windows系统中cmd模式下的python shell 才管用(因为cls的命令是针对cmd的),在python idle直接返回了一个0的值. 2.定义一个cls的函数,每次使用输入cls()即

移动端,多屏幕尺寸高清屏retina屏适配的解决方案

移动端高清.多屏适配方案 背景 开发移动端H5页面 面对不同分辨率的手机 面对不同屏幕尺寸的手机 视觉稿 在前端开发之前,视觉MM会给我们一个psd文件,称之为视觉稿. 对于移动端开发而言,为了做到页面高清的效果,视觉稿的规范往往会遵循以下两点: 首先,选取一款手机的屏幕宽高作为基准(以前是iphone4的320×480,现在更多的是iphone6的375×667). 对于retina屏幕(如: dpr=2),为了达到高清效果,视觉稿的画布大小会是基准的2倍,也就是说像素点个数是原来的4倍(对i

黑马程序员--C语言基础之--变量、内部函数与外部函数

------- IOS培训期待与您交流! ---------- 一.变量 变量代表着一个有名字的.具有特定属性的一个存储单元,运行期间,变量的值是可以改变的.变量必须先定义,后使用.定义时,指定该变量的名字与类型.变量名实际上是以一个名字代表的一个存储地址:而从变量中取值,实际上是通过变量名找到相对应的内存地址,再从该存储单元中读取相对应的数据. 刚刚提到了变量定义这个词,我们知道,在定义函数的时候,我们都会先对函数进行一个声明,这就又引用了一个概念:声明.那么我们先来理解一下什么是声明和定义: