中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档 | 网通镜像
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > 综合其它
《Expert C Programmin》笔记(2)
作者:未知 时间:2005-09-13 23:33 出处:Blog.ChinaUnix.net 责编:chinaitpower
              摘要:《Expert C Programmin》笔记(2)

Translation Limits

Every ANSI C compiler is required to support at least:
• 31 parameters in a function definition
• 31 arguments in a function call
• 509 characters in a source line
• 32 levels of nested parentheses in an expression
• The maximum value of long int can't be any less than 2,147,483,647, (i.e., long integers
are at least 32 bits).

and so on. Furthermore, a conforming compiler must compile and execute a program in which all of
the limits are tested at once. A surprising thing is that these "required" limits are not actually
constraints—so a compiler can choke on them without issuing an error message.

Reading the ANSI C Standard for Fun, Pleasure, and Profit

Sometimes it takes considerable concentration to read the ANSI C Standard and obtain an answer
from it. A sales engineer sent the following piece of code into the compiler group at Sun as a test case.

1 foo(const char **p) { }
2
3 main(int argc, char **argv)
4{
5 foo(argv);
6}
If you try compiling it, you'll notice that the compiler issues a warning message, saying:
line 5: warning: argument is incompatible with prototype
The submitter of the code wanted to know why the warning message was generated, and what part of
the ANSI C Standard mandated this. After all, he reasoned,
argument char *s matches parameter const char *p
This is seen throughout all library string functions.
So doesn't argument char **argv match parameter const char **p ?
The answer is no, it does not. It took a little while to answer this question, and it's educational in more
than one sense, to see the process of obtaining the answer. The analysis was carried out by one of
Sun's "language lawyers," [6] and it runs like this:
[6] The New Hacker's Dictionary defines a language lawyer as "a person who will show you the five
sentences scattered through a 200-plus-page manual that together imply the answer to your question 'if only
you had thought to look there.'" Yep! That's exactly what happened in this case.
The Constraints portion of Section 6.3.2.2 of the ANSI C Standard includes the phrase:
Each argument shall have a type such that its value may be assigned to an object with the unqualified
version of the type of its corresponding parameter.
This says that argument passing is supposed to behave like assignment.
Thus, a diagnostic message must be produced unless an object of type const char ** may be
assigned a value of type char **.To find out whether this assignment is legal, flip to the section
on simple assignment, Section 6.3.16.1, which includes the following constraint:
One of the following shall hold:?
?Both operands are pointers to qualified or unqualified versions of compatible types, and the
type pointed to by the left has all the qualifiers of the type pointed to by the right.
It is this condition that makes a call with a char * argument corresponding to a const char *
parameter legal (as seen throughout the string routines in the C library). This is legal because in the
code
char * cp;
const char *ccp;
ccp = cp;
  The left operand is a pointer to "char qualified by const".
  The right operand is a pointer to "char" unqualified.
  The type char is a compatible type with char, and the type pointed to by the left operand
has all the qualifiers of the type pointed to by the right operand (none), plus one of its own
(const).
Note that the assignment cannot be made the other way around. Try it if you don't believe me.
cp = ccp; /* results in a compilation warning */
Does Section 6.3.16.1 also make a call with a char ** argument corresponding to a const
char ** parameter legal? It does not.
The Examples portion of Section 6.1.2.5 states:
The type designated "const float *" is not a qualified type--its type is "pointer to const-qualified float"
and is a pointer to a qualified type.
Analogously, const char ** denotes a pointer to an unqualified type. Its type is a pointer to a
pointer to a qualified type.
Since the types char ** and const char ** are both pointers to unqualified types that are
not the same type, they are not compatible types. Therefore, a call with an argument of type char
** corresponding to a parameter of type const char ** is not allowed. Therefore, the
constraint given in Section 6.3.2.2 is violated, and a diagnostic message must be produced.
This is a subtle point to grasp. Another way of looking at it is to note that:
?the left operand has type FOO2--- pointer to FOO, where FOO is an unqualified pointer to a
character qualified by the const qualifier, and
?the right operand has type BAZ2---pointer to BAZ, where BAZ is an unqualified pointer to
a character with no qualifiers.
FOO and BAZ are compatible types, but FOO2 and BAZ2 differ other than in qualifica-tion of the
thing immediately pointed to and are therefore not compatible types; therefore the left and right
operands are unqualified pointers to types that are not compatible. Compatibility of pointer types is
not transitive. Therefore, the assignment or function call is not permitted. However, note that the
restriction serves mainly to annoy and confuse users. The assignment is currently allowed in C++
translators based on cfront (though that might change).

We felt that a lot of people would have questions in the future, and not all of them would want to
follow the process of reasoning shown above.


Const Isn't

The keyword const doesn't turn a variable into a constant! A symbol with the const
qualifier merely means that the symbol cannot be used for assignment. This makes the value
re ad -onl y through that symbol; it does not prevent the value from being modified through
some other means internal (or even external) to the program. It is pretty much useful only
for qualifying a pointer parameter, to indicate that this function will not change the data that
argument points to, but other functions may. This is perhaps the most common use of
const in C and C++.
A const can be used for data, like so:
const int limit = 10;
and it acts somewhat as in other languages. When you add pointers into the equation, things
get a little rough:
const int * limitp = &limit;
int i=27;
limitp = &i;
This says that limitp is a pointer to a constant integer. The pointer cannot be used to
change the integer; however, the pointer itself can be given a different value at any time. It
will then point to a different location and dereferencing it will yield a different value!
The combination of const and * is usually only used to simulate call-by-value for array
parameters. It says, "I am giving you a pointer to this thing, but you may not change it."
This idiom is similar to the most frequent use of void *. Although that could
theoretically be used in any number of circumstances, it's usually restricted to converting
pointers from one type to another.
Analogously, you can take the address of a constant variable, and, well, perhaps I had better
not put ideas into people's heads. As Ken Thompson pointed out, "The const keyword
only confuses library interfaces with the hope of catching some rare errors." In retrospect,
the const keyword would have been better named readonly.


Even though the rules were changed, subtle bugs can and do still occur. In this example, the
variable d is one less than the index needed, so the code copes with it. But the if statement
did not evaluate to true. Why, and what, is the bug?
int array[] = { 23, 34, 12, 17, 204, 99, 16 };
#define TOTAL_ELEMENTS (sizeof(array) /
sizeof(array[0]))
main()
{
int d= -1, x;
/* ... */
if (d <= TOTAL_ELEMENTS-2)
x = array[d+1];
/* ... */
}
The defined variable TOTAL_ELEMENTS has type unsigned int (because the return type
of sizeof is "unsigned"). The test is comparing a signed int with an unsigned int quantity. So
d is promoted to unsigned int. Interpreting -1 as an unsigned int yields a big positive
number, making the clause false. This bug occurs under ANSI C, and under K&R C if
sizeof() had an unsigned return type in a given implementation. It can be fixed by
putting an int cast immediately before the TOTAL_ELEMENTS:
if (d <= (int) TOTAL_ELEMENTS - 2)


Advice on Unsigned Types

Avoid unnecessary complexity by minimizing your use of unsigned types. Specifically,
don't use an unsigned type to represent a quantity just because it will never be negative
(e.g., "age" or "national_debt").
Use a signed type like int and you won't have to worry about boundary cases in the
detailed rules for promoting mixed types.
Only use unsigned types for bitfields or binary masks. Use casts in expressions, to make all
the operands signed or unsigned, so the compiler does not have to choose the result type.

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有