输入宏的类型以优化代码

时间:2017-10-10 16:40:49

标签: c macros c-preprocessor

致力于优化代码。将宏转换为char以减少内存消耗是否是个好主意?这样做的副作用是什么?

示例:

#define TRUE 1 //non-optimized code 
sizeof(TRUE) --> 4

#define TRUE 1 ((char) 0x01) //To optimize 
sizeof(TRUE) --> 1

#define MAX 10 //non-optimized code 
sizeof(MAX) --> 4

#define MAX ((char) 10) //To optimize 
sizeof(MAX) --> 1

3 个答案:

答案 0 :(得分:4)

它们在内存消耗方面几乎没有差别。

这些宏提供了在表达式中使用的值,而实际的内存使用量(大致)由变量的类型和数量以及动态分配的内存决定。因此,您可能TRUEintchar,但实际上重要的是变量的类型(或者,它出现的表达式)被分配给,这不受常数类型的影响。

这些常量的类型可能具有的唯一影响是如何执行它们的表达式 - 但即使这种效果应该几乎不存在,因为C标准(简化)隐含地促进{{1在执行几乎任何操作之前,或int所有较小的类型。 1

所以:如果你想减少你的内存消耗,不要看你的常量,但是在你的数据结构中,可能是全局的并动态分配 2 !也许你有一大堆unsigned值,其中double的精度就足够了,也许你保留的大数据比你需要的时间长,或者你有内存泄漏,或者大量的一个布置得很糟糕的float,或者当它们可能是一个4字节宽的布尔值时 - 这是你应该照顾的东西,绝对不是这些struct。 / p>

注释

  1. 这个想法是整数运算以本机寄存器大小执行,传统上对应于#define。此外,即使这个规则不正确,在表达式中更改整数临时值的大小的唯一记忆效应也可能最多是增加堆栈使用量(通常大多数情况下预先分配),以防寄存器溢出

  2. 在堆栈上分配的内容通常没有问题 - 如上所述,它通常是预先分配的,如果你筋疲力尽,你的程序就会崩溃。

答案 1 :(得分:2)

在C中没有char常量这样的东西,这就是为什么没有“short”和“char”的后缀,因为有“long”和“long long”。由于整数提升(§6.3.1.1p2),(char)0x10的转换值将立即在几乎任何整数上下文中提升回int

因此,如果cchar而你写if (c == (char)0x10) ..., 在进行比较之前, x(char)0x10都会被提升为int

当然,如果某个编译器知道它没有区别,那么它可能会忽略转换,但是如果可能的话,即使没有显式转换,编译器肯定也会使用字节常量。

答案 2 :(得分:-1)

优化级别取决于(1)使用这些定义的位置以及(2)您运行代码的处理器的结构(或微控制器)是什么。

(1)已在其他答案中得到解决。

(2)是重要的,因为有处理器/微控制器以8位而不是32位表现更好。有些处理器例如是16位,如果你使用8位变量,它可以减少所需的内存,但会增加程序的运行时间。

以下是一个示例及其反汇编:

#include <stdint.h>

#define _VAR_UINT8      ((uint8_t)  -1)
#define _VAR_UINT16     ((uint16_t) -1)
#define _VAR_UINT32     ((uint32_t) -1)
#define _VAR_UINT64     ((uint64_t) -1)

volatile uint8_t v1b;
volatile uint16_t v2b;
volatile uint32_t v4b;
volatile uint64_t v8b;

int main(void) {

   v1b = _VAR_UINT8;

   v2b = _VAR_UINT8;
   v2b = _VAR_UINT16;

   v4b = _VAR_UINT8;
   v4b = _VAR_UINT16;
   v4b = _VAR_UINT32;

   v8b = _VAR_UINT8;
   v8b = _VAR_UINT16;
   v8b = _VAR_UINT32;
   v8b = _VAR_UINT64;

   return 0;
}

下面是针对x86 32位特定平台的反汇编(如果您编译上面的代码并在我们的处理器中生成反汇编,它可能是不同的)

00000000004004ec <main>:
  4004ec:   55                      push   %rbp
  4004ed:   48 89 e5                mov    %rsp,%rbp
  4004f0:   c6 05 49 0b 20 00 ff    movb   $0xff,0x200b49(%rip)        # 601040 <v1b>
  4004f7:   66 c7 05 48 0b 20 00    movw   $0xff,0x200b48(%rip)        # 601048 <v2b>
  4004fe:   ff 00 
  400500:   66 c7 05 3f 0b 20 00    movw   $0xffff,0x200b3f(%rip)        # 601048 <v2b>
  400507:   ff ff 
  400509:   c7 05 31 0b 20 00 ff    movl   $0xff,0x200b31(%rip)        # 601044 <v4b>
  400510:   00 00 00 
  400513:   c7 05 27 0b 20 00 ff    movl   $0xffff,0x200b27(%rip)        # 601044 <v4b>
  40051a:   ff 00 00 
  40051d:   c7 05 1d 0b 20 00 ff    movl   $0xffffffff,0x200b1d(%rip)        # 601044 <v4b>
  400524:   ff ff ff 
  400527:   48 c7 05 06 0b 20 00    movq   $0xff,0x200b06(%rip)        # 601038 <v8b>
  40052e:   ff 00 00 00 
  400532:   48 c7 05 fb 0a 20 00    movq   $0xffff,0x200afb(%rip)        # 601038 <v8b>
  400539:   ff ff 00 00 
  40053d:   c7 05 f1 0a 20 00 ff    movl   $0xffffffff,0x200af1(%rip)        # 601038 <v8b>
  400544:   ff ff ff 
  400547:   c7 05 eb 0a 20 00 00    movl   $0x0,0x200aeb(%rip)        # 60103c <v8b+0x4>
  40054e:   00 00 00 
  400551:   48 c7 05 dc 0a 20 00    movq   $0xffffffffffffffff,0x200adc(%rip)        # 601038 <v8b>
  400558:   ff ff ff ff 
  40055c:   b8 00 00 00 00          mov    $0x0,%eax
  400561:   5d                      pop    %rbp
  400562:   c3                      retq   
  400563:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40056a:   00 00 00 
  40056d:   0f 1f 00                nopl   (%rax)

在我的特定平台中,它使用4种类型的mov指令,movb(7字节),movw(9字节),movl(10字节)和movq(12个字节)取决于变量类型以及要分配的数据类型。