const在c语言_C语言编译软件

const在c语言_C语言编译软件const关键字,很多人想到的可能是const常量,其实关键字const并不能把变量变成常量!在一个符号前加上const限定符只是表示这个符号不能被赋值

const在c语言_C语言编译软件

const关键字,很多人想到的可能是const常量,其实关键字const并不能把变量变成常量!在一个符号前加上const限定符只是表示这个符号 不能被赋值。也就是它的值对于这个符号来说是只读的,但它并不能防止通过程序的内部(甚至是外部)的方法来修改这个值(C专家编程.p21)。也就是说 const变量是只读变量,既然是变量那么就可以取得其地址,然后修改其值。看来const也是防君子不防小人啊!:)

const 使用情况分类详析

1
、const的普通用法
const int n = 10

意思很明显,n是一个只读变量,程序不可以直接修改其值。这里还有一个问题需要注意,即如下使用:int a[n];在ANSI C中,这种写法是错误的,因为数组的大小应该是个常量,而n只是一个变量。

2
、const用于指针
const int *p
int const *p
int * const p

在最后的一种情况下,指针是只读的(即p只读),而在另外两种情况下,指针所指向的对象是只读的(即*p只读)。const 是一个左结合的类型修饰符,它与其左侧的类型修饰符一起为一个类型修饰符,所以,int const 限定 *p,不限定p。int *const 限定p,不限定*p。这里有一个简便的区分方法:

沿着*号划一条线,如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。

3
、const用于函数的地址传递参数

void foo(const int *p)

这种形式通常用于在数组形式的参数中模拟传值调用。也就是相当于函数调用者声称:”我给你一个指向它的指针,但你不能去修改它。”如果函数编写者遵循了这个约定,那么就相当于模拟了值传递。这也是const最有用之处了:用来限定函数的形参,这样该函数将不会修改实参指针所指的数据。这里注意了,是函数不应该去修改而不是不能修改,也就是说const不能阻止参数的修改(原因见上)。

4
、const用于限定函数的返回值

const int foo();
const struct mytype foo();
上述写法限定函数的返回值不可被更新,当函数返回内部的类型时,已经是一个数值,当然不可被赋值更新,所以,此时const无意义,最好去掉,以免困惑。当函数返回自定义的类型时,这个类型仍然包含可以被赋值的变量成员,所以,此时有意义。

一、宏定义:主要是一些语法问题和技巧

例如:

#define FIND(s,e) (size_t)&(((struct s*)(0))->e)//求结构体内的变量相对于结构体的偏移量

#define SECONDS_PER_YEAR (360*24*60*60)UL//求一年中的秒数

#define MIN(a,b) (((a)<=(b))?(a):(b))//求最小值

说明:尽可能考虑移植性,由于代码可能在16位机,也有可能在32位机器上运行,所以采用size_t和UL都是基于移植性的考虑。

二、const用法:定义常量,修饰指针、函数的输入参数和返回值,简单说const表示只读的意思,本质上来说它只是在全局数据段或者栈中定义的是一个只读的常量,不是真正位于字符串常量区。Const的目的是为了产生高质量的代码,提高代码的可读性,同时保护好不能被任意改变的内存,从而降低Bug产 生的概率。

    const int a = 10;

const int b;//错误,常量必须初始化

    int a = 10,b = 9;

    const int *p1 = &a;//指针指向的内容只读,不能通过该指针去写

    *p1 = 11;//错误

    int * const p2 = &a;//指针本身只读,指针初始化到一个对象后,将不能被修改

p2 = &b;//错误

const int *p3 const= &a;//指针本身和指向的内容都是只读

 

const char *fp1(void) //修饰返回值,表示返回的指针指向内容只读

{

    char *p = “dddd”;

    return p;

}

void fp1(const char *str)

{

    *str = 4; //错误

    const char *p = str;//p必须为const,才能接受str

}

int _tmain(int argc, _TCHAR* argv[])

{

    const char *d = fp1();

    printf(“%s”,d);

}

 

三、extern用法:在别的文件中定义的变量,要想在本文件中使用,必须先用extern声明,例如:extern a;之后就当成在本文件中定义的变量一样使用。

 

四、static用法:

1.修饰变量,从生存域和访问域两个方面说明,无论static变量定义在函数内或外,该变量都位于数据段中;定义于函数体外的static变量的访问域仅仅是它所在文件中定义的函数,其他文件无法通过extern对其声明后访问。

2.修饰函数,使得函数的访问域仅仅为其所定义的函数。

3.类中变量用static修饰表示变量是类变量,类中函数用static修饰表示函数只能访问类中的static变量,不接受this指针,称为类函数。

例如:

//file1

void fstatic(void);

static void fstatic(void)

{

    return;

}

//file2

void fstatic(void);

Main()

{

fstatic();//声明仍然无法调用

}

总之,static实现了c语言的封装性,一定程度上实现了信息的封装和隐藏。

五、sizeof用法:sizeof是一个运算符,编译器在编译时就能确定其的运算结果,其运算对象可以是内部数据类型名、struct类型名、 class类型名、数组变量和其他类型的变量,只有计算对象是数组变量时,能得出数组实际的长度,其他类型的变量作为运算对象时,都将退化为对该对象的数据类型的运算。另外,在计算struct和class等用户自定义数据类型的大小时,需要考虑编译器会对其做优化处理,根据一定的原则调整,即会自动对齐,sizeof的结果往往会大于各个元素长度的和。

例一:

    short l;//sizeof(l)==2

    char *a =”abcddd”;//sizeof(a)==4

    void *s = malloc(100);//sizeof(s)==4

    char b[] = “abcddd”;//sizeof(b)==7*1

    float c[20];//sizeof(c)==20*4;

例二:

struct A

{

  short a;

  short b;

  short c;

};//sizeof(A)==6

struct B

{

  int a;

  char b;

  short c;

};//sizeof(A)==8

 

struct C

{

  double a1;

  long a;

  int a2;

  char b;

 

};//sizeof(B)==24

#pragma pack(1)//使用pack指令强制编译器不做优化对其,这种方式一般用于这个

struct D//结构体要直接写入文件或者直接用于网络收发的情况下

{

  double a1;

  long a;

  int a2;

  char b;

 

};

#pragma pack()//sizeof(B)==17

说明:例二中演示了数据对其的情况,由于CPU访问数据的特点是一次访问多个字节,故如果多字节数据的首地址是2的整数倍的话,将可以一次内存访问即可取得其所对应的所有数据,所以一个优化要求就是变量的地址是其数据类型长度的整数倍,例如int a的a的地址要求是4的整数倍。

针对结构体,如果结构体中所有元素类型相同,作为数组处理,入struct A;否则一般都是其最常元素的整数倍,例如struct B和C。这样处理的目的是考虑定义结构体数组的情况,例如struct B b[10];那么这10个结构体变量如果每个长度都是8,将能保证每个变量的第一个元素都能是4的整数倍,否则将达不到这个效果,不能实现高效的内存访问,故编译器将对不符合要求的结构体自动调整数据对齐。

最后需要交代的是,class中的static变量不被分配到栈上,所以不能计入sizeof中,空类的长度是1,有虚函数的长度为4,因为包含一个指向函数表的指针。

下面分析几个面试题:

例一:

int fp1(char var[])//返回值为4,因为var作为参数传递时,已经退化为普通指针,

{//故其大小就是指针的大小,不作为数组名处理,无法计算出数组长度。

   returnsizeof(var);

}

 

 

 

 

 

 

例二:

//str1是一个数组名,该数组是一个指针数组,长度为3,故sizeof(str1)为3*4

void fp1(void)

{

    char *str1[] ={“hello”,”mico”,”china”};

    for(int i=0;i <sizeof(str1)/sizeof(char *);i++)

    {

      printf(“%s”,str1[i]);

    }

}

六、volatile的用法:该词的意思是”易变的”,用于修饰变量的一个关键字,表示该变量在很多地方都能被改变,会被意象不到的改变,编译器不能对其优化,往往用于多任务系统或嵌入式系统中,情况一:嵌入式系统中的很多外设寄存器的值会实时改变,如:#define PTA  *(volatile unsigned char *)(0x00000001);情况二:嵌入式系统内存中的某些变量有可能被中断程序修改;情况三:多任务系统中的共享变量可能随时改变。满足这些特点之一的变量必须要用volatile修饰,保证编译器不能对其优化处理,如果被优化,往往程序的执行结果出错。

例一:

int a = 10;

if(a == 10)

{

   a = 11;

}

这短代码正常被优化为

a = 11; 但是,如果volatile int a;则这段代码将不被优化。

例二:

//原始代码:

int square(volatile int *ptr)

{

    return *ptr * *ptr;

}

//编译器优化完的代码:

int square(volatile int *ptr)

{

    int a = *ptr;

    int b = *ptr;

    return a*b;

}

//正确代码:由于*ptr的值随时可能改变,故优化代码中的a和b的值可能不一样,故计

int square(volatile int *ptr) //算的结果可能有误

{

    int a = *ptr;

    return a*a;

static有两种用途:一是修饰变量,二是修饰函数

 

第一:修饰变量

例子:

static int a;

void Func()

{

    static int b;

}

全局变量默认的存储类型是extern,若不加static修饰,在不同头文件中定义名字相同的全局变量会发生冲突。static修饰符是一个能够减少这类命名冲突的有用工具。例如,以下声明语句

static int a;

其含义与下面的语句相同:

int a;

只不过,a的作用域限制在一个源文件内,对于其他源文件,a是不可见的。因此,如果若干个函数需要共享一组外部对象,可以将这些函数放到一个源文件中,把它们需要用到的对象也都在同一个源文件中以static修饰符声明。

变量b是个局部变量,在程序退出函数Func后,b就不能被使用。但是当程序再次进入函数Func时,变量b保持上次运行后的值。

 

第二:修饰函数

static修饰符不仅适用于变量,也适用于函数。一个函数,其默认存储状态也是extern。如果函数f需要调用另一个函数g,而且只有函数f需要调用函数g,我们可以把函数f与函数g都放到同一个源文件中,并且声明函数g为static:

static int

g(int x)

{

    // g函数体

}

void f() 

{

    // 其他内容

    b=g (a);

}

我们可以在多个源文件中定义同名的函数g,只要所有的函数g都被定义为static,或者仅仅只有其中一个函数g不是static。因此,为了避免可能出现的命名冲突,如果一个函数仅仅被同一个源文件中的其他函数调用,我们就应该声明该函数为static。

 

如果说得规范一点,C/C++没有全局变量,只有外部和自动。C/C++里的全局变量是靠你自己做出来的。

所有在某函数里定义的变量全是自动变量。默认是auto。在所有函数外定义的是外部变量。

如果你的程序放在单个源文件里,那它就是全局变量。如果有多个源文件,你在其中一个文件里定义那个变量,再在所有其它文件里用extern说明它。它也成了全局变量。

注意:这里很仔细地使用了“定义”和“说明”。前者引起存储分配,是真正产生那个变量。后者只是让变量在那个文件里也可见(可以访问)。

如果在定义外部变量时,加上指示符static,则生成的是静态外部变量。这种变量的作用域仅限于那个文件。也就是说,不能再在其它文件里用extern说明它。

如果在定义自动变量时,加上指示符static,则生成的是静态自动变量。作用域没变,仍然是所在的语句块。但是生命期却变成永久。

 

 

今天的文章const在c语言_C语言编译软件分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/82513.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注