16

我试图了解我应该如何const在 C 代码中使用。首先,我并没有真正费心使用它,但后来我看到了很多const贯穿始终的例子。我应该努力回去并虔诚地制定合适的变量const吗?还是我只是在浪费时间?

我想它可以更容易地阅读哪些变量预计会发生变化,尤其是在函数调用中,无论是对于人类还是编译器。我是否遗漏了其他重要的点?

4

4 回答 4

13

const类型化的,#define宏不是。

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

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

查看诸如此类的定义,strcpy()您就会明白我的意思。一开始就将“const-ness”应用于函数原型。改造const并不像“大量工作”那么困难(但如果你按小时计酬的话就可以了)。

还要考虑:

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

哪个是正确的,为什么?

于 2013-01-18T15:17:58.503 回答
7

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

const当您想将其设为“只读”时使用。就这么简单:)

于 2013-01-18T15:17:27.847 回答
3

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

于 2013-01-18T15:16:34.447 回答
1

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

C 常量

constregister基本上相反,volatile使用volatile将覆盖文件和块范围的 const 优化以及块范围的register优化。const register并且register将产生相同的输出,因为 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不同的cf

#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如果 a未初始化(在文件范围和块范围内),C++ 具有产生编译器错误的额外限制。const在 C++ 上也有默认的内部链接。仍然volatile覆盖const并结合了 C++ 上的两种优化。registerconst register

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

于 2020-04-10T16:38:31.183 回答