每日一题(七)

"本文详细介绍了如何区分C与C++程序,展示了不同数据类型的零值比较方法,并解释了C++中调用C编译函数时extern"C"的作用。同时,深入探讨了堆栈的区别,包括它们的申请方式、系统响应、大小限制、效率以及存储内容。最后,详细阐述了volatile关键字在多线程、中断服务和硬件交互等场景中的应用,强调了其防止编译器优化的重要作用。"
摘要由CSDN通过智能技术生成

9.26 如何确认一段程序是C还是C++编译的

使用C++中的自定义宏来确定:

#ifdef __cplusplus
	cout<<"C++"<<endl;
#else
    cout<<"C"<<endl;
#endif

在这里插入图片描述

9.27 给出各数据类型与零值比较的代码

分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var)

  • 对于逻辑判断,使用if (!var)来判断是否为真
  • 对于整型数值判断,使用if (var == 0)判断是否为0
  • 对于浮点型数值判断,使用if ((var >= 0.00001) && (var <= 0.00001))之类的,进行一个范围判断
  • 对于指针判断,使用if (var == NULL)来判断指针指向是否为空

9.28 C++程序中调用被C编译器编译后的函数,为什么要加extern”C”

由于C++支持函数重载,C不支持函数重载,而且函数的重载表面上看起来函数名一模一样,实际上C++编译器会在编译的时候给重载的函授根据参数的不同分配不同的名字,比如:

void fun (int x, int y);

C编译器编译之后,该函数在库中的名字为:_foo

C++编译器编译之后,该函数在库中的名字为:_foo_int_int

所以,为了区别这种名字匹配问题,C++提供了C链接交换指定符号extern “C”。当C++程序引用C的函数时,它会按照重载后的名字去目标文件(.obj)中去寻找对应的函数,而目标文件中存放的却是C版本的函数,名字对不上,所以根本找不到!

9.29 堆栈的区别详解

从以下几点分析堆栈的区别:
1.申请方式

2.申请后系统的响应

3.申请大小的限制

4.申请效率比较

5.存储的内容

6.存取效率比较

1.申请方式

stack:是由系统自动分配的,比如进入函数的参数、函数内声明的一些局部变量;

heap:由程序员申请的,C中经常用malloc函数(使用时要指明申请空间大小),C++中使用new函数;

要注意的是:p = (int*) malloc (10)中,虽然申请的空间是堆上的,但是指针p在栈上,指向堆。

2.申请后系统的响应

stack:只要申请的空间大于栈剩余的空间,系统就会自动为其提供空间,否则将报错栈溢出。

heap:操作系统有一个记录空闲内存地址的链表,当收到请求之后,会遍历链表,找到第一个空间大于申请空间的堆结点,将该结点的空间分配出去。当然,系统也会记录分配出去的空间的首地址以及申请空间的大小,以便于后面释放空间。

3.申请大小限制

stack:windows中是向低地址扩展的数据结构,是一块连续的内存空间

heap:堆是==向高地址扩展的数据结构,由于系统用链表来存储空间地址,==所以不是连续的;链表的遍历方向是由低地址到高地址;堆的大小取决于系统中有效的虚拟地址的大小。

4.申请效率的比较

stack:由系统自动分配,速度快。

heap:由new分配的内存,速度比较慢,而且容易产生内存碎片,但是用起来方便。

5.存储的内容

stack:函数调用的时候,第一个进栈的是函数调用语句之后的下一条可执行语句的地址,随后入栈的是函数的各个参数,遵循从右往左的入栈顺序(大多数情况),随后入栈的是函数中的局部变量;函数调用完毕之后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存储的地址,也就是第一个进栈的:函数调用语句之后的第一个可执行语句的地址,程序由该点继续运行。

heap:一般在堆的头部用一个字节存放堆的大小,堆中的具体内容由程序员安排。

6.存取效率的比较

说明程序如下:

int main ()
{
    char s1[] = "aaaaaaaa"; //字符串放在 栈
	char *s2  = "bbbbbbbb"; //字符串放在 常量区
    char s = 'c';
    
    s = s1[1];
    s = s2[1];
    
    return 0;
}

其中:char s1[] = "aaaaaaaa";是在函数运行的时候赋值的,char *s2 = "bbbbbbbb";是在编译的时候就确定的!而且在存取速度上来看,栈上的数据比常量区的字符串快。

9.30 volatile 详解

volatile是一个C语言关键字,修饰变量的时候表示:这个变量的值不可以被优化,也就是说每次用到变量的值,都要去变量申请内存空间去,而不是去使用保存在寄存器中的备份。

嵌入式程序员必须要知道的几点volatile的应用如下:

  • 硬件寄存器的值,比如一些状态寄存器的值。

  • 中断服务程序中访问到的一些非自动的变量,这些变量往往是反应程序运行状态或者是外部输入情况的,所以每一次的改变都要被记录下来。

  • 多线程应用中被几个任务共享的变量。

下面通过几个例子来进一步熟悉volatile的用法:
例一:一个参数既可以是const又可以是volatile吗?

可以的,const表示变量不可以被程序修改,volatile表示变量每一次修改都要被记录。所以const和volatile可以同时用来修饰一个只读状态寄存器,只读的状态寄存器是不可以被程序修改的,而且寄存器的每一次改变都要被记录下来,不能被优化。

例二:一个指针可以是volatile吗?
可以的,当我们在中断服务程序中去修改一个指向字符串的指针的时候,就会用到volatile来修饰这个指针

例三:下列这段程序有什么错误?

int square (volatile int *ptr)
{
    return *ptr * *ptr;  //返回平方值
}

首先返回值应该是long,要考虑到数据溢出。

其次,我们要明白,volatile修饰的变量就默认为会被意想不到的改变,所以而指针传进来的是一个地址,地址中的值可能随时改变。

然后,如果使用*ptr * *ptr进行平方运算,编译器会将代码优化为下列形式:

int a, b;
a = *ptr;
b = *ptr;
return a * b;

这个时候由于*ptr的值可能被意想不到的改变,所以a、b的值可能不相同,会造成程序错误。

所以最好的方法就是一次将*ptr赋值给一个变量,然后去求这个变量的平方(利用自身乘以自身),程序如下:

long square (volatile int *ptr)
{
    int a;
    a = *ptr;
    return a * a;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值