c语言复习
时间:2011-04-19 来源:qiang.xu
1. 副作用和顺序点
2. 输出输出缓冲区
3. 变量的声明和变量定义
4. 函数指针
5. const和define
6. 存储类,链接和内存管理
<1>. 副作用side effect和顺序点sequence point;
1.1 side effect和sequence point
对于下面的语句state = 10; 我们认为这是一个表达式(赋值语句在c语言中是表达式),表达式的值即是10,那么从何而来副作用?我们说c语言对于state = 10;的处理仅仅是想得到该语句的值,但是在计算该表达式值的过程中无意将state变量的值改变掉了,这既是所谓的side effect。
有了side effect之后,我们来看sequence point,所谓的sequence point即是在改点处所有的表达式的副作用将会被计算出来。在c语言中; ,&&都是一个顺序点,也就意味着一个语句中的所有赋值,增量等操作,将在下一个语句执行前完成。
1.2 深入理解
1.2.1 while中的顺序点和副作用
考虑下面的代码:
int guess = 0; while(guess++ < 10){
printf("guess is %d now\n", guess);
}
在while语句中的判断表达式guess++ < 10显然是一个完整的表达式,显然在执行printf之前,guess的值已经自增,同时使用后加的话,保证guess首先和10相比较,然后才是自增的过程。
1.2.2 ,号表达式中顺序点和副作用
下面的示例仅仅是为了演示顺序点和副作用,实际coding过程中很少会编写这样的代码。
x = ( y = 3, (z = ++y + 2) + 5 );由于,是顺序点,所以说上面的表达式首先将y赋值为3,然后++y(y=4),然后 计算z=6,最终计算x=11.
<2>. 输入输出缓冲区;
考虑下面的程序:
#include <stdio.h>#include <stdlib.h>
#define STRING_LENGTH 10
int main(void)
{
char str[STRING_LENGTH];
char ch;
printf("enter a string:");
scanf("%s", str);
printf("you enter the %s\ n", str);
printf("enter a char :");
scanf("%c", &ch);
printf("you entered %c\n", ch);
return EXIT_SUCCESS;
}
我们想要的结果是首先提示用户输出一个字符串,然后提示用户输入一个字符,但是运行程序的结果却是用户没有机会输入字符,在输入完字符串之后程序运行至结束。如果仔细查看程序输出的话,可以发现ch最终被赋值成为\n了。
这其中的主要原因是标准的io操作中使用了缓存机制(真是成也缓存,败也缓存),缓存大致上分为两类,完全缓存和行缓存。完全换群的话,只有在缓存区已满的情况下才被清空(发送至目的地),行缓存的话,是在每次遇到\n时将缓存区中的内容发送到目的地。在linux下的ioctl函数能够设置缓存模式,在标准的io中可以通过setbuf函数实现。
上面的程序首先得到输入的字符串,但是'\n'还留在了缓冲区中,那么在调用scanf("%c", &ch);时,scanf查看缓冲区中存在\n字符,所以直接将'\n'赋值到ch中.解决的方法可以通过使用下面的eat_line函数来实现,完整代码如下:
void eat_line(){
char ch;
while( (ch = getchar()) != '\n' )
continue;
}
<3>. 变量定义和声明;
见下面。
<4>. 函数指针;
下面是一个简单示例:
#include <stdio.h>#include <stdlib.h>
#include <string.h>
#define STRING_LENGTH 10
void eat_line()
{
char ch;
while( (ch = getchar()) != '\n' )
continue;
}
void sayhello(char* str)
{
printf(str);
}
void saygreet(char* str)
{
printf(str);
}
int main(void)
{
// test for function pointer
// declare the function pointer
void (*say)(char* );
// assignment
say = sayhello;
(*say)("hello to me");
say = saygreet;
say("greet to me");
return EXIT_SUCCESS;
}
上面的程序程序首先声明了函数指针void (*say)(char* );,然后首先将该函数指针赋值为sayhello,然后调用该函数指针所指向的函数的内容,(*say)("hello to me");
<5>. const和define;
5.1 const的用法
5.1.1 在函数原型中使用const表明在该函数中不改变参数的值
int sum(const int arr[], int length){
int tot = 0;
int i;
for(i = 0; i < length; ++i)
{
tot += arr[i];
}
return tot;
}
int main(void)
{
int arr[5] = {1, 2, 3, 4, 5};
printf("%d", sum(arr, 5));
return EXIT_SUCCESS;
}
5.1.2 定义常量
const int STRING_LENGTH = 10;char str[STRING_LENGTH];
5.1.3 const int* ptr和int* constr ptr, const int* const ptr;
上面的三个表达式是比较容易迷惑,可以采用“就近原则”记忆。
int arr[5] = {1, 2, 3, 4, 5};// const int (*ptr), int value
// can not change
const int * ptr1 = arr;
ptr1 = &arr[1];
// *ptr = 5; error
// int* (const ptr2)
int* const ptr2= arr;
*ptr2 = 5; // ok
// ptr2 = &arr[1]; error
const int* const ptr3 = arr;
// ptr3 = &arr[0]; error
// *ptr3 = 5; error
<6>. 存储类,链接和内存管理;
6.1 变量作用域
一般认为在c语言中变量的作用域分为代码块作用域,函数作用域,文件作用域。
代码块作用域,仅仅是在某个 代码块中存在,如下,虽然声明了两个变量i,但是由于是在不同的作用域{}中,所以分别显示10和5
int main(void){
{
int i = 10;
// 10
printf("%d\n", i);
}
{
int i = 5;
// 5
printf("%d\n",i);
}
return EXIT_SUCCESS;
}
6.2 变量存储域
c语言中存在两种存储域:静态存储时期和动态存储时期。静态存储时期使用static标识,是在程序的整个运行时期该变量均存在,但是动态存储时期的话,仅仅是在该变量的作用域中存在,并且是具有文件作用域。例如:
void static_varible_func(){
// init only once, global varible
static int a = 10;
a++;
printf("in function static_varible_func : %d\n", a);
}
int main(void)
{
int i;
for (i = 0; i < 10; ++i) {
static_varible_func();
}
static_varible_func();
return EXIT_SUCCESS;
}
上面的程序将输出11到20,而不是预想的10个11,主要是由于static变量仅仅初始化一次,然后每次在函数static_varible_func中使用的均是同一个变量。
int global_varible = 10;int main(void)
{
printf("%d\n", global_varible);
return EXIT_SUCCESS;
}
全局变量global_varible默认的是具有文件作用域的,仅仅能够在该文件中使用。
// file: varibles.c
// 该变量是能够被其他c文件使用的
extern int extern_global_var = 10;
// file:main.c// 声明该变量,该变量可能是定义在其他文件中,这里该变量是定义在varibles.c文件中
extern int extern_global_var; int main(void) { printf("%d\n", extern_global_var); return EXIT_SUCCESS; }6.3 定义和声明
变量的声明仅仅想编译器表明该变量的类型,但是并不分配内存空间,变量的定义的话,同时需要给该变量分配内存空间的。
extern int decl1; // this is a declaration
struct decl2
{
int member;
}; // this just declares the type – no variable mentioned
int def1 = 8; // this is a definition
int def2; // this is a definition