考试复习
今天被FPGA整麻了,先更新那么多
Chap 10 函数与程序结构
-
宏
#define 宏名标识符 宏定义
- 宏名一般用大写字母,以与变量名区别
- 宏定义不是C语句,后面不得跟分号
- 宏定义可以嵌套使用
- 宏定义可以写在程序中任何位置,它的作用范围从定义书写处到文件尾
- 可以通过“#undef”强制指定宏的结束范围
- 阅读带宏定义的程序,先全部替换好,最后再统一计算(宏没有自带括号)
-
文件包含
# include <需包含的文件名>//将使用C语言的标准头文件,即在系统的include文件夹中搜索 # include “需包含的文件名”//首先在当前工作文件夹中搜索,后在系统的include文件夹中搜索
-
编译预处理
编译预处理是C语言编译程序的组成部分,它用于解释处理C语言源程序中的各种预处理指令。文件包含(#include)和宏定义(#define)都是编译预处理指令。
C程序的编译处理,目的是把每一条C语句用若干条机器指令来实现,生成目标程序。
由于#define等编译预处理指令不是C语句,不能被编译程序翻译,需要在真正编译之前作一个预处理,解释完成编译预处理指令,从而把预处理指令转换成相应的C程序段,最终成为由纯粹C语句构成的程序,经编译最后得到目标代码。
编译预处理的主要功能
- 文件包含(#include)
- 宏定义(#define)
- 条件编译
-
条件编译
如果只想把源程序中一部分语句生成目标代码,可以使用条件编译。例如:
#define FLAG 1 #if FLAG 程序段1 #else 程序段2 #endif #if的条件只能是宏名,不能是表达式
-
文件模块间的通信
全局变量和局部变量是从空间的角度去看变量
-
局部变量
局部变量也叫自动变量(auto),它声明在函数开始,生存于栈,它的生命随着函数返回而结束
-
形式参数
作用域:整个函数体
-
函数体中定义变量
作用域:定义处至函数结束
-
for语句中定义变量
作用域:整个for语句
-
语句块中定义变量
作用域:定义处至语句块结束
如果没有初始化变量,那么局部变量的值是不可预料的
-
-
全局变量
全局变量声明在函数体外,一般应在函数前;每个函数都可以使用它,不过全局变量应尽量少用
作用域:从定义处至文件结束
对于char类型的变量,默认值’\0’,对于数值类型的变量,默认值是0
-
外部变量
全局变量只能在某个模块中定义一次,如果其他模块要使用该全局变量,需要通过外部变量的声明
外部变量声明格式为:extern 变量名表;
如果在每一个文件模块中都定义一次全局变量,模块单独编译时不会发生错误,一旦把各模块连接在一起时,就会产生对同一个全局变量名多次定义的错误
反之,不经声明而直接使用全局变量,程序编译时会出现“变量未定义”的错误
-
静态全局变量(static)
静态全局变量使全局变量只限于本文件引用,而不能被其他文件引用
-
外部函数
函数能够被程序中的其他程序文件模块调用,在其他文件模块中调用该函数前,声明为外部函数
extern 函数类型 函数名(参数表说明);(一般可省略extern,但调用时不能省略extern!!!) -
内部函数
使函数只能在本程序文件模块中被调用
static 函数类型 函数名(参数表说明);
-
-
PTA好题错题
-
1.定义带参数的宏“#define JH(a,b,t) t = a; a = b; b = t”,对两个参数a、b的值进行交换,下列表述中正确的是()。
A.不定义参数a和b将导致编译错误
B.不定义参数a、b、t将导致编译错误
C.不定义参数t将导致运行错误
D.不需要定义参数a、b、t类型
Answer:D
-
2.一个函数定义中可以完整地包含另一个函数的定义。
Answer:False 函数的定义不可以嵌套,但函数的调用可以嵌套
-
3.以下描述错误的是( )。
A.函数调用可以出现在执行语句中
B.函数调用可以出现在一个表达式中
C.函数调用可以作为一个函数的实参
D.函数调用可以作为一个函数的形参
Answer:D
-
4.以下正确的叙述是( )。
A.宏替换不占用运行时间,只占用编译时间
B.在程序的一行中可以出现多个有效的预处理命令行
C.使用带参数宏定义时,参数的类型应与宏定义时一致
D.宏定义不能出现在函数内部
Answer:A
-
5.以下
main()
函数中所有可用的变量为()。void fun(int x) { static int y; …… } int z; int main( ) { int a, b; fun(a); …… return 0; }
A.x, y
B.x, y, z
C.a, b, x, y, z
D.a, b, z
Answer:D y是静态局部变量,只能保证它在自己的函数里面好好地活着
-
6.阅读以下程序并回答问题。
-
Chap 8 指针
-
指针变量的定义
类型名 *指针类型名 int *p; //p 是整型指针,指向整型变量 //指针变量名是 p,不是*p //* 是指针声明符 float *fp; //fp 是浮点型指针,指向浮点型变量 char *cp; //cp 是字符型指针,指向字符型变量
-
指针的基本运算
- &:取地址运算符,给出变量的地址
- *:间接访问运算符,访问指针所指向的变量
#include <stdio.h> int main (void) { int a = 3, *p; p = &a; printf("a=%d, *p=%d\n", a, *p); *p = 10; printf("a=%d, *p=%d\n", a, *p); printf("Enter a: "); scanf("%d", &a); printf("a=%d, *p=%d\n", a, *p); (*p)++; printf("a=%d, *p=%d\n", a, *p); return 0; } /* a = 3, *p = 3 a = 10, *p = 10 Enter a: 5 a = 5, *p = 5 a = 6, *p = 6 */ /* - &*p 与 &a 相同,是地址, *&a 与 a 相同,是变量 - (*p)++ 等价于 a++,将 p 所指向的变量值加1 - *p++ 等价于 *(p++),先取 *p,然后 p 自加,此时p不再指向a */
-
赋值运算
int a = 3, *p1, *p2; p1 = &a; //把 a 的地址赋给 p1,即 p1 指向 a p2 = p1; //p2 也指向 a //相同类型的指针才能相互赋值
swap2 (&a, &b); void swap2 (int *px, int *py) { int t; t = *px; *px = *py; *py = t; } //值传递,地址未变,但存放的变量的值改变了
-
算术运算
double *p, *q;
q - p:两个相同类型的指针相减,表示它们之间相隔的存储单元的数目(不是字节)
p + 1 / p - 1:指向下一个存储单元 / 指向上一个存储单元
p < q:两个相同类型指针可以用关系运算符比较大小
指针相加、相乘和相除,或指针加上和减去一个浮点数都是非法的
-
字符串
-
字符数组与字符指针的区别
char sa[ ] = "This is a string"; char *sp = "This is a string"; strcpy (sa, "Hello"); sp = "Hello"; //sa = “Hello”; 非法 //数组名是常量,不能对它赋值
-
常用的字符串处理函数
-
输入
//1 char str[80]; i = 0; while((str[i] = getchar( )) != '\n') i++; str[i] = '\0'; //2 scanf("%s", str); //输入参数:字符数组名,不加地址符 //遇回车或空格输入结束,并自动将输入的一串字符和 ‘\0’ 送入数组中 //3 gets(str); //遇回车输入结束,自动将输入的一串字符和 ‘\0’ 送入数组中
-
输出
//输出参数可以是字符数组名或字符串常量,输出遇 '\0' 结束 //1 char str[80]; for(i = 0; str[i] != ‘\0 ’; i++) putchar(str[i]); //2 printf("%s", str); printf("%s", "hello"); //3 puts(str); puts("hello"); //输出字符串后自动换行
-
复制
strcpy(str1, str2); //将字符串 str2 复制到 str1 中
-
连接
strcat(str1, str2); //连接两个字符串str1和str2, 并将结果放入str1中
-
比较
strcmp(str1, str2); //按字典序(ASCII码序)比较两个字符串str1和str2的大小 //如果 str1 和 str2 相等,返回 0; //如果 str1 大于 str2 ,返回一个正整数; //如果 str1 小于 str2 ,返回一个负整数;
-
计算长度
strlen(str); //计算字符串的有效长度,不包括 ‘\0’
-
-
-
动态内存分配
void *malloc(unsigned size) //在内存的动态存储区中分配一连续空间,其长度为size //若申请成功,则返回一个指向所分配内存空间的起始地址的指针 //若申请内存空间不成功,则返回NULL(值为0) //返回值类型:(void *) //通用指针的一个重要用途 //将malloc的返回值转换到特定指针类型,赋给一个指针 /* 动态分配n个整数类型大小的空间 */ if ((p = (int *)malloc(n*sizeof(int))) == NULL) { printf(“Not able to allocate memory. \n”); exit(1); } void *calloc( unsigned n, unsigned size) //在内存的动态存储区中分配n个连续空间,每一存储空间的长度为size,并且分配后还把存储块里全部初始化为0 //若申请成功,则返回一个指向被分配内存空间的起始地址的指针 //若申请内存空间不成功,则返回NULL //malloc对所分配的存储块不做任何事情 //calloc对整个区域进行初始化,并设置值为0 void free(void *ptr) //释放由动态存储分配函数申请到的整块内存空间,ptr为指向要释放空间的首地址 //当某个动态分配的存储块不再用时,要及时将它释放 void *realloc(void *ptr, unsigned size) //更改以前的存储分配 //ptr必须是以前通过动态存储分配得到的指针 //参数size为现在需要的空间大小 //如果调整失败,返回NULL,同时原来ptr指向存储块的内容不变。 //如果调整成功,返回一片能存放大小为size的区块,并保证该块的内容与原块的一致。如果size小于原块的大小,则内容为原块前size范围内的数据;如果新块更大,则原有数据存在新块的前一部分。 //如果分配成功,原存储块的内容就可能改变了,因此不允许再通过ptr去使用它。
Chap11 指针进阶
指针数组
如果数组的各个元素都是指针类型,用于存放内存地址,那么这个数组就是指针数组。
一维指针数组定义的一般格式为:类型名 *数组名[数组长度]
char *color[5];
//color是一个数组,它有5个元素
//每个元素的类型都是字符指针
char *color[5] = {"red", "blue", "yellow", "green", "black" };
char *s = "hello";
printf("%s", s); //hello
printf("%c", *s); //h
printf("%s", s+2); //llo
printf("%c", *(s+2)); //l
printf("%c", *(s+2)+2); //n
二级指针
指向指针的指针(二级指针)一般定义为:类型名 **变量名
char *color[5] = {“red”, ”blue”, ”yellow”, ”green”, ”black”};
char **pc; /* 定义二级指针变量 */
pc = color; /* 二级指针赋值 */
/*
pc <=> color <=> &color[0]
*pc <=> color[0]
*(pc+i) <=> color[i]
**pc <=> *(*pc) <=> *color[0] : ‘r’
*/
printf("%s",*pc); //red
printf("%s", *pc+1); //ed
printf("%c", *(*pc+1)); //e
printf("%s", *(pc+2)); //yellow
printf("%s", pc[2]); //yellow
printf("%s", *(pc+2)+2); //llow
printf("%s", color[2]+2); //llow
printf("%c", *(color[2]+2)); //l
printf("%c", *(color[2]+2)+2); //n
printf("%c", **pc); //r
printf("%c", *color[0]); //r
printf("%c", **color); //r
//虽然指针的指针与数组指针均是二级指针,但它们的类型不同,不能相互赋值
//指针的指针指向的是指针变量
//数组的指针指向的是一个数组,注意是数组整体,而非数组首元素
二维数组
/*二维字符数组*/
char ccolor[ ][7] = {"red", "blue", "yellow", "green", "black"};
/*指针数组*/
char *pcolor[ ] = {"red", "blue", "yellow", "green", "black"};
//使用指针数组更节省内存空间
char *color[ ] = {“red”, ”blue”, ”yellow”, ”green”, ”black”};
//数组名color代表数组首元素color[0]的地址,是指向指针的指针(二级指针)
//color+2指向color[2] ,*(color+2)和color[2]等价
//color[0]指向字符串"red"的首字符r,color[0]+2指向首字符r后的第2个字符d
//color[k] <=> *(color+k)
//*(color[k]+j) <=> *(*(color+k)+j) <=> color[k][j]
命令行参数
C语言源程序经编译和连接处理,生成可执行程序(例如test.exe)后,才能运行。
在DOS环境的命令窗口中,输入可执行文件名,就以命令方式运行该程序。
输入命令时,在可执行文件(命令)名的后面可以跟一些参数,这些参数被称为命令行参数
命令行的一般形式为:命令名 参数1 参数2 … 参数n
命令名和各个参数之间用空格分隔,也可以没有参数
使用命令行的程序不能在编译器中执行,需要将源程序经编译、链接为相应的命令文件(一般以.exe为后缀),然后回到命令行状态,再在该状态下直接输入命令文件名
#include <stdio.h> /* test.c */
int main(int argc, char *argv[ ])
{ printf("Hello ");
printf("%s", argv[1]);
return 0;
}
//第1个参数 argc 接收命令行参数(包括命令名)的个数,不需要用户传递,它会根据用户从命令行输入的参数个数,自动确定
//第2个参数 argv 接收以字符串常量形式存放的命令行参数,它的第一个成员是用户运行的程序名字。
//如果要把中间含空格的字符串当成一个整体,就要用""将它们包起来
//char ** argv,char argv[ ][ ],char* argv[ ]三者是等价的