我试图了解在C代码中该如何使用const
关键字。一开始我并没有太在意这个,但是后来我看到有很多例子都在大量使用const
关键字。那我是不是应该去努力回头将合适的变量都设为const
?还是说这样只是浪费时间呢?
我认为使用const
可以使代码更易读,尤其是在函数调用时,它能让人和编译器都更清楚哪些变量会被修改。除此之外,我是否忽略了其他重要的点呢?
const
是有类型的,#define
宏没有。
const
受C块的限定,#define
适用于一个文件(或更严格地说,编译单元)。
const
在参数传递中最有用。如果您看到带指针的原型使用了const
,则知道安全地传递数组或结构体,因为函数将不会更改它。没有const
就可以更改。
查看strcpy()
等定义,就会明白我的意思。在一开始将“const-ness”应用于函数原型。回溯添加const
并不那么困难,但是需要“很多工作”(但如果按小时计费,则可以)。
还要考虑:
const char *s = "Hello World";
char *s = "Hello World";
哪一个是正确的,为什么?
s
指向完全不同的字符串),这正确吗?我也在这里和那里看到了那种表示法,这是初始化字符串(即char
数组)的方式吗? - c00kiemonsterconst
。但在上面的例子中,s
是一个常量,因此无法修改。 - David Schwartzconst
是正确的。第二个例子是错误的,但不幸的是很常见。它会导致尝试更改只读内存(崩溃!)。很久以前,Visual Studio 5允许这样做,并产生了一些“有趣”的效果。 - cdarke如何在C语言中最好地使用const关键字?
当您想要将其设置为“只读”时,请使用const
。就是这么简单 :)
使用const
不仅是一种良好的实践,而且可以提高代码的可读性和理解性,还有助于防止一些常见的错误。在适当的情况下,一定要使用const
。
#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
const
、volatile
和 auto
都会产生相同的代码,只有 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
#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
也具有内部链接。 volatile
仍然覆盖const
和register
,但const register
在C ++上结合了两种优化。consts
,令人惊讶地发现const
仍然不是冗余的。除非使用const
,否则内存加载不会被优化掉,即使文件范围变量在代码中没有被修改。https://godbolt.org/z/PhDdxk。
const
也无妨。但在某些时候,你可能必须放弃一些东西。例如,const
和多维数组真的很难协同工作。 - Shahbazconst
性质,不应混淆。一个const
对象是一个无法通过任何方式修改的对象。一个const
指针或引用只是一个指针或引用,不能直接使用来修改对象,但对象仍然可以通过其他方式进行修改。 - David Schwartz