再读《C和指针》(笔记4)

1.移位操作
标准说明无符号值执行的所有移位操作都是逻辑移位,但对于有符号值,到底采用逻辑移位还是算是移位操作取决于编译器,可以编写一个测试程序测试。
2.位操作符
AND(与),OR(或),XOR(异或)。
例一:把指定的位设为1:
value = value | 1 << bit_number;
例二:把指定位清零:
value = value & (1 << bit_number);
例三:下面的表达式可以用来测试指定位是否为1,若为1,表达式非零:
value & (1 << bit_number)
例四:下面这个使用异或的例子很有代表性:

/*交换两个变量的值,但不得使用中间变量*/
void swap(int *a,int *b)
{
    *a = *a ^ *b;//表达式1
    *b = *a ^ *b;//表达式2
    *a = *a ^ *b;//表达式3
}

解释一下上面的细节:
首先,异或的定义是:两个位不同,结果为1,两个位相同,结果为0。
所以,a^a = 0,a^0 = a。假设*a存储的值为A,*b存储的值为B,
所以经过表达式1的运算后,*a所存储的值为A^B;
在求解表达式2的时候需要一个因式代换:*a = A^B,所以表达式2等价于:
*b = A ^ B ^ B;
由于两个相同的数异或值为0,所以B^B=0,又由于任何数与0异或结果为此数本身,所以A^0=A,于是*b就存入了A。
再看表达式3。注意此时*a存储的还是A^B,*b存储的已经是A了,所以表达式3等同于:
*a = A ^ B ^ A;
结果同上,自然就是B了。
这样就实现了*a *b不经过中间变量就交换了他们的值。

3.复合赋值符
a += expression;//把expression加到a,它的功能相当于下面:
a = a + (expression);
两种代码经现代编译器优化后并无多大差别,但如果是下面的语句呢:

a[2 * (y - 6*f(x))] = a[2 * (y - 6*f(x))] + 1;//方式1
a[2 * (y - 6*f(x))] += 1;//方式2

如果函数f没有副作用,则他们的效果是等同的。
但方式1中用于选择数组下标的表达式必须书写两次,而且由于编译器无从知道函数f是否具有副作用,所以它必须两次计算下标表达式,然而第二种形式效率更高,因为下标表达式只计算一次。

4.单目操作符(也就是只接受一个操作数的操作符)
!:对操作数执行逻辑反。如果操作数为真,则结果为假,如果操作数为假,则结果为真。实际上!产生一个整数,0或1。

~:对整型类型的操作数进行求补操作,操作数中所有原先为1的位变为0,所有原先为0的位变为1。

&:产生它的操作数的地址。如下的例子:

int a,*b;
b = &a;

&操作符取得变量a的地址,并把它赋值给指针变量b。

*:间接访问操作符。它与指针一起使用,用于访问指针所指向的变量。
像上面的例子,b的值是变量a的地址,但*b的值就是变量a的值了。

sizeof:这个操作符用来判断它的操作数的类型长度,以字节为单位。它的操作数可以是个表达式,也可以是两边加上括号的类型名。
sizeof(int) sizeof a (这个表达式等于:sizeof(a))
第一个表达式返回的字节数取决于机器,第二个表达式返回a这个变量所占的字节数。从定义上说,字符变量的长度为1个字节。
注意,如果sizeof的操作数是一个数组名时,它返回数组的长度,且以字节为单位。
由于判断表达式的长度并不会对表达式求值,所以sizeof(a=b+1)并没有向a赋值。

(类型)操作符被称为强制类型转换。它优先级很高,所以只改变表达式的第一个项目的类型。

++和–:他们都由两种使用方式,前缀和后缀,而且他们都只能作用于左值。
记住下面一句话:
前缀和后缀形式的增值操作符都复制一份变量值的拷贝,但前缀操作符在进行复制前,先增加变量的值,而后缀操作符是先复制变量的值,再增加变量的值。这些操作符的结果不是被他们所修改的变量,而是变量值的拷贝,所以这就是为什么下面这个语句是错误的原因:
++a = 10;
++a的结果是a值的一份拷贝,并不是变量,向一个值进行赋值当然是错误的。
再看看下面的例子:

int a,b,c,c;
a=b=10;//a和b都得到值10
c=++a;//++先增加a,再拷贝a,所以a增加到11,c得到的值为11
d=b++;//++先拷贝b,得到的拷贝值为10,再增加b,所以b增加到11,但d得到的是这个拷贝,为10

6.关系操作符
这里只说一下 ==

if(a == 0)//判断1
{}
if(0 == a)//判断2
{}

两种判断方式都是正确的,但方式1如果错误的写成下面:
if(a = 0)
这时判断语句永远成立,这常常导致BUG,而且不一定容易发现,而第二种书写方式就可以让编译器来替我们检查我们是否把‘==’写成了‘=’,因为给一个常量赋值是错误的,编译器可以检查出来。

7.算术转换
int a = 5000;
int b = 25;
long c = a*b;
注意,a*b是以整形进行计算的,如果在32位的机器上运行它,是没有错的,但在16位的机器上就会产生溢出,所以c就会被初始化为错误的值。
解决方案是执行乘法运算之前先把其中一个或两个转换为long。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言指针是C语言中非常重要的概念,它是程序设计中最基本的数据结构之一。指针可以用于在程序中访问和操作内存中的数据,它们是C语言中最强大、最灵活的工具之一。在本篇笔记中,我们将介绍C语言指针的基本概念和用法。 1. 指针的概念 指针是一个变量,它存储了一个内存地址。在C语言中,每个变量都有一个内存地址,指针可以用来指向这个地址。通过指针,我们可以访问和操作内存中的数据,这使得C语言非常灵活和强大。 2. 定义指针变量 在C语言中,定义指针变量有两个步骤。第一步是声明指针变量的类型,第二步是用"&"符号取得一个变量的地址,并将这个地址赋给指针变量。例如: ```c int num = 10; //定义一个整型变量num int *p; //声明一个指针变量p p = &num; //将num的地址赋给指针变量p ``` 这样,指针变量p就指向了变量num的地址。 3. 操作指针变量 有了指针变量之后,我们可以通过指针变量访问和操作内存中的数据。例如,要访问变量num的值,可以通过指针变量p来实现: ```c printf("num的值为:%d", *p); //输出num的值 ``` 在这个例子中,通过"*"符号来访问指针所指向的内存中存储的值。这个符号被称为“解引用符号”,它可以用来访问指针所指向的内存中存储的值。 4. 指针的运算 指针可以进行加、减运算,这种运算是基于指针的类型和指针所指向的内存的大小进行的。例如,如果定义了一个指向整型的指针变量p,那么p+1将指向下一个整型变量的地址。同样的,p-1将指向上一个整型变量的地址。 此外,指针还可以用来访问数组中的元素。例如: ```c int arr[5] = {1, 2, 3, 4, 5}; //定义一个整型数组 int *p; //声明一个指针变量 p = arr; //将数组的首地址赋给指针变量 printf("arr[0]的值为:%d", *p); //输出数组第一个元素的值 ``` 在这个例子中,将数组的首地址赋给了指针变量p,然后通过解引用符号来访问数组中的元素。 5. 指针的应用 指针是C语言中非常重要的概念,它可以用于很多方面。例如,可以通过指针来动态分配内存空间、访问硬件设备、实现复杂的数据结构等等。 总之,指针是C语言中非常重要的概念,程序员需要深入理解它的概念和用法,才能够在程序设计中发挥其强大的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值