首页 > 其他分享 >const 常量与常量

const 常量与常量

时间:2023-07-16 11:00:22浏览次数:28  
标签:enable constant 常量 -- 内联 test const

const 常量与常量

在 C 语言中,通过内联方式直接写到源代码中的字面量值一般被称为“常量”。比如这里的 -10,‘c’, 2.0。

int x = -10;  
char y = 'c';  
double z = 2.0; 

还有一种常量,是用 const 关键字按照与定义变量相同语法定义的量。比如:

const int vx = 10;
const int* px = &vx;

那这两种常量有什么区别呢?

通常来说,在 C 语言中,使用 const 关键字修饰的变量定义语句,表示对于这些变量,我们无法在后续的程序中修改其对应或指针指向的值。因此,我们更倾向于称它们为“只读变量”,而非常量。当然,在程序的外在表现上,二者有一点是相同的:其值在第一次出现时便被确定,且无法在后续程序中被修改。

只读变量与字面量常量的一个最重要的不同点是,使用 const 修饰的只读变量不具有“常量表达式”的属性,因此无法用来表示定长数组大小,或使用在 case 语句中。常量表达式本身会在程序编译时被求值,而只读变量的值只能够在程序实际运行时才被得知。并且,编译器通常不会对只读变量进行内联处理,因此其求值不符合常量表达式的特征。

案例一

我们来做一个测试:

#include <stdio.h>
int main(void) {
  const int vx = 10;
  const int vy = 10;
  int arr[vx] = {1, 2, 3}; 
  switch(vy) {
    case vx: {  
      printf("Value matched!");
      break;
    }
  }
}

使用gcc编译,结果如下:

[email protected] 09:58:54 [pwd:~]# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/8/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl --disable-libmpx --enable-offload-targets=nvptx-none --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC)
[email protected] 09:59:03 [pwd:~]# gcc test_constant.c -o test_constant
test_constant.c: In function ‘main’:
test_constant.c:5:3: error: variable-sized object may not be initialized
   int arr[vx] = {1, 2, 3};
   ^~~
test_constant.c:5:18: warning: excess elements in array initializer
   int arr[vx] = {1, 2, 3};
                  ^
test_constant.c:5:18: note: (near initialization for ‘arr’)
test_constant.c:5:21: warning: excess elements in array initializer
   int arr[vx] = {1, 2, 3};
                     ^
test_constant.c:5:21: note: (near initialization for ‘arr’)
test_constant.c:5:24: warning: excess elements in array initializer
   int arr[vx] = {1, 2, 3};
                        ^
test_constant.c:5:24: note: (near initialization for ‘arr’)
test_constant.c:7:5: error: case label does not reduce to an integer constant
     case vx: {
     ^~~~

使用g++编译, 结果如下:

[email protected] 10:00:34 [pwd:~]# g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/8/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --with-isl --disable-libmpx --enable-offload-targets=nvptx-none --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC)
[email protected] 10:00:37 [pwd:~]# g++ test_constant.c -o test_constant
[email protected] 10:00:47 [pwd:~]# ./test_constant
Value [email protected] 10:00:50 [pwd:~]#

可以看到使用非常量表达式定义定长数组或者用于case 语句,GCC编译时是不通过的。

但是g++编译时通过了。这是因为内联常量的处理在 C 和 C++ 中存在一些差异。

在 C++ 中,const 变量默认是隐式内联的,这意味着编译器会尝试将其内联展开。因此,在 C++ 中,对于内联常量的编译行为通常是符合预期的,而且编译器会更倾向于将其内联展开。

然而,在 C 中,并没有类似于 C++ 的内联机制。虽然在某些情况下,编译器可能会选择将常量进行内联展开,但这是完全由编译器决定的。对于 C 语言而言,常量的内联展开并不是 C 语言标准所要求的行为。

由于 C 和 C++ 是两种不同的语言,它们的编译器在处理代码时有不同的行为和优化策略。因此,GCC 在 C 和 C++ 的编译过程中可能会有不同的行为,导致对内联常量的处理有所差异。

所以,如果在 C 代码中尝试进行内联常量,GCC 可能会在编译时报错,因为它将严格遵循 C 语言标准。而在 C++ 中,它更倾向于将常量进行内联展开,因此可以通过编译。

案例二

类似地,来看看其他编译器。以下是一个DMSQL语言案例。

CREATE OR REPLACE PACKAGE PKG_CONSTANTS IS
	DATE_FMT CONSTANT VARCHAR2(8):='YYYYMMDD';
END;

DROP TABLE IF EXISTS TEST_TABLE;
CREATE TABLE test_table (
  id INT,
  event_name VARCHAR(50),
  event_time TIMESTAMP
);
--初始化数据
BEGIN 
	FOR I IN 1..1000000 LOOP
        INSERT INTO test_table VALUES(I,'AAA'||I,SYSDATE-I%1000);  
	END LOOP;
	COMMIT;
END;
--创建索引
CREATE INDEX IDX_DATE ON test_table(TO_CHAR(EVENT_TIME,'YYYYMMDD'));
CREATE TABLE test_table1 AS SELECT * FROM test_table WHERE 1=0;

CREATE OR REPLACE PROCEDURE TEST_CONSTANT() AS
	DATE_FMT CONSTANT VARCHAR2(8):='YYYYMMDD';
BEGIN
  INSERT INTO test_table1 SELECT * FROM test_table WHERE  TO_CHAR(EVENT_TIME,DATE_FMT) BETWEEN '20230605' AND TO_CHAR(SYSDATE,DATE_FMT);
END ;

这里存储过程中的SQL从执行计划看是不使用索引的。这是因为存储过程编译器无法对只读变量内联处理。如果使用常量值替换只读变量会带来很大性能提升。

小结

对于常量的内联处理,不同编程语言和编译器的支持程度可能有所不同。C++通常对常量的内联处理提供良好的支持,而C语言则一般不提供显式支持。这取决于编译器的实现和语言的规范。

资料参考自极客时间

标签:enable,constant,常量,--,内联,test,const
From: https://www.cnblogs.com/lixiaomeng/p/17557583.html

相关文章

  • constexpr的作用(转)
    原文: https://www.zhihu.com/question/274323507constexpr 的主要用处有拓宽「常量表达式」的范围提供显式「要求」表达式编译时(compile-time)求值的方法为什么要拓宽「常量表达式」的范围,从原本标准库中的很多尴尬之处就可以看出:比如我们都知道 INT_MAX 是C语言的遗......
  • 变量、常量、作用域
    变量、常量、作用域变量变量:就是可以变化的量Java是一种强类型语言,每个变量都必须声明其类型Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域typevarName[=value][{,varName[=value]}];//数据类型变量名=值;//可以用逗号隔开来声明多个同类型......
  • java—运行时常量池(Runtime Constant Pool)、常量池(Constant Pool)、字符串常量池(String
    最近在看常量池相关的东西的时候,会被这几个常量池给弄的晕乎乎的查阅了《深入理解java虚拟机》总结如下:一、常量池共有三类:’运行时常量池(RuntimeConstantPool)常量池(ConstantPool):也是常说的class文件常量池(classconstantpool)字符串常量池(StringConstantPool)二、详解......
  • cpp class constructor initialize list and override cout
    //book.h#pragmaonce#include<iostream>classbook{public:intidx;std::uint64_tid;std::stringauthor;std::stringcontent;std::stringcomment;std::stringisbn;std::stringsummary;std::stringtopic;boo......
  • vue3项目 运行 报错 Cannot assign to "b" because it is a constant
    环境依赖node18.16.0vite4.4.4vue 3.2.47背景当前错误与环境依赖关系不大,是由于我在打包的文件写的代码错误导致的,一般情况不会有这个错报错信息X[ERROR]Cannotassignto"b"becauseitisaconstantThesymbol"b"wasdeclaredaconstanthere:原因将r......
  • Qt信号槽信号函数重载问题 error: C2664: “QMetaObject::Connection const”
    //connect(spinFontSize,&QSpinBox::valueChanged,this,&MainWindow::spinFontSize_valueChanged);//由于信号函数存在重载,发送者找不到正确信号函数。//改用A.Qt4带形参方式//connect(spinFontSize,SIGNAL(valueChanged(int)),this,SLOT(spinFontSize_valueChang......
  • 注解 @RequiredArgsConstructor
    注解@RequiredArgsConstructor生成带有必需参数的构造函数。必需的参数是最终字段和具有约束的字段,例如@NonNull。完整的文档可在@lconstructor的项目lombok功能页面上找到。即使未列出,该注释也具有onConstructor参数。有关更多详细信息,请参见完整的文档。这个是基于lombo......
  • 现代C++(Modern C++)基本用法实践:六、constexpr编译时计算
    概述constexpr修饰的变量、函数、对象构造函数表示在编译时就可以确定。它经常用来计算一些编译期可以确定常数,和常数组成的表。比如编译时确定10000以内所有的素数,运行时用的时候直接查表。用法举例参考测试项目代码ModernCppTest/modrenc_constexpr.cpp主要内容:constexpr......
  • SQL 约束(Constraints)
    SQL约束约束用于限制加入表的数据的类型。可以在创建表时规定约束(通过CREATETABLE语句),或者在表创建之后也可以(通过ALTERTABLE语句)。我们将主要探讨以下几种约束:NOTNULLUNIQUEPRIMARYKEYFOREIGNKEYCHECKDEFAULT详细讲解每一种约束。SQLNOTNULL约束NOTN......
  • java.lang.IllegalStateException: No primary or single unique constructor found f
      错误:Noprimaryorsingleuniqueconstructorfoundforinterfacejava.util.List(没有为List接口找到主要的或唯一的构造函数)原因:请求的参数没有匹配上处理函数的参数  解决:为List参数添加@RequestParam注解即可 ......