c语言的几个问题 static
今天搞几个简单的C语言问题搞了好久。。。。都是一些好简单的。。。
先说一下 static 我做了个C语言的static的权限实验 代码如下:
--------------------1.c-------------------
#include <stdio.h>
extern int n;
void main()
{
printf("%d",n);
}
-----------2.c---------------------------
int n=7; //这里改成static就验证了权限问题
就是这样简单的一直编译不通过 我开始还以为是c++6.0的问题 我就开了虚拟机用gcc试试看 结果,,,,还是不通过。。。。。
上面是c++6.0 下面是gcc
我就一直觉得1.c 是不是要include”2.c” 进来 但是那样的话 static用什么用? 都是一个文件了
最后,我找到了原因 不是代码的问题 。是万恶的编译器。。。
原来我用c++6.0的时候 喜欢直接点那个运行 但是这个程序不一样,如果直接运行就报找不到I 但是我要是每个文件都先编译一下 再运行就没问题了 可见是从编译文件中找i的啊
Gcc里面分别编译文件也没有用 最后我是这样直接出来的。。。
Gcc –o xiaobai 1.c 2.c 就好了
我在网上看了很多人讨论static 但是很少看到代码。。。。 而且也没人说起这个问题。。。
------------------------------------------------------------------------------------------------
还有一个是从专家编程上看到的。。。
这是别人的帖子。。。
我比较接受最后那个人的说法
阅读ANSI C标准,寻找裨益与乐趣.
//1.
void foo(const char *p) {}
int main(int argc, char *argv)
{
foo(argv);
return 0;
}
//2.
void foo(const char **p) {}
int main(int argc, char **argv)
{
foo(argv);
return 0;
}
第一个例子正确是因为实参 char * 能与 const char * 匹配,所指的的类型相容,形参所指类型具有实参所指类型的全部限定符.
第二个例子错误是因为实参指向 char * , 形参指向 const char * ,因此它们不相容.
////////////////
问题:
第一个例子说因为 char * 与 const char * 匹配.
照这样在第二个例子实参,形参指向是匹配的,但是实际上编译器并不认为这是对的.
难道仅仅是因为从字面上来看实参指向 char * , 形参指向 const char * ,由于确实是多了一个词语 "const",就解释它们不相容吗?
网友回答:
网友:plainsong
从t * 到const t *的转换是允许的。
而从类型a到类型b可转换并不代表从a*到b*可转换。比如int到double是可以的,但从int*到double*则不行。
而char** 和 const char ** 相当于:
typedef char * char_pointer;
typedef const * const_char_pointer;
char_pointer* 到const_char_pointer的转换则不允许,它们不是t与const
t的关系。
标准c++中限制main只能有两种原型,不过在c中可能没有这个限制。同时很多c++编译器也放宽了这个限制,在windows上的编译器通常至少还可以用int main(int, char**, char**)的原型。
网友:ckacka
正确的用法是:
____________________________________________________
void foo(char* const *p) {}
int main(int argc, char **argv)
{
foo(argv);
return 0;
}
____________________________________________________
其原因在于const是一个左结合的操作符
上面的代码的const正好修饰了char*这样一个类型,即字符串类型,起到了保护传入参数的作用。
关于main的形式c++98和c99一样,都指定了两种基本的形式:
____________________________________________________
int main()
int main(int argc, char *argv[])
____________________________________________________
但是,同时也补充说明他们在编译器上的实现不是唯一的,而是是至少的,即不同的实现可以有自己的扩充
网友:alng
说实在的,我也学了一招。
plainsong和ckacka的解释应该是合理的。
但是我看不出如果标准允许这种从较宽松的类型到较严格类型的转换或赋值会有什么不利的后果,比如破坏const语义之类。
试想:
const char cc=e;
char c;
const char *pcc=&cc;
char *pc = &c;
// 下面的选一
const char **ppcc =&pcc; // 当然合法
const char **ppcc =&pc; // 不合法,就是所讨论的情形。但是如果标准允许这种赋值
// 会有不利后果吗。我觉得不会。ppcc放弃了通过自己修改**ppcc的权利
// 难道放弃权利也有错吗?
// 至于另外两种组合之一
char **ppc = &pcc; // 当然不合法,可能破坏了const语义,要求了更多权力。
char **ppc = &pc; // 当然合法
想就ppcc = &pc; 可能有什么潜在危害请教plainsong和ckacka.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
网友:chinajiji
我觉得 alng(?) ( ) 的分析很是击中要害.
我也没有搞清楚这是这么回事,不过我只是想到了一个解决方案,从这个解决方案中似乎可以说点什么问题:
先看 alng(?) ( )的这个例子:
const char **ppcc =&pc; // 不合法,就是所讨论的情形。但是如果标准允许这种赋值
// 会有不利后果吗。我觉得不会。ppcc放弃了通过自己修改**ppcc的权利
// 难道放弃权利也有错吗?
我给出的解决方案是:
const char **ppcc1 =const_cast<const char**>(&pc);
这说明这里仅牵涉到const modifier 而不存在plainsong(短歌)所说的"类型不同"的因素,因为char** 与 const char**之间的转换只涉及const modifier的不同,与int* 与 double*之间的转换各是两码事.
我给出的那个例子也应证了ckacka的那个修改案例:void
foo(char* const *p) {}
如果我们采用ckacka的这做方案的话,这句:
const char **ppcc =&pc;
就应该改为:
const char *const*ppcc =&pc;
ckacka这做改法有个小问题,就是不能使这句合法:
(*ppcc)++;
因为(*ppcc)是const pointer.
但对于:
const char cc=e;
const char *pcc=&cc;
const char **ppcc =&pcc;
下面这句是合法的.
(*ppcc)++;
如果用我的哪个方案改:
const char **ppcc1 =const_cast<const char**>(&pc);
则下面的语句同样是合法的,这样就保证了从char**到const char**语意的完整性:
(*ppcc)++;
总之,我觉得问题的关键出现在一级指针上,即(*ppcc)上,可能是因为从char**转换到const char**时,出现了一个临时变量指针char*,这个临时指针变量只能绑定到char const*上才能正确(但我不能肯定这种分析是否正确).但我觉得从语意上分析,从char**到转换const char**的转换是合法且合理的.我还没有找到一个实际的例子说明这种转换会造成不安全的因素.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
网友:merlinran
今天下午查标准,绞尽脑汁,才发现这个问题只需要用通常的类型转换法则就可以解释。
依据c99标准6.2.5[26]和c++98标准3.9.3[1],带有const、volatile限定的类型,与不带这些限定的是不同的类型,但要求有同样的表示和对齐方式。所以const t和t是两种不同的类型。const t* 和t*当然更是不同的类型。但这与讨论的主题没有多大关系。
在4.4[1]中,明确规定了由t*转换到const t*的合法性,这里只需要一步类型转换。
t**无法转换为const t**,因为t*和const t*是两种不同的类型,而且它们之间没有直接的转换渠道。可以用中间变量来辅助这种转换:
const t** convert(t** t)
{
return &((const t*)*t);
}
这种关系就像三个没有继承关系的类型a,b,c:a可以转换为b,b可以转换为c,并不代表a就可以直接转换为c。
举个例子:
class myobj {
myobj(const std::string&);
//...
};
f(const myobj& obj);
f("csdn is good!"); // error! const char* --> std::string -->
myobj not acceptable
f(std::string("csdn is good!")); // ok!
如果我们允许多级的类型转换,不仅编译器难以实现,程序员也会遭遇大大的不幸。
-----------------------------------------------------------------------------------------------------------------------
最后说到const 我把这个的相关资料总结下 也是在摘抄的
不得不说的是,const是三个限定词中比较好理解的一个。变量声明中带有关键词const,意味着不能通过赋值,增量或减量来修改该变量的值,这是显而易见的一点。指针使用const则要稍微复杂点,因为不得不把让指针本身成为const和指针指向的值成为const区别开来、下面的声明表示pf指向的值必须是不变的
const float * pf;而pf则是可变的,它可以指向另外一个const或非const值;相反,下面的声明说明pf是不能改变的,而pf所指向的值则是可以改变的:
float * const pf;
最后,当然可以有既不能改变指针的值也不能改变指针指向的值的值的声明方式:
const float * const pf;
需要注意的是,还有第三种放置const关键字的方法:
float const * pf; //等价于const float * pf;
总结就是:一个位于*左边任意位置的const使得数据成为常量,而一个位于*右边的const使得指针本身成为const
还要注意的一点是关于const在全局数据中的使用:
使用全局变量被认为是一个冒险的方法,它使得数据在程序的任何部分都可以被错误地修改,如果数据是const,那么这种担心就是多余的了不是嘛?因此对全局数据使用const是合理的。
然而,在文件之间共享const数据要格外小心,有两个策略可以使用。一个是遵循外部变量的惯用规则,在一个文件进行定义声明,在其他文件进行引用声明(使用关键字extern)。
const double PI = 3.14159;
extern const dounle PI;
另外一个方法是把全局变量放在一个include文件里,这时候需要格外注意的是必须使用静态外部存储类
static const double PI = 3.14159;
#include”constant.h”。
#include”constant.h”
如果不使用关键字static,在文件file1.c和file2.c中包含constant.h将导致每个文件都有同一标识符的定义声明ANSI标准不支持这样做(有些编译器确实支持)。通过使用static,实际上给了每个文件一个独立的数据拷贝,如果文件想使用该数据与另外一个文件通话,这样做就不行了,因为每个文件只能看见他自己的拷贝,然而由于数据是不可变的,这就不是问题了。使用头文件的好处是不必惦记在一个文件中进行定义声明,在另一个文件中进行引用声明,缺点在于复制了数据,如果常量很大的话,这就是个问题了。
已有 0 人发表留言,猛击->>这里<<-参与讨论
ITeye推荐