C专家编程摘录

  • c操作符的优先级

有时一些c操作符有时并不会像你想象的那样工作。

下方表格将说明这个问题:

优先级问题 表达式 期望的情况 实际情况
. 优先级高于* *p.f (*p).f *(p.f)
[ ]优先级高于* int *ap[ ] int (*ap)[ ] int *(ap[ ])
函数()优先级高于* int *fp() int (*fp)(), fp是 int型函数的指针 int *(fp( )),fp是返回int型指针的函数
==和!=优先级高于位操作 (val & mask != 0) (val &mask) != 0 val & (mask != 0)
==和!=优先级高于= c=getchar() != EOF (c=getchar()) != EOF c=(getchar() != EOF)
算数运算符高于位移运算符 msb << 4 + lsb (msb << 4) + lsb msb << (4 + lsb)
, 优先级最低 i = 1,2; i = (1,2); (i = 1),2;

对于上表做出补充:

1.表中第二条。指向数组的指针(ptr to array of ints)和指针数组(array of ptrs-to-int)的区别.

指向数组的指针可以认为是二维数组,即int (*ap)[ ] 等价于 int ap[ ][ ].ap指针及每个ap(即ap+i)偏移指针指向一个一维数组(一串数)。

而指针数组是由连续多个int指针组成的数组,每个数组元素指向一个int 变量。

对于指针数组要多提一点,虽然声明一个指针数组并不会被编译器报错,但是这是不安全的。 首先考虑这样一种情况,声明一个int 数组

int Na[5];

Na数组中的5个值都是随机值。当数组元素为指针时,情况也是相同的,即申请了一堆野指针。这将会导致运行时错误。

实测图:



可以看到,a[1]是一个空指针(nil),当用户使用这个指针指向的内存时,将会导致程序异常终止。

2. 一个非常妥善的对策是使用小括号将表达式包含起来(小括号优先级很高)。

  • c操作符的结合性

每个操作符都有优先级和“左”或“右”的结合性。当操作符优先级不同时,求值顺序取决于操作符优先级,而当操作符优先级相同时,这时将使用结合性。

1.除“位与”和“位或”外的所有操作符都遵守从右到左的结合性。(前者从左到右)

2.结合性目的在于表达式中操作符优先级相同情况下给出标准的运算顺序。

3.当操作符的优先级和结合性组合情况较为复杂时,可以采用拆分表达式或者使用小括号。

eg:

int a,b=1,c=2;

a = b = c;



上述两行代码a,b,c结果为: a = 2,b = 2,c = 2;

  • c中的空格

c中的空格并非人们普遍认为的那样不重要,可以随意增加或者减少。有时候空格会在根本上改变程序的原意。



1.反斜杠可以用来跳过一些字符,包括跳过一个新行(跳过回车符)。一个被反斜杠跳过回车的新行和他的上方未结束的行被认为是逻辑上的一行。但是如果上述情况下在反斜杠后面多打了一个空格(space),情况就不一样了。(反斜杠跳过了空格,而不是回车)。

如图:

2.现在这个问题主要由于ANSI 标准c中的“最大一口策略(maximal munch strategy)”。 考虑下述代码:

int z = y+++x;

z = y + ++x;?还是 z = y++ + x;?


根据最大一口策略,答案是前者。

(注:最大一口策略:如果表达式下一个符号(token)有超过一种语义的可能性,编译器将更愿意咬掉更长的字符序列)

考虑下述代码:

int z = y+++++x;

编译器如何解析?根据maximal munch strategy,结果为 :

z = y++ ++ +x;

但不妙的是,编译器不认识“++”,所以程序编译错误。

3.第三种情况发生在类似这样的一行代码中:

int ratio = *x/*y;

编译器如何处理这条语句?

结果如图:

即“/*”被认为是一段注释的开始,与我们期望的大相径庭。而15行在加入空格(space)后则解决了这个问题。

4.其他情况(上述三种情况并不代表全部)。

  • C声明中的优先级规则

c语言的声明表达对于编译器来说并没什么难度去解析,但对于学习C语言的程序员来说这种复杂性会造成一定的困扰。

举个复杂声明的例子:

char* const (next)();

下面介绍几个较为有用的运算符优先级,并提供一个系统的面向于人的解析c复杂声明的方法,最后附加一个c语言小程序,用于实现一个声明的自动解析。   



1.较为有用的优先级  

声明从名字开始解析,然后按照优先级顺序继续读取下一个词汇单元。

优先级顺序:(从高到低)

1、小括号包含起来的声明。

2、后继操作符“()”表示一个函数,后继操作符“[]”表示一个数组。

3、前驱操作符“*”表示一个“指向...的指针”。

4、如果const或volatile在类型标识符(e.g.int,long,etc)旁,则它与类型标识符匹配。否则与它直接的左边的星号匹配。



2.系统的方法

	处理顺序:
对于整个表达式,采取从右向左的顺序逐次擦除掉记号(token)。当所有记号被擦除,声明的解读过程也就完成了。
<--------
cha* const *(*next)(); step1:找到最左端的标识符;
读作:“标识符 是...”; step2:如果右边下一个记号是方括号“[possible-size]”;
读作:“...的数组”; step3:如果右边下一个记号是一个开放的小括号“(possible-parameter)”;
处理:一直读到最近的右小括号匹配。
读作:”返回...的函数“; step4:如果记号是左小括号 “(”;
处理:这个记号是当一个开放的小括号内记号被处理完后向左查询到的。这时左右小括号里的记号已经被擦除,继续读取右到小括号匹配,擦除,返回step2; step5:如果左记号是"const,volatile,*"三者中的一者
处理:继续向左查询记号,知道不再出现这三者中任何一个记号为止。从step4重新开始。
读作:const:“常...”, volatile: “非常...”,* : “...指针”; step6:当记号是基础的类型说明符时。
处理:结束
读作:相应类型名。 下面以一个例子帮助理解:

(处理中的记号使用粗体)

待处理声明 下一步处理 结果
char* const *(*next) ( ) step1 称"next是...”
char * const *(*  __)__ ( ) step2,step3 右小括号不匹配,下一步
char *const *(*    ) ( ) step4 *不匹配,下一步
char *const *( *   ) ( ) step5 ”*“ 匹配,称”...的指针“,应用step4
char *const *__(__    ) ( ) step4 "(" 和 “)”匹配,应用step2
char *const *    __(__ ) step2 不匹配,下一步
char *const *      ( ) step3 称”返回...的函数“
char *const * step4 不匹配,下一步
char *const * step5 称”...的指针“
char *const step5 称”常...“
char * step5 称”...的指针“
char step6 称“字符类型”

最后连在一起表达:next是一个指向返回指向常字符指针的函数的指针。:)  

  • c中的指针和数组的关系

    首先,如果你认为在c中指针和数组是等价的,那么你很有必要向下看。

    数组和指针本质是不同的,数组名代表的是一个地址,可以认为是一个只读的整数(const int),而指针是一个地址的地址(一个保存地址的变量)。但是由于在很多情况下,数组可以转换为指向数组首元素地址的指针,只不过这种转换是隐式的,但正因如此,才造成了我们的困惑。

    在下面三种情况下,数组将转换为指针:

    1.数组作为函数的参数,将以指针的形式传递。

    eg:

    void foo(int *arr) { ... }

    main {
    int arr[...];
    foo(a);
    }</code>

2.数组作为表达式一部份参与求值。

  <code>eg:
int c = a[i];
//隐式转换:
int c = *(a+i); //此时a是一个指针


3.使用下标的数组名将会被转换成一个指针加上偏移量。
eg:
a[i] --->*(a+i) ;

注意:指针不会转换成数组。
附:简单的c语言声明语义解析程序

点击此行显示代码

\#include \
\#include \
\#include \
\#include \
\#define MAXTOKEN 100
\#define MAXTOKENLEN 64
\#define pop stack[top--]
\#define push(s) stack[++top]=s;
typedef enum type_taag { IDENTIFIER,QUALIFIER,TYPE} tyag;
struct token
{
char type;
char string[MAXTOKENLEN];
};
int top = -1;
struct token stack[MAXTOKEN]; //save tokens as a structure contians string
struct token this_; //global structure contains string and type
tyag classify_string(void)
{
char *s = this_.string;
if(!strcmp(s,"const")) {
strcpy(s,"read only ");
return QUALIFIER;
}
if(!strcmp(s,"volatitle")) return QUALIFIER;
if(!strcmp(s,"signed")) return TYPE;
if(!strcmp(s,"char")) return TYPE;
if(!strcmp(s,"unsigned")) return TYPE;
if(!strcmp(s,"short")) return TYPE;
if(!strcmp(s,"int")) return TYPE;
if(!strcmp(s,"long")) return TYPE;
if(!strcmp(s,"float")) return TYPE;
if(!strcmp(s,"double")) return TYPE;
if(!strcmp(s,"struct"))return TYPE;
if(!strcmp(s,"union")) return TYPE;
if(!strcmp(s,"enum")) return TYPE;
return IDENTIFIER;
}
void gettoken(void)
{
char *p = this_.string;
while((*p = getchar()) == ' ') ;
if(isalnum(*p)) { // A-z, 0-9
while(isalnum(*++p = getchar())) ;
ungetc(*p,stdin);
*p = '\0';
this_.type = classify_string();
return ;
}
if(*p == '*') {
strcpy(this_.string,"pointer to");
this_.type = '*';
return ;
}
this_.string[1] = '\0'; //'['']'(')'
this_.type = *p;
return ;
}
void read_to_first_identifier(void)
{
gettoken();
while(this_.type != IDENTIFIER) {
push(this_);
gettoken();
}
printf("%s is ", this_.string);
gettoken(); //read next token after identifier
}
void deal_with_array(void)
{
while(this_.type == '[') {
printf("array ");
gettoken(); //read a number or ']'
if(isdigit(this_.string[0])) {
printf("0...%d ",atoi(this_.string)-1);
gettoken(); //eat the ']'
}
gettoken(); //get next token past ']'
printf("of ");
}
}
void deal_with_function(void)
{
while(this_.type != ')') {
gettoken(); //ignore the parameters of function
}
gettoken(); //eat the ')'
printf("function returnning ");
}
void deal_with_pointer(void)
{
while(stack[top].type == '*') {
printf("%s ", pop.string);
}
}
void deal_with_declarator(void)
{
switch(this_.type) {
case'[':deal_with_array();break;
case'(':deal_with_function();break;
}
deal_with_pointer();
//process the tokens which still in the stack
while(top >= 0)
{
if(stack[top].type == '(') {
pop;
gettoken();
deal_with_declarator();
}
else
printf("%s ", pop.string);
}
}
int main(void)
{
read_to_first_identifier();
deal_with_declarator();
printf("\n");
return 0;
}

转载请注明出处

c专家编程摘录的更多相关文章

  1. <<c专家编程>>笔记

    C专家编程摘录 c操作符的优先级 有时一些c操作符有时并不会像你想象的那样工作. 下方表格将说明这个问题: 优先级问题 表达式 期望的情况 实际情况 . 优先级高于* *p.f (*p).f *(p. ...

  2. C专家编程阅读笔记

    周末闲来无事,(哗),好久之前买的C专家编程一直没看,翻起来看了一下 尽量不使用unsigned 尽量不要在代码中使用unsigned,尤其是一些看起来是无符号类型的数字,比如年龄等,因为难免要使用u ...

  3. C专家编程

    [C专家编程] 1.如果写了这样一条语句: if(3=i).那么编程器会发出“attempted assignment to literal(试图向常数赋值)”的错误信息. 所以将常量放置在==前央, ...

  4. 《C专家编程》数组和指针并不同--多维数组

    <C专家编程>数组和指针并不同 标签(空格分隔): 程序设计论著笔记 1. 背景理解 1.1 区分定义与声明 p83 声明相当于普通声明:它所说明的并不是自身,而是描写叙述其它地方创建的对 ...

  5. C语言学习书籍推荐《C专家编程Expert C Programming Deep C Secrets》下载

    Peter Van Der Linden (作者) <C和C++经典著作 C专家编程Expert C Programming Deep C Secrets>展示了C程序员所使用的编码技巧, ...

  6. C专家编程cdecl

    理解所有分析过程的代码段 Page71(中文版) 你可以轻松地编写一个能够分析C语言的声明并把他们翻译成通俗语言的程序.事实上,为什么不?C语言声明的基本形式已经描述清楚.我们所需要的只是编写一段能够 ...

  7. C#.Net 如何动态加载与卸载程序集(.dll或者.exe)4-----Net下的AppDomain编程 [摘录]

    最近在对AppDomain编程时遇到了一个问题,卸载AppDomain后,在内存中还保留它加载的DLL的数据,所以即使卸载掉AppDomain,还是无法更新它加载的DLL.看来只有关闭整个进程来更新D ...

  8. c专家编程读书笔记

    无论在什么时候,如果遇到malloc(strlen(str));,几乎可以直接断定他是错误的,而malloc(strlen(str)+1):才是正确的: 一个L的NUL哟关于结束一个ACSII字符串: ...

  9. 《C专家编程》第四章——令人震惊的事实:数组和指针并不相同

    数组和指针是C语言里相当重要的两部分内容,也是新手程序员最容易搞混的两个地方,本章我们锁定指针与数组,探讨它们的异同点. 首先来看指针与数组在声明上的区别: int a[10]; int *p; 很明 ...

随机推荐

  1. Mybatis基本用法--中

    Mybatis基本用法--中 第四部分 动态 SQL 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似.MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素 ...

  2. svn: Working copy 'D:\workspace\web\..\..\images' is too old (format 10, created by Subversion 1.6

    问题:SVN同步或者提交的时候出现类似错误信息: 解决:找到对应目录,删除隐藏文件.SVN 重新提交或更新

  3. 改变 font Awesome、Ionic 图标属性

    可使用 -webkit-text-stroke属性. 例如: -webkit-text-stroke:1px red 使用color属性,可改变 图标颜色: 例如: color: red: 使用fon ...

  4. 话说LightningChart是最快最美的图表控件,到底先进在哪里?

    LightningChart Ultimate v.8.2 最新版本新特征告诉你它先进在哪里! 1. Headless 模式 headless模式允许在没有GUI的情况下使用LC.例如,在Window ...

  5. 剑指offer(一)

    面试题3:二维数组中查找 题目描述: 在一个二维数组中,每一行都按照从左往右递增地顺序排序,每一列都按照从上往下递增的顺序排序.请完成一个函数,输入这样的一个数组和一个整数,判断数组中是否存在该整数. ...

  6. 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) K Tournament Wins

    题目链接:http://codeforces.com/gym/101201 /* * @Author: lyucheng * @Date: 2017-10-22 14:38:52 * @Last Mo ...

  7. 一个基于EntityFramework Core的简单数据库访问层,适用于轻量级数据库业务

    这个访问层的代码实际上是园子里某个前辈的,本人只是觉得好使,记录了下来. 本访问层需要通过Nuget安装EntityFramework Core,不过个人认为EF 6同样可以使用. 搭配数据库,最好是 ...

  8. 数据库无法连接JDBC

    解决:Connections could not be acquired from the underlying database! 博客分类: 问题解决 数据库 iBatis   og4j:WARN ...

  9. 设备类型检测大全---userAgent

    对各种类型的设备的检测,以及所使用的浏览器的类型 function detect(ua) { var os = this.os = {}; var browser = this.browser = { ...

  10. css中单位 px、em 的区别【转载】

    原文:http://www.admin10000.com/document/6267.html     在国内网站中,包括三大门户,以及“引领”中国网站设计潮流的蓝色理想,ChinaUI等都是使用了p ...