本节主要讲C语言中的各种符号,包括注释符、单引号双信号以及逻辑运算符等。

一、注释符

注释符号和注释在程序的预编译期就已经被解决了,在预编译期间,编译器会将注释符号和注释符号之间的部分简单的替换成为空格。

1.在程序中使用注释符号有以下几点需要注意

(1)如果注释符号在数据类型和变量之间,那么将不会影响程序的正常运行。

int/*...*/i;

(2)如果注释符号位于数据类型之间,那么程序将不会正常运行,因为一个数据类型关键字中间出现一个空格,那么数据类型关键字也就不再是关键字了。

/*不能正常运行*/
in/*...*/t i;

(3)如果字符串之间出现注释符号,程序仍然可以正常运行。

 char* s = "abcdefgh      //hijklmn";

(4)/**/这个注释符号是不能够被嵌套的,同时这个注释符是C90标准之前的C语言正式的注释符,在C90标准之后增加了//这个注释符。

(5)关于除法的运算在C语言中也要额外的注意

x/*p

这个程序的本意假如是我么想对x比上(*p)进行运算,但是最后的程序运行效果将是从x之后所有的代码全部被注释掉了。所以在写程序的时候多加一些括号吧,无论是运算还是优先级,利人利己。别像某考试那样。

2.注释符号是程序中必须存在的,某位大神说过,一个优秀的代码注释会在程序中占四分之一到三分之一,我没有看过太多的代码,所以自己先这么记了。还有就是注释不要乱用,而且注释也要讲求规范,同时写程序注释的时候要注释说明代码实现的功能,而不是去说明代码实现了什么操作,真的能去做编程的人都懂代码是怎么工作的。

二、接续符

1.定义:接续符号就是一个斜杠,用来通知编译器程序在这一行并没有结束,\之后的内容仍然属于这一行。同时注意,\绝对不可以有空格。同时在下一行的开始也绝对不可以有空格。

2.接续符号也要慎重使用,因为过多的使用接续符号会让程序显得很乱,同时,我们不是大神,不是大牛,只是菜鸟,所以淡定。

3.在编译器中,编译器会将程序的\剔除,那么在\后面的代码会自动提到这一行。

4.可以把一整块的代码定义成为一个宏,调用宏的时候就像调用函数一样。

5.\还有一个重要的使用方法,那么就是在字符和字符串中作为转义字符使用。

6.使用\来修饰宏代码块的历程如下:

#include <stdio.h>

#define SWAP(a,b) \
{ \
int temp = a; \
a = b; \
b = temp; \
} int main()
{
int a = 1;
int b = 2; SWAP(a,b); printf("a=%d, b=%d\n", a, b); return 0;
}

这个宏的主要操作是交换两个数的值,作为参数是进行复制的函数功能,我们只能用指针来完成这个功能,但是宏的使用可以使我们很简单来完成这个功能。在定义这个宏的时候使用接续符会让程序更加清晰,如果不使用接续符号,那么这个宏的本色其实是这样的:

#define SWAP(a,b) {int temp = a; a = b;b = temp;}

这样还好一些,如果宏的功能更多呢?那么是不是会更乱一些呢?

7.在C语言中转义字符主要用于表示无回显字符,也可以用于表示常规字符。常用的转义字符如下:

8.老唐总结:

(1)C语言中的\,有转义符和接续符两种作用。

(2)当\作为接续符的时候可以直接出现在程序中。

(3)当\作为转义符的时候需要出现在字符或者字符串中。

三、单引号和双引号

1.始终认为字符串和字符是C语言中的一个大难点,自己一直也没有好好看,包括scanf和printf的问题,看C primer的时候也给略过了,希望过几天强迫给自己一些时间去看吧。

2.在C语言中,单引号代表一个字符,双引号代表一个字符串常量。

(1)'a'在内存中占用一个字节。(2)"a"占用两个字节,因为字符串的结尾都会有\0作为结束,所以占用的内存是两个字节。(3)'a'+1表示'a'的ASCII码加1,结果为'b'

(4)"a"+1表示指针运算,结果指向"a"结束符'\0'

3.在C语言中,我们在注意野指针的同时还要注意给指针赋值的问题。首先看一个例程

#include <stdio.h>
#include <stdlib.h> int main ()
{
char *p1 = 1;
char *p2 = '1';
char *p3 = "1"; printf (" %d %d %d", *p1, *p2,*p3);
printf (" %d %d %d", p1, p2,p3); }

这个程序运行之后会崩溃,导致崩溃的语句是我们通过printf函数要打印出指针p1,p2,p3所指向的内容。但是当我们单独打印p1,p2,p3所代表的地址的时候,我们会发现,我们打印出来的地址截图如下:

通过上图可以发现,char *p1 = 1;其实就是把地址1给了指针变量p1,char *p2 = '1'是把1这个字符的ASC码(49)赋值给了指针p2,而第三个是把一个字符串的首地址赋值给了指针p3,如果我们只打印p3指针指向的内容,那么程序不会崩溃,因为p3指向的地址是一个合法的地址。而p1,p2的地址并不是合法的,因为无论什么系统中,很低的地址都是留给操作系统来使用的。

4.我最深刻的感受就是如果没有深刻理解字符和字符串,那么我们也会进行错误的赋值

#include <stdio.h>
int main()
{
char a = " ";
while (a=="\t" || a == " " || a == "\n")
{
scanf ("%c",&a);
}
return 1;
}

上面的程序无论在任何编译器中都不会进入while循环,因为在程序的第一个赋值语句中

char a = " ";

我们实际上是把一个字符串赋值给了一个字符,但是一个字符在32位机下占的是8位,而字符串占用的是32位,所以会发生截断,也就是说当进入while循环的时候,实际上就是将一个4字节长度的字符串在和一个1字节长的字符在比较,所以肯定进入不了while循环,导致程序直接退出。

5.老唐总结:

总结:单引号代表的是一个数字,双引号代表的是一个指针(指向只读字符常量的首地址)。

四、逻辑运算符

始终认为某计算机考试很脑残,我也在不断的吐槽,但是它里面的内容还是不断的出现在学习中,没有办法。先看一个程序:

#include <stdio.h>

int main()
{
int i = 0;
int j = 0; if( ++i > 0 || ++j > 0 )
{
printf("%d\n", i);
printf("%d\n", j);
} return 0;
}

打印结果如下图所示:

很难受吧?优先级,顺序点没有一个是好朋友。

1.&&

&&运算符代表的与运算,它是用来判断真假的,而&是进行按位与。&&运算符的规则是:如果&&符号前面的为真,那么继续判断&&符号后面的内容,如果为真,那么返回真,如果为假,那么返回假。如果&&符号前面的为假,那么将不再运行&&y运算符后面的内容,这个表达式直接被判为假。

#include <stdio.h>
#include <stdlib.h> int main()
{
int i = 0;
int j = 1; if (i && j++)
{
printf ("%d", j);
}
return 0;
}

在上述程序中,最后的j值也没有打印,因为程序执行到i = 0的时候就已经判定为假直接返回了。

在C语言中,0为假非0为真!

2.||运算符代表或运算。||运算符的规则是:如果||运算符前面的内容为真,那么将不再判断||运算符后面的内容,程序直接返回真。如果||前面的运算符为假,那么程序将会继续执行,判定||运算符后面的内容是否为真,如果为真,那么程序返回真,如果为假那么程序最后返回假。

#include <stdio.h>
#include <stdlib.h> int main()
{
int i = 0;
int j = 1; if (i || j++)
{
printf ("%d", j);
}
return 0;
}

程序最后运行的结果如下:

从程序的运行结果中可以看出:当程序判定i 为假以后程序还是继续执行了的,所以我们才能够打印出j的值为2.

3.!

取反运算符在很多条件判定的时候是很常用的。

首先说一下C语言中一个很容易混淆的规则:C语言中的逻辑运算符!只认得0,所以如果我们对0取反它将会返回1,如果对非0取反那么它将会返回0 。简单的一句话:在C语言中只有0和非0
例程如下:

#include <stdio.h>

int main()
{
printf ("%d\n",!0);
printf ("%d\n", !1);
printf ("%d\n", !10);
printf ("%d\n",!(-100));
return 0;
}

程序打印结果如下所示:

4.三目运算符

三目运算符在C语言中不是特别常用,不过有的时候使用它会使程序更加简便。

三目运算符的执行规则(a ?b:c ),当a为真时执行b中的内容,当a为假时执行c中的内容。

老唐经典代码:

#include <stdio.h>

int main()
{
int a = 1;
int b = 2;
int c = 0; c = a < b ? a : b; (a < b ? a : b) = 3; printf("%d\n", a);
printf("%d\n", b);
printf("%d\n", c); return 0;
}

五、位运算符

C语言中的位运算符如下图所示:

(1)异或(^)

异或在的运算原则是两个数有不同的位的时候进行或运算,如果两个数的所有位都相同,那么最后结果为0.

例程:

#include <stdio.h>
#include <stdlib.h> int main()
{
int a = 1;
int b = 2; int c = 2;
int d = 2; printf ("a^b = %d", a^b);
printf ("c^d = %d", c^d);
}

程序运行结果如下:

(2)左移右移(<< >>)

运算规则:左移:规则,高位丢弃,低位补0.
                    右移:高位补符号位,低位丢弃。

如果要单纯的计算是数值,那么可以使用如下计算方式:

左移n位,就是乘以2的n次方
右移n位,就是除以2的n次方如果不够除了那么就直接归0
上面的计算方式仅仅适用于计算具体的数值,但是很多硬件操作中是使用位移这种方式来操作寄存器的,这个时候这种计算放式就不行了。

(3)交换两个数的数值的方法

交换两个数的数值有很多种方法,其中包括宏定义的方式,还有函数传址的方式。这里主要来说一下宏的操作方式。

主函数代码如下:

int main()
{
int a = 1;
int b = 2; SWAP1(a,b);
SWAP2(a,b);
SWAP3(a,b); return 0;
}

宏SWAP1的定义如下:

#define SWAP1(a,b) \
{ \
int temp = a; \
a = b; \
b = temp; \
}

宏SWAP2的定义如下:

#define SWAP2(a,b) \
{ \
a = a + b; \
b = a - b; \
a = a - b; \
}

宏SWAP3的定义如下:

#define SWAP3(a,b) \
{ \
a = a ^ b; \
b = a ^ b; \
a = a ^ b; \
}

第一种方法是比较普通的方法,我们相当于给程序找来一个杯子,然后利用这第三个杯子完成了另外两个杯子中内容的交换。

第二种方式是一种巧妙的运算,但是假如我们的a ,b 数值都很大的话,那么第二种方法不如第一种方法,因为第二种方法的第一步是a+b,假如a和b的数值都很大的话那么a+b的值很有可能导致程序溢出。

第三种方法是将两个数进行异或运算,最后实现两个数的交换,这种方法不会造成溢出,同时执行效率也很高,不过这种方式仅仅适用于整型数。
(4)在使用位运算符的时候一定要注意位运算符和其他运算符的优先级问题,例如:

#include <stdio.h>
#include <stdlib.h> int main()
{
int a = 0x1;
int b; b = 0x1<<2 + 3;
printf ("%d", b);
}

b = 0x1<<2 + 3。原来程序的本意可能是将0x1左移两位然后加3,但是程序实际的执行结果是将0x1左移5位。所以我们最好加上括号来区别开来。同时真心希望编译器能够让程序员们强制性的加上括号,没有优先级的C语言的世界该是多么的完美啊!

(5)在gcc编译器下如果我们将一个整数执行左移负位,那么实际上是进行右移操作。

#include <stdio.h>
#include <stdlib.h> int main()
{
int a = 0x1;
int b; b = 0x1<< (-1);
printf ("%d", b);
}

程序的执行结果是:b = 0

六、++,--操作符号

++,--操作符号至今我也没弄太懂,因为这里不单单是一个简单的运算操作问题,里面还涉及到顺序点的问题。

(1)陈正冲老师和唐老师都给了这样一个题:

i = 3;
(++i) + (++i) + (++i);

求上面执行完运算以后的结果是多少?

其他的编译器就不说了,我只说gcc编译下打印出来的值是16.原因是这样的,编译器先对前两个i进行自加操作,那么执行完前两个i以后,程序运行到10,然后再执行最后一个++i操作,程序最后运行结果是16.

(2)++操作符号和逗号表达式混合

#include <stdio.h>
#include <stdlib.h> int main()
{
int a;
int b = 1;
printf("%d\n",(++b, b++, b+10));
return 1;
}

程序最后的打印结果是13,也就是程序执行了最后一个表达式并且打印了,这里还有其他的问题,也就是在什么时候执行++,哪一个符号才是真正的顺序点,哪里才真正的开始执行++操作,这些问题我一直都没弄清楚,大神们希望给我指导。

#include <stdio.h>
int main()
{
int i = 2;
int b = 4;
int x; x = i+++b;
printf ("%d\n",i);
printf ("%d\n",b);
printf ("%d\n", x);
}

程序最后打印的是6,顺序点问题!

(3)在没有顺序点的前提下我们可以单纯的使用贪心法来进行计算

贪心法:

贪心法:使用贪心法i一直在吃+,但是当出现++i++的时候,这个时候是一个整数2++,这完全不符合C的规范。在编译器贪心的过程中,空格是一个很好的结束标志。

贪心法例程:

#include <stdio.h>
#include <stdlib.h> int main()
{
int a = 0;
int i = 0;
a = ++i+ ++i + ++i;
printf ("%d", a);
}

这里面涉及到的是顺序点和++运算问题,所以最后打印结果是7

七、优先级和类型转换

1.优先级仍然是C语言中一大难点

C语言中的优先级问题总是把人搞晕,所以我们在编写程序的过程中一定要注意这一部分,同时也要不断的积累,不过唐老师说没有一个人会把这个优先级背下来,不过我真的想不到其他的好办法了。

2.类型的转换

(1)在算术运算式中,低类型转换为高类型 (2)在赋值表达式中,表达式的值转换为左边的变量类型 (3)函数调用中,实参转换为形参的类型 (4)返回值中,return返回值的类型转换为返回值类型

无论我们的函数return后面的是什么类型,函数的最终返回值都以函数的返回值类型为准。

例程:

#include <stdio.h>
#include <stdlib.h> int f()
{
double a = 10.0;
return a;
}
int main()
{
int b;
b = f();
printf ("%d", b);
}

程序最后打印结果为10

(5)函数的隐式类型转换的方向

例程:

int main()
{
int i = -2;
unsigned int j = 1;
if ((i + j) > 0)
{
printf ("i + j > 0\n");
}
else
{
printf ("i + j < 0\n");
}
printf ("%d\n",(i + j));
return 0;
}

程序最后的运行结果:

分析:i为int型的,j为 unsigned int型的,所以它们两个相加根据那个截图可知 都应该转换成为unsigned型的,又因为-2无符号补码是一个很大的值,同时再+1,是一个比较大的值,所以最后i + j>0;但是最后输出是-1的原因是因为%d起的作用,%d输出的是整型,自然而然的将输出的数转换为-1。

《C语言深度剖析》学习笔记----C语言中的符号的更多相关文章

  1. C语言深度剖析学习错误点记录

    0. static修饰变量和函数 static修饰变量,1)限定作用域,本文件内.全局变量(自定义起,本文件前面要用需extern声明),局部变量函数内:2)生命周期,程序运行期间一直保存. stat ...

  2. C语言数据结构基础学习笔记——C语言基础

    抽象数据类型(ADT)是指一个数学模型以及定义在该模型上的一组操作,通常用(数据对象,数据关系,基本操作集)这样的三元组来表示抽象数据类型. 数据结构是相互之间存在一种或多种特定关系的数据元素的集合, ...

  3. 读书笔记之:C语言深度剖析

    读书笔记之:C语言深度剖析 <C 语言深度解剖>这本书是一本“解开程序员面试笔试的秘密”的好书.作者陈正冲老师提出“以含金量勇敢挑战国内外同类书籍”,确实,这本书中的知识点都是一些在面试中 ...

  4. C语言深度解剖读书笔记

    开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有一段时间了,一直没有时间来写这篇博客.正巧还刚刚看完了国嵌唐老师的C语言视频,觉得两者是异曲同工,所以就把两者一起记录下 ...

  5. [java学习笔记]java语言核心----面向对象之this关键字

    一.this关键字 体现:当成员变量和函数的局部变量重名时,可以使用this关键字来区别:在构造函数中调用其它构造函数 原理:         代表的是当前对象.         this就是所在函数 ...

  6. [java学习笔记]java语言核心----面向对象之构造函数

    1.构造函数概念 特点: 函数名与类名相同 不用定义返回值类型 没有具体的返回值 作用:                给对象进行初始化 注意: 默认构造函数 多个构造函数是以重载出现的 一个类中如果 ...

  7. Java学习笔记:语言基础

    Java学习笔记:语言基础 2014-1-31   最近开始学习Java,目的倒不在于想深入的掌握Java开发,而是想了解Java的基本语法,可以阅读Java源代码,从而拓展一些知识面.同时为学习An ...

  8. IOS学习笔记07---C语言函数-printf函数

    IOS学习笔记07---C语言函数-printf函数 0 7.C语言5-printf函数 ------------------------- ----------------------------- ...

  9. IOS学习笔记06---C语言函数

    IOS学习笔记06---C语言函数 --------------------------------------------  qq交流群:创梦技术交流群:251572072              ...

  10. C语言深度解剖读书笔记(6.函数的核心)

    对于本节的函数内容其实就没什么难点了,但是对于函数这节又涉及到了顺序点的问题,我觉得可以还是忽略吧. 本节知识点: 1.函数中的顺序点:f(k,k++);  这样的问题大多跟编译器有关,不要去刻意追求 ...

随机推荐

  1. ASP.NET MVC 中@html.ActionLink的几种参数格式

    一 Html.ActionLink("linkText","actionName") 该重载的第一个参数是该链接要显示的文字,第二个参数是对应的控制器的方法, ...

  2. SQL Server 内存管理在64位时代的改变

    64位机上  地址空间比以前大了去了.它引起的改变多了去了 1.MemToLeave这个词不存在了.因为SQL Server以不再做这种预留空间的事了,也就是说multiple page 想用多少就用 ...

  3. TCP协议: SYN ACK FIN RST PSH URG 详解

    TCP的三次握手是怎么进行的了:发送端发送一个SYN=1,ACK=0标志的数据包给接收端,请求进行连接,这是第一次握手:接收端收到请求并且允许连接的话,就会发送一个SYN=1,ACK=1标志的数据包给 ...

  4. MySQL_数据分页查询(limit用法)

    取前5条数据 select * from table_name limit 0,5 或 select * from table_name limit 5 取第11条到第15条数据,共5条 select ...

  5. mysql perl 抓取update语句

    <pre name="code" class="html"><pre name="code" class="ht ...

  6. C语言的本质(27)——C语言与汇编之计算机结构

    现代计算机都是基于冯·诺依曼或哈佛体系结构的,不管是嵌入式系统.个人电脑还是服务器.这种两种体系结构的主要特点是:CPU和内存是计算机的两个主要组成部分,内存中保存着数据和指令,CPU从内存中取指令执 ...

  7. 【POJ 1182 食物链】并查集

    此题按照<挑战程序设计竞赛(第2版)>P89的解法,不容易想到,但想清楚了代码还是比较直观的. 并查集模板(包含了记录高度的rank数组和查询时状态压缩) *; int par[MAX_N ...

  8. openstack 使用cloud init 和 console-log, nbd或者libguestfs 获取VM中的硬件信息。

    以获取PCI的信息为例. 基本代码: pci.py import base64 import guestfs from functools import partial import os impor ...

  9. OpenStack cloudCompute glassary术语project,tenant,user

    1,tenantA group of users, used to isolate access to Compute resources(一组用户,用于隔离访问计算资源). An alternati ...

  10. 采用dlopen、dlsym、dlclose加载动态链接库【总结】

    摘自http://www.cnblogs.com/Anker/p/3746802.html 采用dlopen.dlsym.dlclose加载动态链接库[总结]   1.前言 为了使程序方便扩展,具备通 ...