Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15253
  • 博文数量: 10
  • 博客积分: 410
  • 博客等级: 下士
  • 技术积分: 110
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-27 00:54
文章分类
文章存档

2010年(7)

2009年(3)

我的朋友
最近访客

分类: C/C++

2010-01-07 23:43:24

16 除了在for语句中之外,在哪些情况下还要使用逗号运算符?

 逗号运算符通常用来分隔变量说明、函数参数、表达式以及for语句中的元素。下例给出了使用逗号的多种方式:
#include
#include
void main(void);

void main ()
{
    / * Here, the comma operator is used to separate three variable declarations.  * /
     int i, j, k;
   / * Notice how you can use the comma operator to perform
          multiple initializations on the same line.  * /

     i=0, j=1, k=2;
    printf("i= %d, j=%d, k= %d\n", i, j, k);
     / * Here, the comma operator is used to execute three expressions
          in one line: assign k to i, increment j, and increment k.
          The value that i receives is always the rigbtmost expression.* /

     i= ( j++, k++ );
     printf("i=%d, j=%d, k=%d\n", i, j, k);
/ * Here, the while statement uses the comma operator to assign the value of i as well as test it.  * /
     while (i=(rand() % 100), i !=50)/*
随即数*/
        printf("i is %d, trying again... \n", i)
     printf ("\nGuess what? i is 50!\n" )
}

 
请注意下述语句:

     i
(j++k++)
   
这条语句一次完成了三个动作,依次为:

    (1)
k值赋给i。这是因为左值(lvaule)总是等于最右边的参数,本例的左值等于k。注意,本例的左值不等于k++,因为k++是一个后缀自增表达式,在把k值赋给j之后k才会自增。如果所用的表达式是++k,则++k的值会被赋给i,因为++k是一个前缀自增表达式,k的自增发生在赋值操作之前。
    (2)j
自增。
    (3)k
自增。
   
此外,还要注意看上去有点奇怪的while语句:
while (i=(rand() % 100), i !=50)
  printf("i is %d, trying again... \n");

 
这里,逗号运算符将两个表达式隔开,while语句的每次循环都将计算这两个表达式的值。逗号左边是第一个表达式,它把099之间的一个随机数赋给i;第二个表达式在while语句中更常见,它是一个条件表达式,用来判断i是否不等于50while语句每一次循环都要赋予i一个新的随机数,并且检查其值是否不等于50。最后,i将被随机地赋值为50,而while语句也将结束循环。

    请参见:
    1
12 运算符的优先级总能保证是自左至右自右至左的顺序吗?
    1
13 ++varvar++有什么区别?

17  怎样才能知道循环是否提前结束了?

    循环通常依赖于一个或多个变量,你可以在循环外检查这些变量,以确保循环被正确执行。请看下例:
int x
char * cp[REQUESTED_BLOCKS]
/ * Attempt (in vain, I must add... )to  allocate 512 10KB blocks in memory.  * /
for (x = 0;  x{
     cpi[x]= (char * ) malloc (10000,1)
     if (cp[x]= = (char * ) NULL)
           break
}
/ * If x is less than REQUESTED-BLOCKS, the loop has ended prematurely.  * /
if (x  printf ("Bummer ! My loop ended prematurely ! \n" );

 
注意,如果上述循环执行成功,它一定会循环512次。紧接着循环的if语句用来测试循环次数,从而判断循环是否提前结束。如果变量x的值小于512,就说明循环出错了。

18  gotolongjmp()setjmp()之间有什么区别?

     goto语句实现程序执行中的近程跳转(local jump)longjmp()setjmp()函数实现程序执行中的远程跳转(nonlocaljump,也叫farjump)。通常你应该避免任何形式的执行中跳转,因为在程序中使用goto语句或longjmp()函数不是一种好的编程习惯。
    goto
语句会跳过程序中的一段代码并转到一个预先指定的位置。为了使用goto语句,你要预先指定一个有标号的位置作为跳转位置,这个位置必须与goto语句在同一个函数内。在不同的函数之间是无法实现goto跳转的。下面是一个使用goto语句的例子:

void bad_programmers_function(void)
{
     int x
     printf("Excuse me while I count to 5000... \n") ;
     x=l

     while (1)
    {
           printf(" %d\n", x)
           if (x ==5000)
                 goto all_done
           else
                 x=x+1;
    }
all_done:
     prinft("Whew! That wasn't so bad, was it?\n");
}

如果不使用goto语句,是例可以编写得更好。下面就是一个改进了实现的例子:

void better_function (void)
{
     int x
     printf("Excuse me while I count to 5000... \n");
     for (x=1; x<=5000, x++)
           printf(" %d\n", x)
     printf("Whew! That wasn't so bad, was it?\n") ;
}

  
前面已经提到,longjmp()setjmp()函数实现程序执行中的远程跳转。当你在程序中调用setjmp()时,程序当前状态将被保存到一个jmp_buf类型的结构中。此后,你可以通过调用longjmp()函数恢复到调用setjmp()时的程序状态。与goto语句不同,longjmp()setjmp()函数实现的跳转不一定在同一个函数内。然而,使用这两个函数有一个很大的缺陷,当程序恢复到它原来所保存的状态时,它将失去对所有在longjmp()setjmp()之间动态分配的内存的控制,也就是说这将浪费所有在longjmp()setjmp()之间用malloc()calloc()分配所得的内存,从而使程序的效率大大降低。因此,你应该尽量避免使用longjmp()setjmp()函数,它们和goto语句一样,都是不良编程习惯的表现。

   
下面是使用longjmp()函数和setjmp()函数的一个例子:

#include
#include
jmp_buf saved_state;
void main(void);
void call_ longjmp (void);

void main(void)
{
     int ret_code;
     printf("The current state of the program is being saved... \n");
     ret_code = setjmp (saved_state)
     if (ret_code ==1)
     {
           printf("The longjmp function has been called. \n" )
           printf("The program's previous state has been restored. \n");
           exit(0)
     }
     printf("I am about to call longjmp and\n");
     printf('return to the previous program state... \n" )
     call_ longjmp ( )
}
void call_longjmp (void)
{
     longjmp (saved_state, 1 )
}

19 什么是左值(lvaule)?

左值是指可以被赋值的表达式。左值位于赋值语句的左侧,与其相对的右值(rvaule,见 111)则位于赋值语句的右侧。每条赋值语句都必须有一个左值和一个右值。左值必须是内存中一个可存储的变量,而不能是一个常量。下面给出了一些左值的例子:

int x;
int *p_int;
x=1;
   p_int=5;

 
变量x是一个整数,它对应于内存中的一个可存储位置,因此,在语句“x1”中,x就是一个左值。注意,在第二个赋值语句“*p_int5"中,通过“*”修饰符访问p_int所指向的内存区域;因此,p_int是一个左值。相反,下面的几个例子就不是左值:

#define CONST_VAL 10
int x
/* example 1 * /
l=x;
/ * example 2 * /
CONST_VAL = 5;

 
在上述两条语句中,语句的左侧都是一个常量,其值不能改变,因为常量不表示内存中可
存储的位置。因此,这两条赋值语句中没有左值,编译程序会指出它们是错误的。

    请参见:   
    1. 10
数组(array)可以是左值吗?   

    1. 11
什么是右值(rvaule)?

110 数组(array)可以是左值吗?

  19中,左值被定义为可被赋值的表达式。那么,数组是可被赋值的表达式吗?不是,因为数组是由若干独立的数组元素组成的,这些元素不能作为一个整体被赋值。下述语句是非法的:
    int x[5]
y[5];
    x=y

    不过,你可以通过for循环来遍历数组中的每个元素,并分别对它们赋值,例如:
    int i

    int x[5];
    int y[5];
    ......
    for(i=0; i<5
i++)
    x[i]
y[i]

    ......

   
此外,你可能想一次拷贝整个数组,这可以通过象memcpy()这样的函数来实现,例如:
    memcpy(x
ysizeof(y))

   
与数组不同,结构(structure)可以作为左值。你可以把一个结构变量赋给另一个同类型的结构变量,例如:   
    typedef struct t_name
    {
    charlast_name[25]
   

   char first_name[15];
    char middle-init [2];
} NAME
...
NAME my_name, your_name;
...
your_name = my_name;
...

  
在上例中,结构变量my_name的全部内容被拷贝到结构变量your_name中,其作用和下述语句是相同的:

    memcpy(your_name
my_namesizeof(your_name);

    请参见:   
    1
什么是左值
(lvaule)?
    1
11 什么是右值(rvaule)?

111 什么是右值(rvaule)?

19中,左值被定义为可被赋值的表达式,你也可以认为左值是出现在赋值语句左边的表达式。这样,右值就可以被定义为能赋值的表达式,它出现在赋值语句的右边。与左值不同,右值可以是常量或表达式:例如:   
    int X
y;
    x = 1;    /*  1 iS an rvalue,  x is an lvalue  */
    y
(x+1);    /*  (x+1)is an rvaluey is an lvalue  */

    19中已经介绍过,一条赋值语句必须有一个左值和一个右值,因此,下述语句无法通过编译,因为它缺少一个右值:
    int x;
    x=void_function_call();  /* the{unction void—function—call()
                               returns nothing */
   
如果上例中的函数返回一个整数,那么它可以被看作一个右值,因为它的返回值可以存储
 
到左值x中。

   
请参见:
    1
什么是左值(lvaule)?   
    1
10 数组可以是左值吗?  

112 运算符的优先级总能保证是自左至右自右至左的顺序吗?

    对这个问题的简单回答是:这两种顺序都无法保证。C语言并不总是自左至右或自右至左求值,一般说来,它首先求函数值,其次求复杂表达式的值,最后求简单表达式的值。此外,为了进一步优化代码,目前流行的大多数C编译程序常常会改变表达式的求值顺序。因此,你应该用括号明确地指定运算符的优先级。例如,请看下述表达式:
    a=b+c/d/function—call() * 5
   
上述表达式的求值顺序非常模糊,你很可能得不到所要的结果,因此,你最好明确地指定运算符的优先级:
    a
b+(((c/d)/function—call())* 5)
   
这样,就能确保表达式被正确求值,而且编译程序不会为了优化代码而重新安排运算符的优先级了。

113  ++varvar++有什么区别?

  “++”运算符被称为自增运算符。如果“++”运算符出现在变量的前面(++var),那么在表达式使用变量之前,变量的值将增加1。如果“++”运算符出现在变量之后(var++),那么先对表达式求值,然后变量的值才增加1。对自减运算符(--)来说,情况完全相同。如果运算符出现在变量的前面,则相应的运算被称为前缀运算;反之,则称为后缀运算。
   
例如,请看一个使用后缀自增运算符的例子:
    int x, y;
    x=1;   
    y=(x++* 5)

   
上例使用了后缀自增运算符,在求得表达式的值之后,x的值才增加1,因此,y的值为1乘以5,等于5。在求得表达式的值之后,x自增为2
   
现在看一个使用前缀自增运算符的例子:
    int x, y;
    x=1;
    y=(++x*5);
   
这个例子和前一个相同,只不过使用了前缀自增运算符,而不是后缀自增运算符,因此,x的值先增加1,变为2,然后才求得表达式的值。这样,y的值为2乘以5,等于10

114 取模运算符(modulus operator)“的作用是什么?

取模运算符的作用是求两个数相除的余数。例如,请看下面这段代码:
    x=15/7;
   
如果x是一个整数,x的值将为2。然而,如果用取模运算符代替除法运算符"/",得到的结果就不同了:
    X=15
7;
   
这个表达式的结果为15除以7的余数,等于1。这就是说,15除以721

   
取模运算符通常用来判断一个数是否被另一个数整除。例如,如果你要打印字母表中序号为3的倍数的字母,你可以使用下面这段代码:
    int x;
    for(x
1; x<=26; x++)
    if((x%3)
==
0)
       printf("%c"; x+64);  

上例将输出字符串"cfilorux",即字母表中序号为3的倍数的所有字母。 

阅读(1563) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~