📄 c primer plus
字号:
// 输入的首个非空字符
char get_first(void);
// 输入一个整型
int get_int(void);
//
void count(void);
int main(void)
{
int choice;
void count(void);
while ( (choice = get_choice()) != 'q')
{
switch (choice)
{
case 'a' : printf("Buy low, sell high.\n");
break;
case 'b' : putchar('\a'); /* ANSI */
break;
case 'c' : count();
break;
default : printf("Program error!\n");
break;
}
}
printf("Bye.\n");
return 0;
}
void count(void)
{
int n,i;
printf("Count how far? Enter an integer:\n");
n = get_int();
for (i = 1; i <= n; i++)
printf("%d\n", i);
while ( getchar() != '\n')
continue;
}
char get_choice(void)
{
int ch;
printf("Enter the letter of your choice:\n");
printf("a. advice b. bell\n");
printf("c. count q. quit\n");
ch = get_first();
while ( (ch < 'a' || ch > 'c') && ch != 'q')
{
printf("Please respond with a, b, c, or q.\n");
ch = get_first();
}
return ch;
}
char get_first(void)
{
int ch;
ch = getchar();
while (getchar() != '\n')
continue;
return ch;
}
int get_int(void)
{
int input;
char ch;
while (scanf("%d", &input) != 1)
{
while ((ch = getchar()) != '\n')
putchar(ch); // 删除错误的输入
printf(" is not an integer.\nPlease enter an ");
printf("integer value, such as 25, -178, or 3: ");
}
return input;
}
第九章 函数
1、为什么使用函数?
第一、代码重用,写一次,调用多次,避免重复写代码;
第二、函数使得程序更模块化,可读性高。有些函数可能只调用一次也是有必要。
2、ANSI C 要求每个参数前声明其类型。
void dibs(int x, y, x) // 错误
{
//...
}
void dibs(int x, int y, int x) // 正确,前面的void意思是无返回值
{
//...
}
void dibs(x, y, z) // 这种方式已废弃
int x, y, z;
{
//...
}
void show_n() // 正确,但ANSI C标准将会删除此方式
{
//...
}
void show_n(void) // 推荐,可能旧的编译器不支持,后面的void表示无参数
{
//...
}
int pritnf(char *, ...); // ... 表示参数不确定,可能是一个或几个。参见 stdarg.h
3、函数声明、定义
方式1:先声明函数,后定义函数。推荐!
//int max(int, int); // 声明方式1
int max(int a, int b); // 声明方式2
void main(void)
{
int a = max(3, 5);
printf("%n", a);
}
//max(int a, int b) // 早期C允许不定义返回类型,默认为int,但C99不再支持默认类型
int max(int a, int b) // 定义函数
{
return (a > b) ? a : b;
}
方式2:在调用前定义函数。对于较小的函数可接受此方试。
int max(int a, int b) // 定义max()函数
{
return (a > b) ? a : b;
}
void main(void)
{
int a = max(3, 5); // 调用max()函数
printf("%n", a);
}
3、许多程序更倾向只在函数结尾使用一次return语句,因为这样做有利于理解执行流程。
4、递归分析
C函数地位同等,也就是一个函数可以调用其他任何函数,也可以调用自己。
就算是main()也可以被其他函数调用,只不过一般不会这么做。
/* recur.c -- recursion illustration */
#include <stdio.h>
void up_and_down(int);
int main(void)
{
up_and_down(1);
return 0;
}
void up_and_down(int n)
{
printf("Level %d: n location %p\n", n, &n); /* 1 */
if (n < 4)
up_and_down(n+1);
printf("LEVEL %d: n location %p\n", n, &n); /* 2 */
}
输出结果:
Level 1: n location 0012FE98
Level 2: n location 0012FDC0
Level 3: n location 0012FCE8
Level 4: n location 0012FC10
LEVEL 4: n location 0012FC10
LEVEL 3: n location 0012FCE8
LEVEL 2: n location 0012FDC0
LEVEL 1: n location 0012FE98
递归方式很消耗内存:每次递归调用需要把新的变量集合存储在堆栈中。
不过有些功能是无法用循环实现的,而采用递归方式很容易就能实现。
long Fibonacci(int n); // 计算斐波纳契数列值
{
if(n > 2)
return Fibonacci(n-1) + Pibonacci(n-2);
else
return 1;
}
采用双重递归,每级调用需要的变量数是上一级变量数的2位,也就是以指数规则增长,
指数增长的变量会占用大量内存,可能会导致程序瘫痪。
5、递归与循环
long fact(int n) // 使用循环计算阶乘
{
long ans;
for (ans = 1; n > 1; n--)
ans *= n;
return ans;
}
long rfact(int n) // 使用递归计算阶乘
{
long ans;
if (n > 0)
ans= n * rfact(n-1);
else
ans = 1;
return ans;
}
上面这两种实现方式,循环方式比递归更快些,因为递归方式需要更多内存,读写内存次数更多。
6、使用头文件
/* usehotel.c -- room rate program */
/* compile with Listing 9.10 */
#include <stdio.h>
#include "hotel.h" /* defines constants, declares functions */
int main(void)
{
int nights;
double hotel_rate;
int code;
while ((code = menu()) != QUIT)
{
switch(code)
{
case 1 : hotel_rate = HOTEL1;
break;
case 2 : hotel_rate = HOTEL2;
break;
case 3 : hotel_rate = HOTEL3;
break;
case 4 : hotel_rate = HOTEL4;
break;
default: hotel_rate = 0.0;
printf("Oops!\n");
break;
}
nights = getnights();
showprice(hotel_rate, nights);
}
printf("Thank you and goodbye.");
return 0;
}
/* hotel.h -- constants and declarations for hotel.c */
#define QUIT 5
#define HOTEL1 80.00
#define HOTEL2 125.00
#define HOTEL3 155.00
#define HOTEL4 200.00
#define DISCOUNT 0.95
#define STARS "**********************************"
// shows list of choices
int menu(void);
// returns number of nights desired
int getnights(void);
// calculates price from rate, nights
// and displays result
void showprice(double rate, int nights);
/* hotel.c -- hotel management functions */
#include <stdio.h>
#include "hotel.h"
int menu(void)
{
int code, status;
printf("\n%s%s\n", STARS, STARS);
printf("Enter the number of the desired hotel:\n");
printf("1) Fairfield Arms 2) Hotel Olympic\n");
printf("3) Chertworthy Plaza 4) The Stockton\n");
printf("5) quit\n");
printf("%s%s\n", STARS, STARS);
while ((status = scanf("%d", &code)) != 1 ||
(code < 1 || code > 5))
{
if (status != 1)
scanf("%*s");
printf("Enter an integer from 1 to 5, please.\n");
}
return code;
}
int getnights(void)
{
int nights;
printf("How many nights are needed? ");
while (scanf("%d", &nights) != 1)
{
scanf("%*s");
printf("Please enter an integer, such as 2.\n");
}
return nights;
}
void showprice(double rate, int nights)
{
int n;
double total = 0.0;
double factor = 1.0;
for (n = 1; n <= nights; n++, factor *= DISCOUNT)
total += rate * factor;
printf("The total cost will be $%0.2f.\n", total);
}
7、指针(pointer)
/* swap3.c -- using pointers to make swapping work */
#include <stdio.h>
void interchange(int * u, int * v);
int main(void)
{
int x = 5, y = 10;
printf("Originally x = %d and y = %d.\n", x, y);
interchange(&x, &y); // &x 表示变量x的所在内存单元的地址
printf("Now x = %d and y = %d.\n", x, y);
return 0;
}
// 互相交换两个整数
void interchange(int * px, int * py) // int * px 表示声明一个指针变量
{
int temp;
temp = *px; // *px 表示px指向的内存单元的值
*px = *py;
*py = temp;
}
第十章 数组和指针
1、数组声明、初始化
int powers[4] = {11, 20, 5, 3}; // 声明4个整数的数组,并初始化
// 这种初始化方式只有 ANSI C 支持这种初始化方式
// 如果您的编译器不支持,尝试一下在int前面加 static
for (i=0; i<4; i++) // 4 可以写成 sizeof(powers)/sizeof(powers[0]),编译器会计算为4
printf("%d ", powers[i]);
int arr1[5] = {0, 0, 0, 0, 22}; // 传统的初始化方式
int arr2[5] = {[4]=22}; // C99新增初始化方式,有些编译器还不支持
char str1[3] = {'a', 'b'}; // 声明3个字符的数组,并初始化
const char str2[12] = "string2";// 声明12个字符的常量数组,并初始化
printf("%s\n", str1);
printf("%s\n", str2);
在声明变量或数组时不初始化,是不会编译成指令的;在声明时初始化,实际上会被编译器编译成一些指令。
2、为数组赋值
#define SIZE 5
int oxen[SIZE] = {5, 3, 2, 8}; // 正确
int yaks[SIZE];
yaks = oxen; // 不允许
yaks[SIZE] = oxen[SIZE]; // 不正确
yaks[SIZE] = {5, 3, 2, 8}; // 不起作用
int i;
for (i=0; i<SIZE ; i++)
oxen[i] = i; // 正确
3、C程序不会自动检查数组边界,以确保运行速率更快,程序员必须避免越界问题。
4、指定数组大小
const int n = 5;
int m = 8;
float a1[5]; // 可以
float a2[5*2+1]; // 可以
float a3[sizeof(int)+1]; // 可以
float a4[-4]; // 不可以,数组大小必须大于0
float a5[0]; // 不可以,数组大小必须大于0
float a6[2.5]; // 不可以,数组大小必须是整数
float a7[(int)2.5]; // 可以,强制将float转成int
float a8[n]; // 可以,n为整数常量
float a9[m]; // 变长数组(VLA -- variable-length array),C99之前不允许
5、多维数组
// 二维数组:aar2为3行5列的二维数组,共有15个元素;初始化的内花括号可以省去
float arr2[3][5] = {
{1.1, 1.2, 1.3, 1.4, 1.5},
{2.1, 2.2, 2.3, 2.4, 2.5},
{3.1, 3.2, 3.3, 3.4, 3.5} };
// 三维数组,C还支持更多维数组
float arr3[3][4][6];
6、数组与指针
// pnt_add.c -- pointer addition
#include <stdio.h>
#define SIZE 4
int main(void)
{
short dates [SIZE];
short * pti;
double bills[SIZE];
double * ptf;
short index;
pti = dates; // assign address of array to pointer
ptf = bills;
printf("%23s %10s\n", "short", "double");
for (index = 0; index < SIZE; index ++)
{
printf("pointers + %d: %10p %10p\n",
index,
pti + index, // pti 是short指针,每加1,它的值就加sizeof(short),即加2字节
ptf + index // ptf 是double指针,每加1,它的值就加sizeof(double), 即加8字节
);
}
return 0;
}
输出结果类似:
short double
pointers + 0: 0012FF58 0012FF24
pointers + 1: 0012FF5A 0012FF2C
pointers + 2: 0012FF5C 0012FF34
pointers + 3: 0012FF5E 0012FF3C
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -