声明与定义之间的区别:
定义:
1.特殊的声明,确定对象类型并分配内存,用于创建新的对象
2.内存位置不发生改变,同一作用域内只能(而不是必须)出现一次
声明:
1.普通的声明,描述对象的类型,用于引用 指代其他地方定义的对象
2.可以出现在不同作用域多次
3.提醒编译器,在同一作用域内不能出现重名现象
4.为编译器提供函数原型,避免隐式认定导致的错误
1.为什么要确定类型?
确定分配内存的大小,确定从内存中读取位的方式。因为,不能简单的通过检查一个值得位来判断它的类型。
2.地址与对象名的关系?
这些名字就是我们所说的变量,同时也说明了名字和内存位置之间的关联并不是硬件所提供的,它是由编译器实现,所以编译器只是给了我们另一个方式去记住这些地址以便于更好的形象的操作,同时,硬件仍然通过地址访问内存位置。
3.如果一个程序对同一个外部变量定义不止一次,将怎么处理?
处理方式取决于 系统, 不同的系统可能处理方式不同。
当 int a = 6 与 int a= 7 出现在不同的源文件中,大多数系统会拒绝接受程序。
但是, 当 只是 在不同的源文件中定义多次却没有指定初始值,那么某些系统会接受这个程序,而另一些系统则不会接受。所以 ,为了避免这些问题,唯一解决办法就是每一个外部变量只允许定义一次。
4.为什么能避免隐式错误?
如果没有关于调用函数的特定信息,编译器便假定在这个函数的调用时参数的类型和数量是正确的,他同时会假定函数将返回一个整型值。而向编译器提供一些关于函数的特定信息(原型)显然更加安全。
eg: int a ; int a = 7;
都是对变量a的定义。
extern int a;
a是一个外部变量,但是因为有个extern关键字,所以这明显说明
a的储存空间是在别的地方分配的,从连接器的角度,这个声明可以看成是对外部
变量的一个引用。
分析声明
声明的组成:
存储类型 类型限定符 类型说明符 声明器 ;
(红色字体可点击查看详细解释)
|
声明器:
|
Eg:
1、下列不满足直接声明器数量
int f()()函数不能返回一个函数 改为 int(* f())()
int f() [ ] 函数不能返回一个数组,因为函数必须返回标量值 int (* f())[ ]
int f [ ]()数组不能储存函数 因为数组元素必须相同的长度 int (* f[ ])( )
2、不满足存储类型规则:
const static int i;
typedef static int i;
static register int i;
分析声明:
A 声明从名字开始读
B 按照优先级从高到底
() 聚组
() 函数调用
[ ] 下标引用
* 间接访问
C 如果const 或者 volatile 关键字后面紧跟类型说明符,那么它作用于类型说明符。在其他情况下const 和volatile 作用于他左边紧邻的指针星号
Eg:
1、当计算机启动时,硬件将调用首地址为0位置的子例程。
( *( void(* )()) 0 )( ) ;
将0进行强制类型转化(void (* )())0然后引用这个0位置中的函数
(*(void (* )())0)() 就好像fp是一个函数指针来引用fp处的函数 (*fp)()。用 typedf 能使表达更清晰,
typedef void (* funcptr ) ( );
( *( funcptr )0)( );
2、char * const *(*next)();
Next是一个指针
指向一个函数
这个函数返回类型为一个指针
这个指针指向另一个常量指针
这个常量指针指向一个char的变量。
3、void (*signal (int sig, void (*func)(int)))(int) ;