如何在C中最好地使用const关键字?

时间:2013-01-18 15:14:33

标签: c const

我试图了解如何在C代码中使用const。首先,我并没有真正使用它,但后来我看到了很多const在整个过程中被使用的例子。我应该努力并回去并虔诚地制作合适的变量const吗?或者我只是在等我的时间?

我认为这样可以更容易地读取预期会发生变化的变量,特别是在人类和编译器的函数调用中。我错过了其他重要的观点吗?

4 个答案:

答案 0 :(得分:7)

const类型的#define宏不是。

const由C块限定,#define适用于文件(或更严格地说,编译单元)。

const对参数传递最有用。如果您看到带有指针的原型上使用的const,您知道传递数组或结构是安全的,因为该函数不会改变它。没有const,它可以。

查看strcpy()之类的定义,你会明白我的意思。在开始时将“const-ness”应用于函数原型。复古const并不像“大量工作”那么困难(但如果你按小时收到工资,那就好了)。

还要考虑:

const char *s = "Hello World";
char *s = "Hello World";

这是正确的,为什么?

答案 1 :(得分:6)

How do I best use the const keyword in C?

如果您想要“只读”,请使用const。就这么简单:)

答案 2 :(得分:2)

使用const不仅是一种很好的做法,还可以提高代码的可读性和可理解性,并有助于防止出现一些常见错误。绝对要在适当的地方使用const。

答案 3 :(得分:1)

除了在尝试修改常量并将常量作为非常量参数传递时产生编译器错误(因此充当编译器防护)之外,它还使编译器可以执行某些优化,因为知道该值不会更改,并且因此,它可以缓存该值,而不必从内存中读取它,因为它不会更改,并且可以立即将其替换为代码。

C常量

constregister基本上与volatile相反,并且使用volatile将覆盖文件和块范围的const优化,以及register的优化块范围。 const registerregister将产生相同的输出,因为const在gcc C -O0的块范围内在C上不执行任何操作,并且在-O1及更高版本上是多余的,因此仅应用register优化在-O0,并且从-O1开始是多余的。

#include<stdio.h>

int main() {
    const int i = 1;
    printf("%d", i);
}
.LC0:
  .string "%d"
main:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov DWORD PTR [rbp-4], 1
  mov eax, DWORD PTR [rbp-4] //load from stack isn't eliminated for block-scope consts on gcc C unlike on gcc C++ and clang C, even though value will be the same
  mov esi, eax
  mov edi, OFFSET FLAT:.LC0
  mov eax, 0
  call printf
  mov eax, 0
  leave
  ret

在这种情况下,对于-O0,constvolatileauto都产生相同的代码,只有register不同c.f.

#include<stdio.h>
const int i = 1;
int main() {
    printf("%d", i);
}
i:
  .long 1
.LC0:
  .string "%d"
main:
  push rbp
  mov rbp, rsp
  mov eax, DWORD PTR i[rip] //load from memory
  mov esi, eax
  mov edi, OFFSET FLAT:.LC0
  mov eax, 0
  call printf
  mov eax, 0
  pop rbp
  ret

改为使用const int i = 1;

i:
  .long 1
.LC0:
  .string "%d"
main:
  push rbp
  mov rbp, rsp
  mov eax, 1  //saves load from memory, now immediate
  mov esi, eax
  mov edi, OFFSET FLAT:.LC0
  mov eax, 0
  call printf
  mov eax, 0
  pop rbp
  ret

C ++常量

#include <iostream>

int main() {
    int i = 1;
    std::cout << i;
}
main:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov DWORD PTR [rbp-4], 1 //stores on stack
  mov eax, DWORD PTR [rbp-4] //loads the value stored on the stack
  mov esi, eax
  mov edi, OFFSET FLAT:_ZSt4cout
  call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
  mov eax, 0
  leave
  ret

#include <iostream>

int main() {
    const int i = 1;
    std::cout << i;
}
main:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov DWORD PTR [rbp-4], 1 //stores it on the stack
  mov esi, 1               //but saves a load from memory here, unlike on C
                           //'register' would skip this store on the stack altogether
  mov edi, OFFSET FLAT:_ZSt4cout
  call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
  mov eax, 0
  leave
  ret

#include <iostream>
int i = 1;
int main() {

    std::cout << i;
    }
i:
  .long 1
main:
  push rbp
  mov rbp, rsp
  mov eax, DWORD PTR i[rip] //load from memory
  mov esi, eax
  mov edi, OFFSET FLAT:_ZSt4cout
  call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
  mov eax, 0
  pop rbp
  ret

#include <iostream>
const int i = 1;
int main() {

    std::cout << i;
    }
main:
  push rbp
  mov rbp, rsp
  mov esi, 1 //eliminated load from memory, now immediate
  mov edi, OFFSET FLAT:_ZSt4cout
  call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
  mov eax, 0
  pop rbp
  ret
如果未初始化const(在文件作用域和块作用域中),

C ++具有产生编译器错误的额外限制。 const还具有内部链接,这是C ++上的默认链接。 volatile仍会覆盖constregister,但const register结合了C ++的两个优化。

尽管以上所有代码都是使用默认的隐式-O0进行编译的,但使用-Ofast进行编译时,const仍然令人惊讶地对于C或C ++在clang或gcc上对于文件范围的{{1 }}。除非使用consts,否则不会优化内存中的负载,即使未在代码中修改文件作用域变量。 https://godbolt.org/z/PhDdxk