《你必须知道的495个C语言问题》笔记--表达式

1.怎样才能避免这些未定义的求值顺序问题呢?

有几条简单的规则:

1.确保一个表达式最多只修改一个对象:一个简单变量、一个数组或者一个指针指向的位置。

2.如果一个对象在一个表达式中出现一次以上而且在表达式中被修改,则要确保对该对象的所有读访问都被用于计算它的

最终值。这条规则允许表达式i=i+1,尽管i出现了两次而且被修改了,但对i的旧值读取是用于计算i的新值。

3.如果想破坏第一条规则,就要确保修改的对象互不相同。同时,尽量限制到最多2至3个修改并参照下面例子的风格。在

这条规则下,c=*p++是合法的。因为修改了两个对象。类似的,c=a[i++]和a[i++]=c也是允许的。

4.如果在两次修改或修改和访问之间置入定义的序列点操作符(||,&&,?:或者逗号),则可以破坏第一条规则和第二条

规则。如:

(c = getchar()) != EOF && c != ‘\n‘

2.需要根据条件把一个复杂的表达式赋给两个变量中的一个。可以用下面这样的代码吗?

((condition)?a:b) = complicated_expression;

不能,?:操作符跟多数操作符一样,可以生成一个值,而不是被赋值,换言之,?:不能生成一个左值。如果真的需要,可以

试试下面这样的代码:

*((condition)?&a:&b) = complicated_expression;

实践:

#include <stdio.h>

int main(void)
{
        int x = 1,a = 0, b = 0;
        (x>0)?a:b = 1;
        printf("%d,%d\n",a,b);
        return 0;
}

编译时:

[[email protected] ~]# gcc a.c

a.c: In function ‘main‘:

a.c:6: error: invalid lvalue in assignment

修改后:

#include <stdio.h>

int main(void)
{
        int x = 1,a = 0, b = 0;
        *((x>0)?&a:&b) = 1;
        printf("%d,%d\n",a,b);
        return 0;
}

运行结果:

1,0

3.无符号保护和值保护规则的区别在哪里?

在“无符号保护”规则下,提升的类型总是无符号的。这个规则的有点简单明了,但是结果可能会出人意料。

在“值保护”规则下,转换取决于原来类型的提升类型的实际大小。如果提升类型的确较大,就是说它可以用有符号值表达

原来类型的所有无符号值--则提升后的类型为有符号类型。如果这种类型的大小实际是一样的,则提升后的类型为无符号

型。

下面显示了无符号保护规则下可能出现的意外。

unsigned short us = 10;
int i = -5;
if (i > us){
	printf("whoops!\n");
}

在无符号保护规则下us被提升为unsigned int了。通常的整型提升规则标明,如果unsigned int和int出现在二元操作符两侧,

则两个操作数都会转换成unsigned int。因此也被转成unsigned int了。i的原值-5被转化成一个很大的无符号值,这个值比

10大,因此会打印whoops。

在值保护规则下,如果整型比短整型大,则us会被转换成普通型,而i则仍然是普通的整型。这样表达式不为真,也不打印

出任何内容。

可是值保护规则也不能防止所有的意外。详细可以参考以前写过的一个帖子:

http://blog.csdn.net/todd911/article/details/6643627

要避免意外,最好的办法是避免在同一个表达式中混用有符号和无符号变量。

《你必须知道的495个C语言问题》笔记--表达式

时间: 2024-10-13 16:22:01

《你必须知道的495个C语言问题》笔记--表达式的相关文章

《你必须知道的495个C语言问题》笔记--库函数

怎样把数字转为字符串(与atoi相反)?有itoa函数吗? 用sprintf就可以了: sprintf(string, "%d", number); 同理,也可以同sprintf把long型或浮点型转换成字符串(使用%ld或%f),也就是说,可以把sprintf看成是atol或者atof的 反函数. 怎样在日期上加n天?怎样取得两个日期的时间间隔? 第一个问题,mktime接受没有规范话的日期,所以可以用一个日期的struct tm结构,直接在tm_mday域上进行加减,然后 调用mk

《你必须知道的495个C语言问题》笔记--杂项

如何进行移位操作? 因为左移操作(<<)不会导致符号位出现缺位,不考虑符号位,低位补0即可.所以对于无符号和有符号数来说,均为逻辑左移. 右移操作(>>)会涉及到符号位出现缺位的问题,所以在有符号数的右移操作时要考虑符号位怎么补的问题.对于无符号数来说, 最左侧补0,即逻辑右移:对于有符号来说,最左侧补符号位,即符号右移. 实践: #include <stdio.h> int main(void) { unsigned rui; int ri; unsigned int

你必须知道的495个C语言问题,学习体会五

本文是 你必须知道的495个C语言问题 系列的第五篇,主要还是来了解下字符和字符串相关的东西,同时也会涉及内存相关的知识. 一般来说,字符串以及字符的处理都是编程语言中的常客,在C语言中,我们被给予了很好的灵活性,但是同时字符也成了最诡秘的处理单元. 在指针的内容里,我基本上讲解了字符串和字符数组的差异,所以这里就不再赘述了. 我们知道字符的英语翻译是character,这也是为什么字符类型为char的原因,我们使用单引号''来标示一个字符而使用双引号""来标示字符串.实际上,在C语言

《你必须知道的495个C语言问题》笔记--标准输入输出

getchar的返回值 这样的代码有什么问题: char c; while((c = getchar()) != EOF).... getchar返回值变量必须是int型.因为EOF通常定义为-1,二十进制为255的字符会被符号扩展,和EOF比较时会相等,从而 过早第结束输入. feof函数的使用 为什么这些代码最后一行复制了两遍? #include <stdio.h> #include <unistd.h> #include <fcntl.h> #define MAX

《你必须知道的495个C语言问题》笔记--数组和指针

一.如何动态分配多维数组? 1.分配一个指针数组,然后把每个指针初始化为动态分配的行 代码如下: int **array = (int **)malloc(ROW * sizeof(int*)); int i = 0; for(i=0; i<ROW; i++){ array[i] = (int *)malloc(COL * sizeof(int)); } 2.让数组的内容连续,但在后来重新分配行. 代码如下: int **array = (int**)malloc(ROW * sizeof(in

《你必须知道的495个C语言问题》读书笔记之第2章:结构、联合和枚举

1. Q:下面两个声明有何区别? struct x1 {...}; typedef struct {...} x2; A:第一种形式声明了一个“结构标签”,第2种形式声明了一个“类型定义”.前者在声明结构的实例时必须使用struct关键字,如"struct x1 a;",后者则不需要使用struct关键字,如"x2 b;".但这个区别在C++编译器和某些模仿C++的C编译器中已经完全不存在了,在C++中结构标签在本质上都自动声明为类型定义. 2. Q:在C语言中是否

《你必须知道的495个C语言问题》读书笔记之第4-7章:指针

1. Q:为什么我不能对void *指针进行算术运算? A:因为编译器不知道所值对象的大小,而指针的算法运算总是基于所指对象的大小的. 2. Q:C语言可以“按引用传参”吗? A:不可以.严格来说,C语言总是按值传参,你可以模拟按引用传参,定义接受指针的函数,然后在调用时使用&操作符.但C没有任何真正等同于按引用传参的东西. 3. Q:怎样在整型和指针之间进行转换?能否暂时把整数放入指针变量,或者相反? A:C标准中规定整数与指针之间的相互转换是实现定义的,因此没有了指针和整数之间无法修改就相互

《你必须知道的495个C语言问题》笔记

1.10 对于没有初始化的变量的初始值可以作怎样的假定?如果一个全局变量初始值为 "零", 它可否作为空指针或浮点零? 答: 具有 "静态" 生存期的未初始化变量 (即, 在函数外声明的变量和有静态存储类型的变量) 可以确保初始值为零, 就像程序员键入了 "=0" 一样. 因此, 这些变量如果是指针会被初始化为正确的空指针, 如果是浮点数会被初始化为 0.0 (或正确的类型, 参见第 5 章).具有 "自动" 生存期的变量 (

C#刨根究底:《你必须知道的.NET》读书笔记系列

一.此书到底何方神圣? <你必须知道的.NET>来自于微软MVP-王涛(网名:AnyTao,博客园大牛之一,其博客地址为:http://anytao.cnblogs.com/)的最新技术心得和感悟,将技术问题以生动易懂的语言展开,层层深入,以例说理.全书主要,包括了.NET基础知识及其深度分析,以.NET Framework和CLR研究为核心展开.NET本质论述,涵盖了.NET基本知识几乎所有的重点内容.全书分为5个部分,第1部分讲述.NET与面向对象,从底层实现角度分析了.NET如何实现面向