const

来源:互联网 发布:淘宝在哪里领取优惠卷 编辑:程序博客网 时间:2024/05/01 11:57

1 const只读变量

  • const修饰的变量是只读的,本质还是变量。
  • const修饰的局部变量在栈上分配空间。
  • const修饰的全局变量在全局数据区分配空间。
  • const只在编译期有用,在运行期无用。

注意:const修饰的变量不是真的常量,它只是告诉编译器该变量不能出赋值符号的左边。

2 const全局变量的分歧

  • 在现代C语言编译器中,修改全局变量将导致程序崩溃。

注意:标准C语言编译器不会将const修饰的全局变量存储于只读存储区,而是存储于可修改的全局数据区,其值依然可以改变。

编程实验:const变量的本质

#include <stdio.h>const int g_cc = 2;int main(){    const int cc = 1;    int* p = (int*)&cc;    printf("cc = %d\n", cc);    *p = 3;    printf("cc = %d\n", cc);    p = (int*)&g_cc; // error    printf("g_cc = %d\n", g_cc);    *p = 4;    printf("g_cc = %d\n", g_cc);    return 0;}

3 const的本质

  • C语言中的const使得变量具有只读属性。
  • 现代C编译器中的const将具有全局生命周期的变量存储于只读存储区(包括全局变量和静态局部变量)。
  • const不能定义真正意义上的常量。

需要注意的是:未初始化的具有全局生命周期的变量不会被储存到只读存储区,仍然被存储在全局数据区(.bss段),只有被初始化的具有全局生命周期的变量才会被储存到只读存储区。另外需要注意的一点是:对于staitc const int a;,a为全局变量或者静态变量。不能通过指向变量a的指针来更改a的值,a的值一直为0(实际在内存空间中的a的值已经被更改了,只是编译器将引用变量a的地方优化了,直接使用0而不是到内存中读取值。在定义变量时在前面加上voltile关键字即可强制编译器不要进行这种优化,每次引用变量a时都到内存中去读取相应的值)。对于const int a;,a为全局变量,这种情况可以通过指向变量的a的指针来更改a的值。

0804841b <main>:#include <stdio.h>static const int g_a;int main(void){ 804841b:   8d 4c 24 04             lea    0x4(%esp),%ecx 804841f:   83 e4 f0                and    $0xfffffff0,%esp 8048422:   ff 71 fc                pushl  -0x4(%ecx) 8048425:   55                      push   %ebp 8048426:   89 e5                   mov    %esp,%ebp 8048428:   51                      push   %ecx 8048429:   83 ec 14                sub    $0x14,%esp    static const int a;     int *p = (int*)&g_a; 804842c:   c7 45 f4 24 a0 04 08    movl   $0x804a024,-0xc(%ebp)    *p = 123; 8048433:   8b 45 f4                mov    -0xc(%ebp),%eax 8048436:   c7 00 7b 00 00 00       movl   $0x7b,(%eax)    printf("*p = %d\n", *p); 804843c:   8b 45 f4                mov    -0xc(%ebp),%eax 804843f:   8b 00                   mov    (%eax),%eax 8048441:   83 ec 08                sub    $0x8,%esp 8048444:   50                      push   %eax 8048445:   68 50 85 04 08          push   $0x8048550 804844a:   e8 a1 fe ff ff          call   80482f0 <printf@plt> 804844f:   83 c4 10                add    $0x10,%esp    printf("g_a = %d\n", g_a);    // 从这里可以看到,编译器认为未初始化的静态全局只读变量的值就应该是0,而不去实际的内存中取值 8048452:   b8 00 00 00 00          mov    $0x0,%eax 8048457:   83 ec 08                sub    $0x8,%esp 804845a:   50                      push   %eax 804845b:   68 59 85 04 08          push   $0x8048559 8048460:   e8 8b fe ff ff          call   80482f0 <printf@plt> 8048465:   83 c4 10                add    $0x10,%esp    p = (int*)&a; 8048468:   c7 45 f4 28 a0 04 08    movl   $0x804a028,-0xc(%ebp)    *p = 456; 804846f:   8b 45 f4                mov    -0xc(%ebp),%eax 8048472:   c7 00 c8 01 00 00       movl   $0x1c8,(%eax)    printf("*p = %d\n", *p); 8048478:   8b 45 f4                mov    -0xc(%ebp),%eax 804847b:   8b 00                   mov    (%eax),%eax 804847d:   83 ec 08                sub    $0x8,%esp 8048480:   50                      push   %eax 8048481:   68 50 85 04 08          push   $0x8048550 8048486:   e8 65 fe ff ff          call   80482f0 <printf@plt> 804848b:   83 c4 10                add    $0x10,%esp    printf("a = %d\n", a);    // 从这里可以看到,编译器认为未初始化的静态全局只读变量的值就应该是0,而不去实际的内存中取值 804848e:   b8 00 00 00 00          mov    $0x0,%eax 8048493:   83 ec 08                sub    $0x8,%esp 8048496:   50                      push   %eax 8048497:   68 63 85 04 08          push   $0x8048563 804849c:   e8 4f fe ff ff          call   80482f0 <printf@plt> 80484a1:   83 c4 10                add    $0x10,%esp    return 0; 80484a4:   b8 00 00 00 00          mov    $0x0,%eax}

实例分析:const的本质分析

#include <stdio.h>const int g_array[5] = {0};void modify(int* p, int v){    *p = v;}int main(){    int const i = 0;    const static int j = 0;    int const array[5] = {0};    modify((int*)&i, 1);           // ok    modify((int*)&j, 2);           // error    modify((int*)&array[0], 3);    // ok    modify((int*)&g_array[0], 4);  // error    printf("i = %d\n", i);    printf("j = %d\n", j);    printf("array[0] = %d\n", array[0]);    printf("g_array[0] = %d\n", g_array[0]);    return 0;}

4 const修饰函数参数和返回值

  • const修饰参数表示在函数体内不希望改变参数的值。
  • const修饰函数返回值表示返回值不可改变,多用于返回指针的情况。

这里写图片描述

字符串的类型实际上是char*,因此赋值直接赋值给char*也是不会有警告或者错误的。

实例分析:const修饰函数参数与返回值

#include <stdio.h>const char* f(const int i){    i = 5;    return "Delphi Tang";}int main(){    char* pc = f(0);    printf("%s\n", pc);    pc[6] = '_'; // Segmentation fault (core dumped)    printf("%s\n", pc);    return 0;}

注意:把const类型的指针复制给普通类型的指针会有警告:assignment discards ‘const’ qualifier from pointer target type,反过来则不会有警告。

5 小结

  • cosnt使得变量具有只读属性。
  • const不能定义真正意义上的常量。
  • const将具有全局生命周期的变量存储于只读存储区。

这里写图片描述

原创粉丝点击