1.C语言的凝视

    在C语言中,假设须要凝视掉一段代码。且代码中可能会已经存在/**/凝视形式,那么能够使用:

#if 0

    statements

#endif

    这样的形式来凝视掉这段代码(statements代表这段代码)。

这样做的原因是C语言不同意嵌套凝视,也就是说第一个/*和第一个*/符号之间的内容都被看作凝视。无论里面还有多少个/*符号。



2.换行符的处理

#include <stdio.h>

int main()

{

    int ch, i;

    ch = getchar();

    i = getchar();

    printf("%d %d\n", ch, i);

    return 0;

}

输入:A+回车键

输出是多少?

分析:当我们使用getchar()函数接收一个字符时。在键盘输入至少须要键入一个字符(除非仅仅按一个回车键)和一个回车键。这样实际上用户是敲击了两次键盘。也就是实际上是键入了两个字符。也就是一个真正的字符和一个换行符(ASCII码为10相应的字符为'\n')。

所以上面这样的情况下的输出会输出A的ASCII码和换行符的ASCII码。正是因为这样的情况的存在,假设我们须要接收用户从键盘输入的字符时。须要程序中自己主动清理掉其后面跟的换行符。防止这个换行符污染到下一次的输入中。相同的以下的一个样例:

#include <stdio.h>

int main()

{

    int ch, ca, cb;

    scanf("%d", &ch);

    scanf("%d", &ca);

    cb = getchar();

    printf("%d %d %d\n", ch, ca, cb);

    return 0;

}

输入为:12+回车+34+回车的时候输出为多少?

分析:输出为12 34 10。原因和上面几乎相同。可是scanf比較智能,在接收输入的时候会自己主动去匹配stdin中的下一个输入时候匹配类型,匹配则接收。不匹配则丢弃,所以34还是可以正确的接收,然而最后的一个换行符(键盘上敲击回车键产生的)还是会留在stdin中,所以最后会被getchar()函数接收到cb中。



3.读取字符时的注意


    一个常常闻到的问题是:为什么ch被声明为整数。而我们实际上须要用它来读取字符(这个在从文件里读取字符时尤其须要注意)?答案是EOF是一个整型值,它的位数比字符类型要多,把ch声明为整型能够防止从输入读取的字符意外地被解释为EOF。

但同一时候,这也意味着接收字符的ch必须足够大。足以容纳EOF。这就是ch使用整型值的原因。字符仅仅是小整数而已。所以用一个整型变量容纳字符值并不会引起不论什么问题。



4.三字母词和转义字符

    C的标准中定义了几个三字母词,三字母词也就是几个字符的序列,合起来表示还有一个字符。

三字母词使C环境能够在某些缺少一些必需字符的字符集上实现。这里列出了一些三字母词以及它们所代表的字符:

??

(  [        ?

?<  {       ??

=  #

??)  ]        ??

>  }       ??/  \

?

?!  |        ??'  ^       ??

-  ~

比方printf("??

)");将输出一个]符号。

转义字符:

\?

在书写连续多个问号时使用。防止它们被解释为三字母词。

\"  用于表示一个字符串常量内部的双引號。

\'  用于表示字符常量'。

\\  用于表示一个反斜杠,防止它被解释为一个转义字符。



5.关于数据类型大小的ANSI规定

整型之间的大小规则:长整型至少应该和整型一样长,而整型至少应该和短整型一样长。

浮点型之间的大小规则:long double至少和double一样长,而double至少和float一样长。



6.变量作用域

编译器能够确认4种不同的作用域——文件作用域、函数作用域、代码块作用域和原型作用域。标识符声明的位置决定它的作用域。

>>> 代码块作用域

    位于一对花括号之间的全部语句被称为一个代码块。

下图中的5、6、7、9、10的变量都具有代码块作用域,函数的形式參数5在函数体内部也具有代码块作用域。另外声明9的f和声明6的f是不同的变量,在声明9所在的花括号内6声明的f被隐藏。

>>> 文件作用域

    不论什么在全部代码块之外声明的标识符都具有文件作用域,它表示这些标识符从它们的声明之处直到它所在的源文件结尾处都是能够訪问的。

下图中1、2具有文件作用域,同一时候文件里定义的函数名也具有文件作用域,由于函数名本身不属于不论什么代码块。所以4也具有文件作用域。

>>> 原型作用域

    原型作用域仅仅适用于在函数原型声明中的參数名,下图中3、8具有这种作用域。在原型中,參数的名字并没必要。

>>> 函数作用域

    它仅仅适用于语句标签,语句标签用于goto语句。基本上,函数作用域能够简化为一条规则——一个函数中的全部语句标签必须唯一。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGFvbml1X2M=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" height="457" width="374">



7.链接属性

    链接属性一共同拥有3种——external(外部)、internal(内部)和none(无)。没有链接属性的标识符(none)总是被当做单独的个体。也就是说该标识符的多个声明被当作独立不同的实体。属于internal链接属性的标识符在同一个原文件内的全部声明中都指同一个实体。但位于不同源文件的多个声明则分属不同的实体。最后。属于external链接属性的标识符不论声明多少次、位于几个源文件都表示同一个实体。

函数名、外部变量默认的链接属性为external。而局部变量的默认链接属性为none。

同一时候keywordextern和static用于在声明中改动标识符的链接属性,假设某个声明在正常情况下具有external链接属性,在它的前面加上statickeyword能够使它的链接属性变为internal。static仅仅对缺省链接属性为external的声明才有改变链接属性的效果。而externkeyword的规则更为复杂,一般而言,它为一个标识符指定external链接属性。这样能够訪问在其它不论什么位置定义的这个实体。

例如以下的样例:

static int i;

int func()

{

    int j;

    extern int k;

    extern int i;

    ......

}

分析:extern int k;这个声明表示在该函数体内的兴许部分能够訪问其它源文件里定义的k变量实体。而当externkeyword用于源文件里一个标识符的第一次声明时,它指定该标识符具有external链接属性;可是假设它用于该标识符的第2次或以后的声明时。它并不会更改由第1次声明所指定的链接属性。所以extern int i;声明不会改动i的static属性。

8.数组名

    在C中,差点儿全部使用数组的表达式中,数组名的值都是一个指针常量。也就是数组第一个元素的地址。仅仅有在两种场合下数组名并非指针常量:作为sizeof操作符的运算对象。当做单目运算符&的操作数。



9.指针与下标

    下标绝不会比指针更有效率,但指针有时会比下标更有效率。

样例1:

int array[10], a;

for(a = 0; a < 10; a++)

    array[a] = 0;

    为了对下标表达式求值(array[a])。编译器在程序中插入指令,取得a的值,并把它与整型的长度相乘。这个乘法须要花费一定的时间和空间。再看指针形式的写法:

int array[10], *ap;

for(ap = array; ap < array+10; ap++)

    *ap = 0;

    虽然这里不存在下标,可是还是存在乘法运算。

1这个值必须与整型的长度相乘,然后再与指针相加。可是这里存在一个重大的差别:循环每次运行时,运行乘法运算的都是两个同样的数(1和4)。结果这个乘法仅仅有在编译时运行一次——程序如今包括了一条指令。把4与指针相加。程序在运行时并不运行乘法运算。可是以下的两段代码:

a = get_value();

array[a] = 0;



a = get_value();

*(array+a) = 0;

    两组语句产生的代码并无差别。a可能是不论什么值,在执行时才干知道。所以两种方案都须要乘法指令,用于对a进行调整。

这个样例说明了指针和下标的效率全然同样的场合。



10.指针的进一步优化

void try1()

{

    for(p1 = x, p2 = y; p1-x < SIZE;)

        *p1++ = *p2++;

}

●这段代码使用p1-x < SIZE来作为终点推断。可是这个推断事实上会被处理为除法。由于p1-x的值是指针的运算,这个差值必需要除以一个类型的大小做调整。

这一点事实上会耗费比較大的代价。

void try2()

{

    for(i = 0, p1 = x, p2 = y; i < SIZE; i++)

        *p1++ = *p2++;

}

●这段代码又一次使用了计数器,用于控制循环退出。这样能够消除除法。而且缩短汇编后的代码长度。可是和上面的代码一样。代码在运行时p1和p2一般都会被又一次拷贝到别的寄存器之后才開始运算。

void try3()

{

    register int *p1, *p2;

    register int i;

    for(i= 0, p1 = x, p2 = y; i < SIZE; i++)

        *p1++ = *p2++;

}

●这段代码的优化在于将p1和p2声明为寄存器变量。这种话在汇编之后能够降低复制p1和p2的值到别的寄存器的步骤。

void try4()

{

    register int *p1, *p2;

    for(p1 = x, p2 = y; p1 < &x[SIZE]; )

        *p1++ = *p2++;

}

●&x[SIZE]会在编译时求值(由于SIZE是个数字常量),这段代码进一步消除掉了计数器i,汇编之后的代码相当紧凑,差点儿达到和汇编代码一样的效率。这同一时候也是C语言不正确称边界以及不检查数组越界给编程带来的方便体现。

《C和指针》整理一的更多相关文章

  1. C++指针传递和引用传递的区别 (转载整理)

    区别1:指针传递和引用传递是以不同的方式实现相同的效果,但是指针传递的本质还是值传递,只是传递的值是地址. 就拿 交换两个数的函数来举例: // 指针传递 void swap(int * val1, ...

  2. 【css】cursor鼠标指针光标样式知识整理

    在前端开发中,我们经常需要对对象鼠标指针光标进行控制,比如鼠标经过超链接时变成手指形状.在这里整理一下cursor鼠标指针光标样式的知识,记录与方便以后查找. 1.常用cursor光标 div( cu ...

  3. 多线程用this指针来传递参数(整理)

    整理自CSDN的论坛中,地址:https://bbs.csdn.net/topics/390703249 1.不同的线程不是两个独立的程序:线程不是进程(process是你说的程序) 2.线程函数必须 ...

  4. C语言整理复习——指针

    指针是C的精华,不会指针就等于没学C.但指针又是C里最难理解的部分,所以特意写下这篇博客整理思路. 一.指针类型的声明 C的数据类型由整型.浮点型.字符型.布尔型.指针这几部分构成.前四种类型比较好理 ...

  5. 《彻底搞定C指针》文档整理

    一.变量存储 #include <stdio.h> int main(void) { ; printf(“%d\n”, i); printf(“%d\n”, &i); //十进制打 ...

  6. C/C++指针知识整理(一)

    1.指针(变量)的类型 把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型.这是指针本身所具有的类型. (1)int*ptr; //指针的类型是 int* (2) char*ptr;//指针 ...

  7. C语言基础知识点整理(函数/变量/常量/指针/数组/结构体)

    函数 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ...

  8. C++ 指针和引用 吐血整理 Pointer&Reference

    说道C++的指针,很多人都很头疼,也很confuse.经常把它和变量名,引用(reference)等混淆,其实这最主要的原因是很多程序员对于基本知识的掌握有问题,从而导致的很多基本概念的混淆.本文就是 ...

  9. 个人整理的数组splay板子,指针的写的太丑了就不放了。。

    splay的板子.. 由于被LCT榨干了..所以昨天去学了数组版的splay,现在整理一下板子.. 以BZOJ3224和3223为例题..暂时只有这些,序列的话等有时间把维修序列给弄上来!! BZOJ ...

随机推荐

  1. 000 Excel获取数据

    1.目标网址 http://data.10jqka.com.cn/funds/ggzjl/field/zjjlr 二:需求一 1.需求 爬单个页面的数据 2.变化网址 http://data.10jq ...

  2. 同步 Visual Studio Code 的设置与插件

    工具推荐:Settings Sync. 小心有坑! VS Code 没有账号系统,所以设置不能同步,在多终端使用时不是很方便. 有一款插件能做这个事情:Settings Sync - Visual S ...

  3. 异步任务 -- FutureTask

    任务提交 之前在分析线程池的时候,提到过 AbstractExecutorService 的实现: public Future<?> submit(Runnable task) { if ...

  4. 【基础知识】ASP.NET[基础二(aspx)]

    1.cs可以调用aspx中的runat=server控件,aspx中也可以访问测试中定义的字段.函数,还可以编写复杂的C#代码,for等所有C#代码都可以写在aspx中(不推荐这样写): 2.把代码写 ...

  5. Top 5 SSH Clients for Windows (Alternatives of PuTTY)

    这篇博文列举了可以替代putty的5个工具,有些实现了putty没有实现的一些功能.如下: PuTTy is the most popular SSH clients for Windows-base ...

  6. Python与Django的时区问题

    在编码中牵扯到时间问题的时候,总是容易被时区问题搞混,一直以来,都是反复试验应付过去,今天终于搞清楚了个中缘由,一个心结也得以化解. Python 的时区问题 datetime.today() / d ...

  7. Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树

    https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...

  8. 【转载】CMarkup函数说明

    1.初始化Load    导入一个XML文件到CMarkup的对象中,并对它进行解析.类似C#的Load.SetDoc  从字符串中导入XML数据,并对它解析.类似C#的LoadXml. 2.输出Sa ...

  9. BZOJ 1588: [HNOI2002]营业额统计 双向链表

    BZOJ 1588: [HNOI2002]营业额统计 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 9619  Solved: 3287 题目连接 ht ...

  10. Qt线程外使用Sleep

    一:方法1 QTime t; t.start(); while(t.elapsed()<1000){     QCoreApplication::processEvents();} 二:方法2 ...