关于C语言的一点说明

来源:互联网 发布:手游数据报告 编辑:程序博客网 时间:2024/05/10 19:26

前段时间看了一些C语言方面的书,把自己认为不太容易理解的东西,放在这里做些说明吧!

        C语言里的每一个变量都有三个最基本的属性:类型、作用域和生命期。一个变量的类型决定着这个变量需要占用多大的存储空间以及对这个变量可以进行哪些操作;一个变量的作用域说的是这个变量在程序上下文里的哪些部分是可见的。变量的作用域由它在程序里的声明位置来控制。作用域的边界是块、函数和文件;生命期说的是一个变量在程序执行期间的哪些时候能够有一个值。变量的生命期由相应的存储类来控制。

   

    关于数组和下标:“[]”最常见的用途是给出数组的下标,但“[]”本身其实是一个通用的下标操作符。在C语言里,x[i]被定义为“*x+i)”,其中x是一个地址,而i是一个整数递增量。按照C语言中的地址运算规则,整数i的值等于“sizeofx的基类型)”(现在应该明白为什么数组的下标是从0开始了吧:数组的名字其实是指向数组中第一个元素的指针,下标则是从该数组中的第一个元素算起的偏移量.第一个元素的偏移量是0

 

    关于函数和宏:在很多时候,一个例程既可以用宏来实现、也可以用函数来实现。使用一个宏的好处是它执行起来比较快,因为它没有函数调用方面的开销.但也经常因为使用不当,而造成很多副作用。feuer他给出了用宏时需要遵守的几个准则:1,只要一条宏定义语句里包含有操作符,就应该用括号把它括起来.2,宏定义越紧凑越好;表达式比语句好,单条语句比多条语句好.3a,在宏定义里一定要注意避免使用会导致二义性或副作用的C语言元素.3b,在宏定义里避免使用有副作用的表达式. 4,一定要让对宏进行扩展而得到的字符串--不管它是一个表达式,一条语句(不包括表示语句结束的分号)、还是一个语句块--成为一个完整的C语言元素。

   

    关于C语言指针的进一步学习

1、指针的类型

从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针所指的类型.

1)int *ptr; //指针的类型是int *
2)char *ptr; //指针的类型是char *
3)int **ptr; //指针的类型是 int **
4)int (*ptr)[3]; //指针的类型是 int(*)[3]
5)int *(*ptr)[4]; //指针的类型是 int *(*)[4]

2、指针所指向的类型:

当你通过指针来访问指针所指向的内存区时,指针所指向的类型,决定了编译器把那片内存区里的内容当做什么来看待.

从语法的角度看,你只须把指针声明语句中的,指针名字和左边的指针声明符*去掉,剩下的就是指针所指向的类型.

1)int *ptr; //指针所指向的类型是int
2)char *ptr; //指针所指向的的类型是char
3)int **ptr; //指针所指向的的类型是 int *
4)int (*ptr)[3]; //指针所指向的的类型是 int()[3]
5)int *(*ptr)[4]; //指针所指向的的类型是 int *()[4]

3、指针的值,或者叫指针所指向的内存区或地址
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指向的类型是什么?该指针指向了哪里?

 

    数组与指针的关系

指针和数组的名字表示的就是一个首地址,这一点可以肯定,但是数组是在堆里分配,而指针是在栈里分配,两者有者本质的区别,数组是有固定的地址,但是指针如果不使用new或者malloc的话,那么他就是处在一个“游离”的状态。

    什么时候数组与指针相同

所有作为函数参数的数组名总是可以通过编译器转换为指针。
在其他所有情况下,数组的声明就是数组,指针的声明就是指针,两者不能混淆。但在语句或表达式中引用时,数组总是可以写成指针的形式,两者可以互换。然而,数组和指针在编译器处理时是不同的,在运行时的表示形式也是不一样的。对编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址,你应该根据情况做出选择。

    什么时候数组和指针是相同的?C语言标准对此作了如下说明:
规则1 表达式中的数组名(与声明不同)被编译器当作一个指向该数组第一个元素的指针。


规则2 下标总是与指针的偏移量相同。


规则3 在函数参数的声明中,数组名被编译器当作指向该数组的第一个元素的指针。


规则1:“表达式中的数组名”就是指针
规则1和2合在一起理解,就是对数组下标的引用总是可以写成“一个指向数组的起始地址的指针加上偏移量。”对数组的引用如a[i]在编译时总是被编译器改写成*(a+i)的形式。
C语言标准要求编译器必须具备这个概念性的行为。于是,a[6]和6[a]都是正确的。
编译器自动把下标值的步长调整到数组元素的大小。这就是为什么指针总是有类型限制,每个指针只能指向一种类型的原因,因为编译器需要知道对指针进行解除引用操作时应取几个字节,以及每个下标的步长应取几个字节。
规则2:C语言把数组下标作为指针的偏移量
事实上,下标范围检测被认为并不值得加入到C语言中。
数组下标是定义在指针的基础上的,所以优化器常常可以把它转换为更有效率的指针表达式,并生成相同的机器指令。
C语言把数组下标改写成指针偏移量的根本原因是指针和偏移量是底层硬件所使用的基本模型。


    为什么C语言把数组形参当作指针
之所以要把传递给函数的数组参数转换为指针是出于效率的考虑,
这个理由常常也是对违反软件工程做法的辩解。
我们倾向于始终把参数定义为指针,因为这是编译器内部所使用的形式。但从另一方面,有些人觉得int table[]比int *table更能表达程序员的意图。

 

    数组片段的下标
向函数传递数组前面一个位置的地址(a[-1])这样就可以使数组下标从1到N,而不是从0到N-1。不幸的是,这个手段完全为标准所不容,所以你千万不要告诉别人是我告诉了你这个方法。

 

    C语言的多维数组
但所有其他语言都把这称为“数组的数组”
C语言里有一种别的语言称为数组的数组的形式,但C语言称它为多维数组。C语言中的数组就是一维数组,而这个数组的元素可以是另一个数组。
编译器在编译时会把carrot[i][j]解析为*(*(carrot+i)+j)的形式。

 

    如何分解多维数组

在“数组的数组的数组”中的每一个单独的数组都可以看作是一个指针。这是因为在表达式中的数组名被编译器当作“指向数组第一个元素的指针”。

 

    内存中数组是如何布局的
在C语言的多维数组中,最右边的下标是最先变化的,这个约定称为“行主序”。

 

   如何对数组进行初始化
只能够在数组声明时对它进行整体的初始化。之所以存在这个限制,并没有过硬的理由。

 

   

   

原创粉丝点击