现在的位置: 首页 > 综合 > 正文

读朱兆祺攻破C语言之五—关键字、运算符、语句

2014年09月05日 ⁄ 综合 ⁄ 共 4010字 ⁄ 字号 评论关闭

下面文章来自朱兆祺编写的《攻破c语言笔试和机试难点》的pdf,为我们详尽讲解了C语言细节和难点问题。在此感谢这位牛人。

1.1 
    
static

1. 如程序清单4. 1所示,请问输出i、j的结果?
程序清单4.
1  static
#include <stdio.h>
static int  j ;
void fun1(void)
{
    static int i = 0 ;
    i++ ;
    printf("i = %d   " , i );
}
void fun2(void)
{
    j = 0 ;
    j++ ;
    printf("j = %d \n" , j );
}
int main(int argc, char *argv[])
{
   
    int k = 0 ;
    for( k = 0 ; k < 5 ; k++ ){
       fun1() ;
       fun2() ;
       printf("\n");
    }
   
    return 0;
}
很多人傻了,为什么呢?是啊,为什么呢?!
由于被static修饰的变量总存在内存静态区,所以运行这个函数结束,这个静态变量的值也不会被销毁,函数下次使用的时候仍然能使用这个值。
有人就问啊,为什么j一直是1啊。因为每次调用fun2()这个函数,j都被强行置0了。
static的作用:
(1) 函数体内
static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次, 因此其值在下次调用时仍维持上次的值;
(所以fun1中static
int i = 0 ;只执行一次
(2) 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
(3) 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明 它的模块内;
(4) 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;(个人未理解)
(5) 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。(个人未理解)

1.2 
    
for
循环的深究

1. 如程序清单4.
2
所示,输出结果是什么?
程序清单4. 2  for循环
#include <stdio.h>
int main(int argc, char *argv[])
{
    int  i = 0 ;
    for( i = 0 ,printf("First = %d  " , i ) ;
         printf("Second = %d  " , i ) , i < 10 ;
        i++ , printf("Third = %d  " , i ))
{
           printf("Fourth = %d  \n" , i) ;
}
    return 0;
}
这个题目主要考察对for循环的理解。我们先来看看运行程序会输出什么?
First = 0  Second = 0  Fourth = 0
Third = 1  Second = 1  Fourth = 1
Third = 2  Second = 2  Fourth = 2
从输出我们就可以知道程序到底是什么运行:
首先i = 0 , 所以输出:First = 0 ; 接着输出:Second = 0  ;  i < 10 成立,则输出:Fourth = 0 。就此完成第一个循环。接着 i ++ , 此时i = 1 ,输出:Third = 1 ;接着输出:Second = 1 ;i < 10 成立,则输出:Fourth = 1 ······以此类推。

1.3 
    
尺子——sizeof

1.   如程序清单4. 3所示,sizeof(a),sizeof(b)分别是多少?
程序清单4. 3  sizeof
#include <stdio.h>
int main(int argc, char *argv[])
{
    char  a[2][3] ;
    short b[2][3] ;
    printf( "sizeof(a) = %d \n" , sizeof( a ) ) ;
    printf( "sizeof(b) = %d \n" , sizeof( b ) ) ;
    return 0;
}
这个题目比较简单,由于char 是1个字节、short是2个字节,所以本题答案是:
sizeof(a) = 6
sizeof(b) = 12
好的,再来看看如程序清单4. 4所示,sizeof(a),sizeof(b)分别是多少?
程序清单4.
4  sizeof
#include <stdio.h>
int main(int argc, char *argv[])
{
    char  *a[2][3] ;
    short *b[2][3] ;
    printf( "sizeof(a) = %d \n" , sizeof( a ) ) ;
    printf( "sizeof(b) = %d \n" , sizeof( b ) ) ;
    return 0;
}
是数组指针呢,还是指针数组呢?这里涉及*和[]和优先级的问题。我告诉大家的是这两个数组存放的都是指针变量,至于为什么,在后续章节会详细解释,然而指针变量所占的字节数为4字节,所以答案如下。
验证结果如下:(使用TC然间验证输出结果是sizeof(a)=12,sizeof(b)=12)即指针变量在TC占2字节),下面使用ubuntu中GCC验证


1.4 
    
++i
i++

1.   或许大家都知道,++i是先执行i自加再赋值,但是i++是先赋值再自加,但是还有隐藏在后面的东西呢?
int i = 0 ;
int iNumber = 0 ;
iNumber = (++i) + (++i) + (++i) ;
C-Free编译输出是:7,有的编译器输出是:9。这两个答案都是对的,编译器不同所不同。7
= 2+2+3
9=3+3+3。区别在于答案是7的先执行(++i)+(++i)再执行+(++i),但是答案是9的是一起执行。
这只是前奏,先看几个让你目瞪口呆的例子。编译环境是VS2010
int i = 0 ;
int iNumber = 0 ;
iNumber = (i++) + (++i) + (++i) ;
printf( "iNumber = %d \n" , iNumber ) ;
这里输出是:4!!!4 = 1+1+2。
int i = 0 ;
int iNumber = 0 ;
iNumber = (++i) + (i++) + (++i) ;
printf( "iNumber = %d \n" , iNumber ) ;
这里输出是:4!!!4=1+1+2。
int i = 0 ;
int iNumber = 0 ;
iNumber = (++i)  + (++i) + (i++) ;
printf( "iNumber = %d \n" , iNumber ) ;
这里输出是:6!!!6=2+2+2。
这里至少能说明两个问题,其一,先执行前面两个,再执行第三个;其二,(i++)这个i的自加是最后执行!

1.5 
    
scanf()
函数的返回值

1. 
如程序清单4. 6所示,请问输出会是什么?
程序清单4.
6  scanf()
函数的返回值
int a , b ;
printf( "%d \n" ,
scanf(
"%d%d" , &a , &b) ) ;
输出输入这个函数的返回值?!答案是:2。只要你合法输入,不管你输入什么,输出都是2。那么我们就要深入解析scanf这个函数。scanf()的返回值是成功赋值的变量数量。

1.6 
    
const
作用下的变量

1.         阅读如程序清单4.
7
所示,想想会输出什么?为什么?
程序清单4.
7  const
作用下的变量
#include <stdio.h>
int main(int argc,char *argv[])
{
	const int s=10;
	int a=8;
	int *ps =(int *)(&s);
	int *pa=(int *)(&a);
	*ps=50;
	*pa=100;
	printf("s=%d,a=%d\n",s,a);
	return 0;
}
TC和GCC测试的答案一致,都是:50,100 而并不是10,100。即const关键字未起作用。
具体原因待查,分析可能是编译器问题。

这里补充一个知识点:(自己未验证

const int *p                   指针变量p可变,而p指向的数据元素不能变

int* const p                   指针变量p不可变,而p指向的数据元素可变

const int* const p            指针变量p不可变,而p指向的数据元素亦不能变

1.7 
   
*ptr++
*(ptr++)*++ptr*(++ptr)++(*ptr)(*ptr)++的纠缠不清

1. 如程序清单4.
8
所示程序,输出什么?
程序清单4. 8  *ptr++
int ia[3]={1,11,22};
int *ptr = ia;
printf("*ptr++=%d\n",*ptr++);
printf("*ptr=%d\n",*ptr);
纠结啊,是先算*ptr还是ptr++;还是纠结啊,ptr是地址加1还是偏移一个数组元素!
这里考查了两个知识点,
其一:*与++的优先级问题
其二,数组i++和++i的问题。
*和++都是优先级为2,且都是单目运算符,自右向左结合。所以这里的*ptr++和*(ptr++)是等效的。

首先ptr是数组首元素的地址,所以ptr++是偏移一个数组元素的地址。那么ptr++运算完成之后,此时的ptr是指向ia[1],所以第二个输出*ptr = 11 。如图4. 1所示。那么倒回来看第一个输出,ptr++是在执行完成*ptr++之后再执行的,所以,*ptr++
= 1 。
                       
如程序清单4. 9所示程序,输出会是什么?
程序清单4. 9  *++ptr
int  ia[3] = { 1 , 11 , 22} ;
int  *ptr       = ia;

printf( "*++ptr = %d \n" ,
*++ptr ) ;
printf( "*ptr   = %d \n" ,
*ptr   
) ;
这个解释和上面解释差不多,就是++ptr和ptr++的差别,所以这里的两个输出都是:11。同样的道理,*++ptr和*(++ptr)是等效。
再如程序清单4. 10所示,输出又会是什么?
程序清单4. 10  *ptr++
int  ia[3] = { 1 , 11 , 22} ;
int  *ptr       = ia ;
printf( "(*ptr)++ = %d \n" ,
(*ptr)++ ) ;
printf( "*ptr   = %d \n" ,
*ptr   
) ;
这个的输出是:1,2。
验证结果如下图:
               


【上篇】
【下篇】

抱歉!评论已关闭.