《C Primer Plus》学习笔记——C预处理器和C库

1、在Unix系统中,尖括号告诉预处理器在一个或多个标准系统目录中寻找文件。双引号告诉预处理器先在当前目录(或文件名中指定的其他目录)中寻找文件,然后在标准位置寻找文件。

2、#undef指令:取消定义一个给定的#define

#define LIMIT 400
#undef LIMIT

3、条件编译

#ifdef、#else和#endif指令

#ifdef 宏名
  //语句段1
#else
  //语句段2
#endif

作用:当标识符已经被定义过(一般是用#define命令定义),则对语句段1进行编译,否则编译语句段2。

其中#else部分可以没有

#ifdef 宏名
  //语句段1
#endif

#ifndef指令

#ifndef 宏名
  //语句段1
#else
  //语句段2
#endif

作用:若标识符未被定义则编译语句段1,否则编译语句段2。

#else可以用于#ifdef和#ifndef中,但#elif不可以

#if和#elif指令

#if 表达式
    // 语句段1
[#else
    // 语句段2]
#endif
#if 表达式1
    // 语句段1
#elif 表达式2
    // 语句段2
#else
    // 语句段3
#endif

#error

指令将使编译器显示一条错误信息,然后停止编译

#line

指令可以重置由__LINE__和__FILE__宏报告的行号和文件名

#line 1000            //把当前行号重置为1000
#line 10 “cool.c”   //把行号重置为10,文件名重置为cool.c

#pragma

指令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息

实际的例子

#include <stdio.h>
#define LETTER 1
int main( int argc, char * argv[] )
{
    char str[20]="C Language",c;
    int i;
    i=0;
    while( (c=str[i]) != ‘\0‘ )
    {
        i++;
#if LETTER
        if( c>=‘a‘ && c<=‘z‘ )
        c=c-32;
#else
        if( c>=‘A‘ && c<=‘Z‘ )
        c=c+32;
#endif
        printf( "%c",c );
    }
    return 0;
}
运行的结果:C LANGUAGE

用条件编译命令的好处:采用条件编译,可以减少被编译的语句,从而减少目标的长度。当条件编译段比较多时,目标程序长度可以大大减少

4、qsort函数

原型:

void  qsort (void *base,size_t num,size_t width,int ( *compar)(const void *,const void*))

其中base是排序的一个集合数组,num是这个数组元素的个数,width是一个元素的大小,comp是一个比较函数

例子:对一个长为1000的数组进行排序时,int a[1000]; 那么base应为a,num应为 1000,width应为 sizeof(int),comp函数随自己的命名

qsort(a,1000,sizeof(int),compar);

由小到大排序:

int compar(const void*a,const void*b)
{
return *(int*)a-*(int*)b;
}

5、string.h库中的memcpy()和memmove()

memcpy

原型:

void *memcpy(void *dest, const void *src, size_t n);

从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

memmove()

原型:

void *memmove( void* dest, const void* src, size_t n );

从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

6、可变参数步骤:

(1)在函数原型中使用省略号

(2)在函数定义中创建一个va_list类型的变量

(3)用宏将该变量初始化为一个参数列表

(4)用宏访问这个参数列表

(5)用宏完成清理工作

例子

 1 #include <stdio.h>
 2 #include <stdarg.h>
 3 double sum(int, ...);
 4
 5 int main(void)
 6 {
 7     double s,t;
 8
 9     s = sum(3, 1.1, 2.5, 13.3);
10     t = sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1);
11     printf("return value for "
12            "sum(3, 1.1, 2.5, 13.3):                %g\n", s);
13     printf("return value for "
14            "sum(6, 1.1, 2.1, 13.1, 4.1, 5.1, 6.1): %g\n", t);
15
16     return 0;
17 }
18
19 double sum(int lim,...)
20 {
21     va_list ap;                   // declare object to hold arguments
22     double tot = 0;
23     int i;
24
25     va_start(ap, lim);            // initialize ap to argument list
26     for (i = 0; i < lim; i++)
27        tot += va_arg(ap, double); // access each item in argument list
28     va_end(ap);                   // clean up
29
30     return tot;
31 }

习题

1、两个数的调和平均数可用如下方法得到:首先对两数的倒数取平均值,最后再取倒数。使用#define指令定义一个宏“函数”执行这个运算。编写一个简单的程序测试该宏。

 1 #include <stdio.h>
 2
 3 #define FUN(X, Y) (1/(1/X+1/Y))
 4
 5 int main(void)
 6 {
 7  double m, n;
 8  scanf("%lf%lf",&m, &n);
 9  printf("FUN of %g and %g : %g\n",m, n, FUN(m, n));
10  return 0;
11 }

2、ANSI库这样描述clock()函数:

#include<time.h>

clock_t clock (void);

clock_t是在time.h中定义的类型。clock()函数返回处理器时间,其单位依赖于实现(如果无法得到或无法表示处理器时钟,该函数返回值-1)。而同样在time.h中定义的CLOCKS_PER_SEC是每秒的处理器时间单位个数。因此,求出两次调用函数clock()的返回值的差,再用CLOCKS_PER_SEC去除这个差值,结果就是以秒为单位的两次调用之间的时间间隔。在做除法之前,将值的类型指派为double类型,可以将时间精确到小数点以后。编写一个函数,接受一个时间延迟数作为参数,然后运行一个循环,直至这段时间过完。编写一个简单的程序测试该函数。

 1 #include <stdio.h>
 2 #include <time.h>
 3
 4 void delay(double time);
 5
 6 int main(void)
 7 {
 8  double time;
 9  printf("input a time(in seconds) to delay(q to quit):");
10  while( scanf("%lf",&time) == 1 )
11  {
12   delay(time);
13   printf("time is out!\n");
14   printf("input a time(in seconds) to delay(q to quit):");
15  }
16 }
17
18 void delay(double time)
19 {
20  double origin;
21  origin = clock() / CLOCKS_PER_SEC ;
22  while(clock() / CLOCKS_PER_SEC < origin + time);
23 }

3、编写一个函数。该函数接受下列参数:一个int数组的名称,数组大小和一个代表选取次数的值。然后函数从数组中随机选择指定数量的元素并打印它们。每个元素最多选择一次(模拟抽奖或挑选陪审成员)。另外,如果您的实现支持time()(在第12章中介绍)或其他类似函数,可在srand()中使用这个函数的输出来初始化随机数生成器rand()。编写一个简单的程序测试该函数。

 1 #include <stdio.h>
 2 #include <time.h>
 3 #include <stdlib.h>
 4
 5 #define M 20
 6
 7 void random_pick(int *p, int size, int number);
 8
 9 int main(void)
10 {
11      int i, member[M], number;
12      srand(time(0));
13      printf("the M members are:\n");
14      for(i=0; i<M; i++)
15      {
16          member[i] = rand()%100;
17          printf("%d\t",member[i]);
18      }
19      printf("\ninput the size you want to pick (q to quit):");
20      while( scanf("%d", &number) == 1 )
21      {
22          random_pick(member, M, number);
23          printf("input the size you want to pick (q to quit):");
24      }
25 }
26
27 void random_pick(int *p, int size, int number)
28 {
29  int i,j,count,pick[M],temp,repeat; //pick数组用于存放已选取成员的下标
30  for(i=0, count=0; i<number; )
31  {
32   temp = rand() % size;
33   for(j=0, repeat=0; j<count; j++)   //去除重复数
34    if ( temp == pick[j] )
35    {
36     repeat = 1;
37     break;
38    }
39   if ( repeat == 0 )
40   {
41    pick[count++] = temp ;
42    i++;
43   }
44  }
45
46  printf("the picks are:\n");
47  for(j=0; j<count; j++)
48   printf("%d\t",p[pick[j]]);
49  printf("\n");
50 }

《C Primer Plus》学习笔记——C预处理器和C库,布布扣,bubuko.com

时间: 2024-10-16 12:12:21

《C Primer Plus》学习笔记——C预处理器和C库的相关文章

C PRIMER PLUS 学习笔记(一)

1显示程序执行的窗口一闪即逝.可以添加如下语句: getchar() 作用是获取键盘输入. 2 inf 和 nan float toobig = 3.4e38 * 100.0f; float not_a_number = asin(1.2);//math.h printf("%e \t %e\n", toobig,not_a_number); inf-表示无穷大,nan-Not-a-number 3 long double 的输出格式 long double x; x = 3.2e-5

C++ Primer Plus学习笔记之虚函数

C++ Primer Plus学习笔记之虚函数 C++语言的多态性有两种类型:静态多态性和动态多态性.函数重载和运算符重载就是静态多态性的具体表现,而动态多态性是指程序运行过程中才动态的确定操作所针对的对象,它是通过虚函数实现的: 1,虚函数的概念: 一个指向基类的指针可用来指向从基类派生的任何对象,这样就可以达到一个接口多个实现的访问了:虚函数是在基类中被冠以virtual的成员函数,它提供了一种接口界面.虚函数可以在一个或者多个派生类中被重新定义,但要求在派生类中从新定义时,虚函数的函数原型

C++ Primer Plus学习笔记之静态成员

C++ Primer Plus学习笔记之静态成员 关键字static可以用来说明一个类的成员(包括数据成员和成员函数),这样的成员被称为静态成员: 1,静态数据成员 在一个类中,若将一个数据说明前加上static,则该数据成为静态数据,静态数据成员被该类的所有对象共享.无论建立多少个该类的对象,都只有一个静态数据存储空间: 具体语法如下: 类型名 类名::静态数据成员[=常量表达式] 其中,常量表达式用于初始化类的静态数据成员: 静态数据成员属于类,而不属于对象,因为静态成员的存在是不依赖于某个

C++ Primer Plus学习笔记之继承类的初始化顺序

C++ Primer Plus学习笔记之继承类的初始化顺序 基类的构造函数,析构函数和操作符函数operator=是不能被派生类继承的: 那么,当创建一个派生类对象时,怎样调用基类的构造函数对基类的数据进行初始化呢??? 答案是:构造函数执行时遵行先兄长(基类),再客人(对象成员),后自己(派生类)的顺序: 另一方面,执行析构函数时,先执行派生类的析构函数,再执行基类的析构函数.原因是,对基类的破坏隐含了对派生类的破坏,所以派生类的析构函数必须先执行: #include<iostream> u

springmvc学习笔记(16)-异常处理器

springmvc学习笔记(16)-异常处理器 springmvc学习笔记16-异常处理器 异常处理思路 自定义异常类 全局异常处理器 错误页面 在springmvcxml配置全局异常处理器 异常测试 本文主要介绍springmvc中异常处理的思路,并展示如何自定义异常处理类以及全局异常处理器的配置 异常处理思路 系统中异常包括两类: 预期异常 运行时异常RuntimeException 前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发.测试通过手段减少运行时异常的发生. 系统的dao

C++ Primer Plus学习笔记之运算符重载

C++ Primer Plus学习笔记之运算符重载 1,成员函数和友元函数选择的建议 下面我们先看两个例子: 成员函数重载 #include<iostream> using namespace std; class Complex { public: Complex(double r=0,double i=0) { re=r; im=i; } Complex operator+(const Complex& obj); Complex operator!(); void Display

C++ Primer Plus学习笔记之拷贝构造函数

C++ Primer Plus学习笔记之拷贝构造函数 1,什么是拷贝构造函数 拷贝构造函数有两个含义: 首先,它是一个构造函数,当创建一个新对象时,系统自动调用它: 其次,它将一个已经定义过的对象的数据成员逐一对应的复制给新对象: 如果一个类没有显式定义拷贝构造函数,C++编译器可以为该类产生一个缺省的拷贝构造函数.这个缺省的拷贝构造函数采用C的方式,将拷贝对象的内存一个字节一个字节的拷贝到拷贝对象的内存中(内存拷贝): 2,拷贝构造函数的作用 (1)创建一个新对象,并将一个已存在的对象拷贝到这

C Primer Plus (第五版) 第十六章 C预处理器和C库 编程练习

第十六章 C预处理器和C库 开发一个包含您需要使用的预处理器定义的头文件 //max.h  #ifndef _MAX_H_     #define _MAX_H_     #define MAX(X, Y) ((X)>(Y)?(X):(Y)) #endif 2.两个数的调和平均数可用如下方法得到:首先对两数的倒数取平均值,最后再取倒数.使用#define指令定义一个宏"函数"执行这个运算.编写一个简单的程序测试该宏. #include <stdio.h> #defin

C Primer Plus之C预处理器和C库

编译程序前,先由预处理器检查程序(因此称为预处理器).根据程序中使用的预处理器指令,预处理器用符号缩略语所代表的内容替换程序中的缩略语. 预处理器不能理解C,它一般是接受一些文件并将其转换成其他文本. 翻译程序的第一步 对程序作预处理前,编译器会对它进行几次翻译处理. 编译器首先把源代码中出现的字符映射到源字符集(?).该过程处理多字节字符和使C外观更加国际化的三元字符(?)扩展 编译器查找反斜线(\)后紧跟换行符的实例并删除这些实例.注意:在这种场合下,“换行符”代表按下回车键在源代码文件中新