函数定义:函数头+函数体;函数不可以嵌套定义。
函数的封闭性:只能访问自己定义的变量。
函数间的联系:实参传值给形参,函数返回值给调用表达式。
程序必有唯一的主函数(main),程序的执行总是从主函数开始,并以其结束。(主函数的位置任意)
#include <stdio.h>
int max(int m, int n)
{
int t;
if(m>n)
t = m;
else
t = n;
return t;
}
int main()
{
int a, b,c, ans;
scanf("%d %d %d", &a, &b, &c);
ans = max(a,b);
ans = max(ans,c);
printf("The biggest number is %d\n", ans);
return 0;
}
无论简单或复杂,程序大多是 IPO结构(Input - Process - Output)
C语言是强类型语言,任何数据(常量、变量、表达式、函数调用等)必有确定的类型。
数据类型 决定数据的存储和行为:①数据的表示方式(存储形式)②数据的运算
基本数据类型: 整型(字符型)、浮点型
存储空间是有限,因此整型数也是有限的。(根据所占字节不同->表示的范围也不同)-3276832767、-21亿21亿
C语言规定: ( signed 、unsigned ) short
(int) <= int
<= long
(int)
short一般占2个字节,int一般占4个字节,long一般占4个字节
整型以补码形式存储:最高位为符号位,0表示正,1表示负 --> 正数变负数:取反后+1
char
字符型存储的是字符的ASCII编码,占1个字节。-128~127 或 0~255
字符以其编码(整数)存储,因此,在字符型和整型是一致的。字符可视为“1个字节的整型”,即”短短整型“
ASCII编码对应的字符:空格: 32 ;0~9: 48~57 ;A~Z: 65~90 ;az:97122 (小写字母 - 对应大写字母 = 32)
考点:大小写字母转换;数字字符和整数的转换。
float
double
浮点数是近似表示(尾数+阶码),整数才是精确表示,例:0.2+0.5==0.7(?)
double a = 0.3 + 0.4;
double b = 0.7;
if (a==b)
printf("ok");
else
printf("error"); //输出error
// if (fabs(a-b)<1e-6) 则ok 需要#include<math.h>
float:能表示6~7位有效数字(1.23*10^5 --> 有效数字为3位)
double:能表示更多的有效数字(精读更高)
C语言有逻辑类型吗?
任何类型数据均可作为逻辑值, 0表示假,非0 表示真;表达式为假,值为0;表达式为真,值为1。
*注意:(-2值为1,0.5值为1,'0'值为1,'\0'值为1)
一、字面值常量的表示
十进制(%d), 八进制(0为前缀,%o),十六进制(0x为前缀,%x) **注:**输入输出均无前导
后缀u:无符号,后缀L:long
*注意:(018错、34L对、123u对、0017对、0x错)
小数点 前/后 若为0,0可省略 (eg: 1.0 1. .1 )
默认为double型,加后缀F变为float型 (eg: 2.73F )
考点: 尾数E/e指数 表示: 指数必须整型,尾数与指数均不可省略(eg: 1.2E-6)
*注意:(1E2.0错、E2错、03E2对->八进制)
0.123*10^2 --> 12.3
-0.123*10^5 --> -12300.0
0.123*10^-2 --> 0.00123
【printf字符串中:%f %e】
【scanf字符串中:%f 对应float; %lf 对应 double 】
double a;
scanf("%lf", &a);
printf("%f", a)
1、单引号引起来的一个字符序列
2、转义字符
①单引号引起来,并且以 \ 开头的字符
②'\123' ->八进制对应的123字符
③'\x12' ->十六进制对应的12字符 ('\141' = '\x61' = 'a')
ok: 'a'、'5'、'\n'、'\t'、'\123'、'\0'、'0'、'\\'、'\''、' ' 、'?'、'$'
error: 'ab'、"a" 、'''、'\'
双引号括起来的0个或多个字符序列
严格区分字符和字符串,二者类型不同。
二、符号常量
定义格式:#define 符号常量名 符号常量值
#define PI 3.14
**注意:**预编译(宏定义)语句不是语句,不能加分号
采用符号常量的好处:易理解 ,不出错,易修改
具有标识符(变量名)的存储单元,其值可以改变。变量名、函数名、符号常量名,都是标识符。
标识符的规则:
*注意:3a错、a+3错、int错、main对、printf对
变量要 “先定义,后使用”.
定义变量的意义:确定变量名(访问方式)和类型(表示方式),分配内存空间。(变量的三属性:类型 变量名 值)
变量定义语句,可依次定义同类型的多个变量。( int a , b , c ; )
定义的变量,其值不确定,但是可对其初始化。未被初始化的变量其值为随机数
int a=1, b=a+2; // ok
int a=b,b=1; //error 违反“先定义后使用”的原则
int a = b=0; //error!注意两个=意义不同
int a;
a = 5.75;
printf("a=%d",a); //a=5
float a;
a = 123456789;
printf("a=%f",a); //a=123456715.000000 精读损失
int a,b;
scanf("%d %d", &a,&b);
printf("%d and %d is %d", a, b, a+b);
特别注意scanf :(1)变量名前加&;(2)输入时需匹配格式字符串:
scanf("a=%lf,b=%lf", &a, &b);
输入形式: a=12.5, b=23
ch = getchar(); putchar(ch-32);
scanf("%s", str); // gets(str) str前不加&
printf("%s", str); //puts(str)
作用:将多个运算数(常量、变量、函数调用)或表达式 连接起来,构成一个表达式
(1)优先级:
(2)结合性 :
(1) 自动转换:如果参与运算的操作数类型不一致,级别低的自动转为级别高的;
(2)强制转换:运算符sizeof()
无条件转换:char 转 int 、 float 转 double
(1)逻辑运算符 !取反.
(2)自增++ (自减 -- ) :对左值操作,有副作用
前缀和后缀的区别:副作用一样,但表达式的值不一样。(自增和取值的顺序不同)
前缀使用:先自增,再取值
后缀使用:先取值,再自增
n++; ++n; n=n+1; n+=1; //最终n的值相等
3++; 错 (3+n)++; 错 1+n++; 对
int n=1;
a=n++; //得a=1,n=2
a=(n++); //得a=2,n=2
int k=5;
t = -k++; //得t=-5,k=6 等价于 -(k++); 因为是右结合
//t = (-k)++; //语法错误,因为-k后,就不是左值了
int a=10, b;
b = a++ * 10; //b = ++a * 10;
printf("%d, %d", a, b); //11, 100 //11, 110
(3) sizeof :计算类型所占的字节数,其操作数即可为类型,也可为表达式
注意:sizeof()不是函数
int a=1;
double b=2;
printf("%d",sizeof(a+b)); //输出8,等价于printf("%d",sizeof(double))
printf("%d",sizeof(++a)); //输出4,等价于printf("%d",sizeof(int)),其后a值不变,依旧为1
printf("%d",sizeof(a=10)); //输出4,等价于printf("%d",sizeof(int)),其后a值不变,依旧为1
(4)(type) 强制类型转换,注意:括号的位置
double a = 5;
(int)a%2 // 1
(int)(a%2) //error
(double)1/2 //0.5
(double)(1/2) //0
int(3.2) //error
(5)其它
- (取相反数),*(间接访问),&(取地址)
/ 是重载的运算符:整数除和浮点除,注意分析进行除运算时,左右操作数的类型。
5 / 2 = 2 //整数除
5.0 / 2 = 2.5 //类型自动提升
int a=1;
(double)a/2 ; //值是0.5
(double)(a/2); //值是0
1.0*a/2; //值是0.5
a/2*1.0; //值是0
% 要求左右操作数必须是整型或字符型
double a=10;
(int)a%3; // ok
(int)(a%3); // error
5.0 % 2 //错误
5 % -2 = 1 //由被除数的符号决定
-5 % 2 = -1
-5 % -2 = -1
运算得逻辑值 0或1
0 <= a < = 10 //值为1
3 == 2+1 == 1+2 //值为0
C语言表达式,不是数学表达式!要学会从规则出发,像编译器那样去分析表达式
运算得逻辑值 0或1,! 优先级最高,&& 优先级大于||
a>=0 && a<=10 //a介于0~10
(y%4==0&&y%100!=0)||y%400==0 //判y为闰年,4年润但百年不润,400也润
!0>2 //值为0
!(0>2) //等价于 0<=2,值为1
*注意:!(-2) 值为0, 2+!5>3 值为0, !!3 值为1, !a<10 值为1, !(a<10) 值不确定,等价于a>=10
&& 和 ||的短路现象:作为二元运算先计算左 L 再右R ;所谓“短路”:只计算L,不(需)计算R
int a = 1
if( a>10 && ++a)
printf("NO ");
printf("%d", a);
//因短路,++a 并未执行,以上代码输出 1
int a;
scanf("%d",&a);
if (10/a>5) //输入0,语法错误
......
改:if(a!=0 && 10/a>5)
当表达式 E 作为逻辑值时,E 等价于 E != 0 ,!E 等价于 E == 0
例如:a && !b 等价于 a != 0 && b==0
(a<b?a:b) <= b //值为1
c = (a>b?a:b) //求a b中的较大者
+=、-=、/=、%= 表示在原来的基础上改变它的值(例:A+=B表示:在 A 的基础上加 B)
int a=2;
a *= 3 + 4; //则a=14,等价于 a *= (3 + 4)
3 + a = 5 //语法错误
3 + (a = 5) //a值为5,表达式值为8
int a=10,b;
a += a *= b = 2 ;
printf("%d, %d", a, b); // 40,2 注意三个赋值号,三次产生副作用
int a;
a = 1,2; //a的值为1
a = (1,2); //a的值为2
a = 1, a++, a += a; //a为4
例:输入时间 H:M , 然后输入 分钟数n,输出又原时间推移n分钟后的时间.
#include <stdio.h>
int main()
{
int h,m,n;
scanf("%d:%d", &h,&m);
scanf("%d", &n);
h += (n+m)/60 ;
m = (n+m)%60;
printf("%d:%d", h,m);
return 0;
}
结构化程序三结构:顺序分支循环
if (表达式E)
语句1
else
语句2
理解:
int a;
scanf("%d", &a);
if(a=3)
a++;
else
a--;
printf("%d", a); // 输出4,无论a输入何值
如果 if - else 中的语句2为空语句,则简化为: if 语句
if(表达式E)
语句1
int a=1,b=2, t=0;
if(a>b) t = a; a = b; b = t;
printf("%d, %d", a, b); // 2,0
用 { } 括起一条或多条语句,语法上是一条语句。
例:输入两个整数,将其从小到大输出。
#include <stdio.h>
int main()
{
int a, b,t;
scanf("%d %d", &a, &b);
if(a>b)
{
t = a;
a = b;
b = t;
}
printf("%d, %d", a, b);
return 0;
}
分支结构嵌套时,为避免二义性, C语言规定, else 总是与上面最近的的 if 匹配。
if(a>0) if(a>b) printf("1"); else printf("2");
// 上面的代码是如下结构:
if(a>0)
if(a>b)
printf("1");
else
printf("2");
if...else...else...是一种书写形式:if-else 的else部分嵌套if-else语句,它可以很清晰地表达多选一的情形。
例: 根据输入的整数,输出 符号函数 的值。
#include <stdio.h>
int main()
{
int x, t;
scanf("%d", &x);
// 下面是一条if-else 语句 实现三分支
if(x>0)
t = 1;
else if(x==0)
t = 0;
else
t = -1;
printf("Answer is %d \n", t);
return 0;
}
要求输入x的值,计算并输出y的值(输出结果保留小数点后2位)
#include <stdio.h>
#include <math.h>
int main()
{
double x, ans;
scanf("%lf", &x);
if(x<1)
ans = fabs(x);
else if(x<10) // (x>=1 && x<10)
ans = pow(x,3.6);
else
ans = sqrt(x);
/*
if(x<5)
y = x+5;
else if(x<10)
y = x*x +0.3*x;
else
y = 5*x-10;
*/
printf("Answer is %.2f \n", ans);
return 0;
}
例:百分制成绩转五级制等级。
int a; //百分制分数
char rank;
scanf("%d", &a);
// 五选一
if(a>=90)
rank = 'A';
else if(a>=80)
rank = 'B';
else if(a>=70)
rank = 'C';
else if(a>=60)
rank = 'D';
else
rank = 'E';
printf("Rank is %c\n",rank );
注意:以上结构是多分支选其一,依次检查逻辑条件,一旦为真则执行对应语句即跳出。
例:输入三边长度(整数),若能构成三角形则输出其面积,否则输出"Error”。
#include <stdio.h>
#include <math.h>
int main()
{
int a,b,c;
double s,area;
scanf("%d%d%d",&a,&b,&c);
if(a+b>c && a+c>b && b+c>a)
{
s = (a+b+c)/2.0;
area = sqrt(s*(s-a)*(s-b)*(s-c));
printf("%.2f",area);
}
else
printf("Error");
return 0;
}
例: 输入公里数,停车等候分钟数,根据以下收费规则,计算车费
int s,time;
int fee = 10;
scanf("%d %d", &s, &time);
if(s>3)
{
if(s<=10)
fee += (s-3)*2;
else
fee += 14+(s-10)*3;
}
fee += time/5*2;
printf("%d", fee);
例:分段计价:每月用电量50千瓦时(含50千瓦时)以内的,电价为0.53元/千瓦时;超过50千瓦时的,超出部分的用电量,电价上调0.05元/千瓦时。请编写程序计算电费
scanf("%d", &kw);
if(kw<=50)
fee = kw*0.53;
else
fee = 50*0.53+(kw-50)*0.56;
printf("%.2f",fee);
A ?B :C
意义:若A非0 ,值为B,否则为C
注意:A被视为逻辑值 等价于 A!=0
t = a>b ? a:b ; // a,b较大者 赋给t
t = a>=0 ? a:-a; // a的绝对值 赋给t
a ? 1 : 2 // 如果a!=0,值为1,否则2
int max(int m, int n) // 返回m,n之较大者
{
return m>n?m:n;
}
//符号函数
t = x>0?1:(x==0?0:-1) //括号可去掉
//a,b,c最大值=> t
t = a>=b && a>=c ? a : (b>=c ? b:c);
// 上述表达式与下面的代码等价
if(a>=b && a>=c)
t = a;
else
if(b>=c)
t = b;
else
t = c;
计算switch后的表达式,将其值与case 标号进行比较,从匹配的标号进入执行。
“标号”:形如"3:" "a:", 放在语句前来标识此语句的位置,“标号” 必须是(整型或字符)常量表达式。
switch语句执行过程:计算switch后的表达式,将其值与"case 标号"进行比较,从匹配的标号进入执行。
强调:
1. switch与break语句配合,方才实现多分支; (考卷 上一定会缺失break) break跳出switch结构
2.标号不可以重复;可以连续放置多个标号;
3. default可省略
while ( E ) 语句1
当E为真,则执行循环体(语句1)
理解:
例:分析下面的代码,建议记录进入循环以及循环中,变量的变换。
int main()
{
int a=1,b=10;
while(a<=b)
{
printf("%d, %d\n", a, b); // 1,10 3,10 4,8 6,8
if(a%3)
a++;
else
b-=2;
a++;
}
printf("Over: %d, %d\n", a, b); // Over: 7,6
return 0;
}
例:输入一个整数N,输出N的位数。
int main()
{
int n,k;
scanf("%d", &n);
k=0; // 循环计数
while(n!=0)
{
n /= 10;
k++;
}
printf("%d", k);
return 0;
}
例:输入一个整数 N,输出N的各位数之和。
#include <stdio.h>
int main()
{
int n,sum = 0;
scanf("%d", &n);
while(n!=0)
{
sum += n % 10;
n /= 10;
}
printf("sum is %d ", sum);
return 0;
}
例:输入一行字符,统计字母,数字,空格,其他字符的数量。
分析:一行字符,即以‘\n’为结束标识的若干字符。
int main()
{
char ch;
int alpha=0,dig=0,space=0,other=0;
ch = getchar();
while(ch!='\n')
{
if(ch>='a'&&ch<='z' || ch>='A'&&ch<='Z')
alpha++;
else if(ch>='0' && ch<='9')
dig++;
else if(ch==' ')
space++;
else
other++;
ch = getchar();
}
printf("%d, %d, %d, %d", alpha, dig, space, other);
}
例:输入两个整数,输出二者之间所有非3倍数之和。
int main()
{
int i,m,n,sum=0,t;
scanf("%d %d", &m, &n);
if(m>n)
{ t = m; m = n; n = t; }
for(i=m; i<=n; i++)
{
if(i%3!=0)
sum += i;
}
printf("%d", sum);
return 0;
}
例:输出所有水仙花数
int main()
{
int n,a,b,c;
for( n=100; n<=999; n++ )
{
c = n%10;
b = n/10%10;
a = n/100;
if(a*a*a+b*b*b+c*c*c == n)
printf("%d ", n);
}
return 0;
}
例: 输出1000以内的所有完全数。所谓完全数,它等于所有(不包括自身)的因数之和
int main()
{
int n,k,sum;
for(n=1; n<=1000; n++)
{
sum=0; // 每个n的因数累加器
for(k=1; k<n; k++)
{
if(n%k==0)
sum += k;
}
if(sum==n)
printf("%d ", n);
}
return 0;
}
例:定义函数:输出高度为n的金字塔
分析:规律图像都是二重循环,外循环控制各行,内循环输出各行字符。
void printStar(int n)
{
int i,k;
for(i=1;i<=n; i++)
{
for(k=1;k<=n-i;k++) // n-i个空格
putchar(' ');
for(k=1;k<=2*i-1;k++) // 2i-1个*
putchar('*');
putchar('\n'); // 记得换行
}
}
break 和 continue 语句
// 输出 1 2 4 5 7 8 10
for(i=1;i<=10 ;i++)
{
if(i%3==0) // 能被3整除
continue;
printf("%d ", i);
}
// 输出1 2
for(i=1;i<=10 ;i++)
{
if(i%3==0) // 能被3整除
break;
printf("%d ", i);
}
i=1;
while( i<=10 )
{
if(i%3==0) // 能被3整除
{
i++; // 没它,死循环!!!
continue;
}
printf("%d ", i);
i++;
}
例:求1-n的阶乘和
int main()
{
int n;
int i,t,k,sum;
scanf("%d", &n);
// 利用二重循环求各阶乘的和
// sum = 0;
// for(i=1;i<=n;i++)
// {
// t = 1; // i!总是从1开始累乘
// for(k=1;k<=i;k++)
// t *= k;
//
// sum += t;
// }
// 下面是利用的递推的思想求解
t = 1; //0!=1
sum = 0;
for(i=1;i<=15;i++)
{
t *= i; // i! = (i-1)! * i
sum += t;
}
printf("%d ", sum);
return 0;
}
例: a + aa + aaa + aaaa + aaaaaa + ... 求1个a到n个a的和。
int main()
{
int n,a,t,i,sum;
scanf("%d %d", &a, &n);
t = 0; // 0个a是0
sum = 0;
for(i=1; i<=n; i++)
{
t = t*10+a; // 递推
sum += t;
}
printf("%d ", sum);
return 0;
}
例:输出Fibonacci数列 1 1 2 3 5 8 13 21....的前30项,5个一行。
int main()
{
int a, b,c,i;
a = b = 1; //起点
printf("%8d%8d", 1,1);
for(i=3; i<=30; i++)
{
c = a+b;
printf("%8d", c);
if(i%5==0)
putchar('\n');
a = b;
b = c;
}
return 0;
}
例:2/1 3/2 5/3 8/5 13/8 .... 求其前20项之和i
int main()
{
double z,m,sum;
int i;
z = 2; m = 1; //递推起点
sum = 2;
for(i=2; i<=20; i++)
{
int t = m;
m = z;
z = t+z;
sum += z/m;
}
printf("%f", sum);
return 0;
}
题型:多变量组合的穷举
百钱百鸡问题。公鸡单价5,母鸡单价3,小鸡三只价1。 百钱买百鸡,问公鸡,母鸡,小鸡各多少?
分析: 多重循环穷举三个变量的所有组合。因小鸡,和母鸡确定后,公鸡数量即确定,所以二重循环即可。注意,小鸡数量是3的倍数,直接枚举小鸡数量。
int main()
{
int g,x,m; // 公鸡,小鸡,母鸡数量
for(x=0; x<=100; x+=3) // 小鸡数 0 3 6 9 ...
{
for(m=0; m<=100-x; m++)
{
g = 100-x-m;
if( 5*g+3*m+x/3 == 100 ) //满足百钱
printf( "%d %d %d\n", g, x, m ); //输出一组满足条件的解
}
}
return 0;
}
数组就是在内存中,以连续存储空间来存储多个元素。
定义数组 int a[ N ];
int a[] = {3,4,5,6,7};
printf("%d ", sizeof(a)/sizeof(int) ); // 5
访问数组元素:通过 a[ 下标 ] 来访问 ,下标从 0 - N-1
类型1:通过扫描数组,对数组元素进行统计和查询(查询,求和,计数,最...)
例:依次输入10个实数,将其最大和最小元素交换后输出。
#include <stdio.h>
#define N 10 // 符号常量
int main()
{
double a[N],t;
int i,maxIdx,minIdx; // 分别记录当前最大(最小)元素的下标
for(i=0; i<N; i++) // 依次输入数组元素值
scanf("%lf", &a[i]);
maxIdx = minIdx = 0; // 令0号元素最大(最小)
for(i=1; i<N; i++)
{
if(a[i]>a[maxIdx])
maxIdx = i;
if(a[i]<a[minIdx])
minIdx = i;
}
if(maxIdx != minIdx) // 交换
{
t = a[maxIdx]; a[maxIdx] = a[minIdx]; a[minIdx] = t;
}
for(i=0; i<N; i++) // 输出调整后的数组
printf("%8.2f",a[i]);
return 0;
}
例:输入10个整数,求其中奇数的均值。
#include <stdio.h>
#define N 10 // 符号常量
int main()
{
int a[N],t,oddSum;
int i,maxIdx,minIdx,cnt; // 分别记录当前最大(最小)元素的下标
for(i=0; i<N; i++)
scanf("%d", &a[i]);
oddSum = 0; // 奇数的累加器
cnt = 0; // 奇数的计数器
for(i=0;i<N;i++)
{
if( a[i]%2 )
{
oddSum += a[i];
cnt++;
}
}
if(cnt)
printf("%.2f", 1.0*oddSum/cnt); // 注意均值是浮点数
return 0;
}
类型二:数组转置
例: 输入9个数,将其逆序后输出。
#include <stdio.h>
#define N 4 // 符号常量
int main()
{
int a[N],t;
int i,j;
for(i=0; i<N; i++)
scanf("%d", &a[i]);
// i和N-1-i号元素是对称的
for(i=0; i<N/2; i++)
{ // 交换a[i] 和 a[N-1-i]
t = a[i];
a[i] = a[N-1-i];
a[N-1-i]=t;
}
// i和j号元素是对称的
for(i=0,j=N-1; i<j; i++,j--)
{ //通过赋值交换
int t = a[i];
a[i]=a[j]
a[j]=t;
}
for(i=0; i<N; i++)
printf("%d ", a[i]);
return 0;
}
类型三:通过递推,生成数组中的各元素。
例:输出Fibonacci数列的前20项,三个一行。
#include <stdio.h>
#define N 20 // 符号常量
int main()
{
int i;
int a[N] = {1,1};
for(i=2;i<N;i++)
a[i] = a[i-1] + a[i-2];
for(i=0;i<N;i++)
{
printf("%7d ", a[i]);
if((i+1)%3 == 0 )
putchar('\n');
}
return 0;
}
类型四: 排序
例: 冒泡排序
// 长度为n的数组a递增排序
void sort(int a[],int n)
{
int i,j;
for(i=1; i<n; i++) // n-1趟
for(j = 0; j<n-i; j++) //相邻元素比较,逆序则交换
if(a[j]>a[j+1])
{
t = a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
选择排序
思路:
...
#include <stdio.h>
#define N 10
int main()
{
int a[]= {4,26,1,11,67,92,32,47,21,42},t;
int i,j,kmin;
for(i=0; i<N-1; i++)
{
kmin=i; // 先认为i号元素最小
for(j=i+1; j<N; j++)
{
if(a[j]<a[kmin])
kmin=j;
}
if(i!=kmin)
{
t= a[i];
a[i]=a[kmin];
a[kmin]=t;
}
}
for(i=0; i<N; i++)
printf("%4d", a[i]);
return 0;
}
定义3行4列数组: int a[3][4]
,则 a[0] a[1] a[2]
分别是一行,即长度为4的一维数组。
定义二维数组时:
int a[][5]={1,2,3,4,5,6,7} // 2*5个元素
int a[][5] ={{1,2},{1,2,3}}; // 2*5个元素
题型1:通过按行,按列访问进行查询,统计
例:按行输出,按列输出
int a[3][2] = {1,2,3,4,5,6};
int i,j;//行和列下标
// 按行输出
for(i=0;i<3;i++) // 行下标作外循环变量
{
for(j=0;j<2;j++)
printf("%d ", a[i][j]);
putchar('\n');
}
// 按列输出
for(j=0;j<2;j++) // 列下标作外循环变量
{
for(i=0;i<3;i++)
printf("%d ", a[i][j]);
putchar('\n');
}
例:输出二维数组 a[N][M]
各行及各列的平均值
#include <stdio.h>
#define N 20
#define M 5
int main()
{
int a[N][M];
double sum;
int i,j;
for(i=0;i<N;i++)
{
sum=0;
for(j=0;j<M;j++)
sum += a[i][j];
printf("%f ", sum/M);
}
for(j=0;j<M;j++)
{
sum=0;
for(i=0;i<N;i++)
sum += a[i][j];
printf("%f ", sum/N);
}
return 0;
}
例:求二维数组中最大元素
#include <stdio.h>
#define N 3
#define M 4
int main()
{
int a[N][M]={{1,2,3},{4,17},{26}};
int i,j,maxi,maxj;
maxi=maxj=0;
for(i=0;i<N;i++)
for(j=0;j<M;j++)
if(a[i][j]>a[maxi][maxj])
{
maxi=i; maxj=j;
}
printf("%d ", a[maxi][maxj]);
return 0;
}
题型二 转置
例:二维数组(N*N方阵)转置。
分析: a[i][j]与 a[j][i]
关于主对角线对称,下三角形的 a[i][j]与 上三角形的a[j][i]
交换
for(i=0; i<N; i++)
{
for(j=0; j<i; j++) // a[i][j]只取下三角中的元素
{ // a[i][j]与 a[j][i]交换
int t = a[i][j];
a[i][j] = a[j][i];
a[j][i] = t;
}
}
例: 求N*N方阵的主副对角线元素之和。
s = 0;
for(i=0;i<N;i++)
s += a[i][i]+ a[i][N-1-i];
if(N%2) // N是奇数,减去重复计算的中间元素
s -= a[N/2][N/2];
题型三,利用二维数组递推
利用二维数组,输出杨辉三角形前10行
int main()
{
int a[10][10];
int i,j;
for(i=0;i<10;i++)
{
for(j=0;j<=i;j++) // 下三角的元素
{
if(j==0||i==j) // 首列和主对角线元素值为1
a[i][j]=1;
else
a[i][j] = a[i-1][j] + a[i-1][j-1]; // 等于正上方和左上方元素之和
printf("%4d",a[i][j]);
}
putchar('\n');
}
return 0;
}
函数是“独立”的功能模块,是C语言程序的基本单位。
// 判断n是否为素数,是则返回1,否则返回0
int isSx(int n)
{
int i;
int flag=1;
for(i=2; flag!=0 &&i<n; i++)
{
if(n%i==0)
flag=0;
}
return flag;
}
// 将100-200内所有偶数表示为两个素数之和
int main()
{
int n, a;
for(n=100; n<=200; n+=2)
for(a=2; ; a++)
{
if( isSx(a) && isSx(n-a) )// 调用函数判a和n-a是素数
{
printf("%d %d\n", a, n-a);
break;
}
}
return 0;
}
#include <stdio.h>
void disp(int b[],int n)
{
int i;
for(i=0;i<n;i++)
printf("%d ", b[i]);
}
// 从右边左边顺序查找,如果查找成功返回其下标,失败返回-1
int search(int a[],int n,int e)
{
int i;
for(i=n-1;i>=0;i--)
{
if(a[i]==e)
break;
}
return i;
}
// 折半查找
int bisearch(int a[],int n , int x)
{
int low=0,high=n-1,mid;
while(low<=high)
{
mid = (low+high)/2;
if(a[mid]==x)
return mid;
else if(x>a[mid])
low = mid+1;
else
high = mid-1;
}
return -1;
}
int main()
{
int a[10]={1,3,5,7,8,10,13,14,17,18};
int x = 9;
int k = bisearch(a,10,x);
if(k>=0)
a[k]+=100;
disp(a,10);
return 0;
}
求两个整数的最大公约数
// 辗转相除法
int gcd(int a, int b)
{
c= a%b;
while(c!=0)
{
a = b;
b = c;
c= a%b;
}
return b;
}
// 根据最大公约数的定义
int gcd1(int a, int b)
{
int k;
for(k=a; ; k--)
{
if(a%k==0&& b%k==0)
break;
}
return k;
}
int lcb(int a, int b)
{
return (a*b)/gcb(a,b);
}
例:通过全局变量调用函数 divmod 获取两个整数的商和余数。
分析:函数只能返回一个值。考虑借助两个全局变量,如同公告的“快递柜“一样,在函数间传递数据
int a,b; // 全局变量
void divmod(int m,int n)
{
a = m/n;
b = m%n;
}
int main()
{
divmod(15,7);
printf("%d %d", a, b);
return 0;
}
int fun(int n)
{
static int t = 1; // 静态变量,生存期是全局的。函数多次调用,也不会重复初始化
t *= n;
return t;
}
int main()
{
int i;
for(i=1;i<=5;i++)
printf("%d\n", fun(i) ); // 输出1! 2! 3! 4! 5!
return 0;
}
7 . 如果程序文件中,调用前未定义函数,则需要声明函数,声明即函数头;
递归函数:调用自身的函数。
理解:虽然是调用同一个函数,但每次调用时,函数的环境是独立的(比如局部变量等)
递归调用的要点:
void printStar(int n)
{
if(n==0)
return; // 出口
putchar('*');
printStar(n-1); // 递归
}
int main()
{
printStar(5);
return 0;
}
例:递归函数,计算 ,n!,汉诺塔问题
//x的n次方
int fun(double x, int n)
{
if(n==0)
return 1 ;
return x*fun(x,n-1);
}
// n的阶乘
int jc(int n)
{
if(n>0)
return n*jc(n-1);
else
return 1;
}
//将n个盘片从from经temp移动到des
//高度为n的塔,上面是高度n-1的塔作为子问题递归处理,下面大盘片直接移动
void hanoi(int n,char from,char temp, char des)
{
if(n>0)
{
hanoi(n-1,from ,des, temp);
printf("%c-->%c\n", from, des);
hanoi(n-1,temp,from, des);
}
}
int main( )
{
hanoi(5,'A','B','C');
}
指针就是“存储对象”的地址 。
int n=10,*p=&n; // 定义整型变量n,指针变量p( 初始化p指向n )
*p = 5; // 等价于 n = 5
强调 :以上两个 * 完全不相同 :
int *
类型,表达式 *p 是int 类型分析指针及 其指向的对象之间关系时,将指针抽象为指向存储对象的 “箭头指针”
例子:分析以下代码
int main()
{
char s1[100];
scanf("%s", s1) ; //s1是char*类型
char s[]={'a','b','c','\0'};//等价于 char s[]= "abc";
s[1] = '\0';
puts(s); // 输出a
char *p = "abc";
puts(p);// 输出abc
p[1] = 'B'; // 错误,p指向的常量区,只读不可写
}
函数不能直接访问 其它函数的局部对象;但可以传递对象的地址,使函数能够间接访问。
void swap(int *p,int *q)
{
int t; // t是int类型哦
t = *p; *p = *q; *q = t; // 交换的是*p和*q哦!
return;
}
// 假交换:虽接收了地址,但并未间访
void swap(int *p ,int *q)
{
int *t;
t = p; p = q; q = t; // 交换的是p和q哦!
return;
}
int main( )
{
int a=1,b=2;
swap(&a,&b);
printf("%d %d", a ,b); // 2 1
}
// 函数通过p和q两个指针变量,将两个运算结果,赋值给main中的变量
void divmod(int m,int n,int *p,int *q)
{
*p = m/n;
*q = m%n;
return;
}
int main( )
{
int a,b; // 主函数定义变量
divmod(10,3,&a,&b); // 传递操作数和a,b的地址,divmod就可能访问a和b
printf("%d %d", a ,b); // 2 1
}
++*p *p++
; ++*p
是p间访的数组元素自增, *p++
是指针自增。void disp(int a[], int n)
{
int *p = a+n;// 尾元素后的元素,作为结束的标识
while(a!=p) // 或while(a<p) 或 for(i=0;i<n;i++)
printf("%d ", *a++); //这里的a是指针变量
}
int main()
{
int a[7] = {10,20,30,40,50,60,70},i,*p=a;
// 依次输出数组元素
for(i=0;i<7;i++)
printf("%d ", *p++ ); //*a++ 错误,因为a是常量
p = a;
while(p < a+7)
*p++ = 0;
disp(a+2,3); // 输出30 40 50
return 0;
}
int a[N][M]
是元素为行(即 int [M]
)的一维数组,因此a是”行指针 ”类型: int (*)[M]
a[i][j]
等价于 *(*(a+i)+j)
,形式变换而已。void disp(int a[][4],int n) //(int (*a)[4],int n)
{
int i,j;
for(i=0;i<n;i++)
for(j=0;j<4;j++)
a[i][j]= i*j; // (*(a+i)+j) = i*j;
}
int main()
{
int a[2][4], (*p)[4] = a;
fun(a,2); // fun(p,2);
return 0;
}
二维数组本质是一维数组,按行存储。因此可以用一维形式进行访问。
int a[2][3]={{1,2,3},{4,5,6}},*p,i;
p = &a[0][0]; // p = a 错误
for(i=0;i<2*3;i++)
{
p[i] = 0; //*p++ = 0;
}
\0
为结束标识的字符数组。char *
int main()
{
char s1[100];
scanf("%s", s1) ; //s1是char*类型
char s[]={'a','b','c','\0'};//等价于 char s[]= "abc";
s[1] = '\0';
puts(s); // 输出a
char *p = "abc";
puts(p);// 输出abc
p[1] = 'B'; // 错误,p指向的常量区,只读不可写
}
例:字符串常见的操作
// 输出字符串
void Puts(char *s)
{
while( *s!='\0' )
putchar(*s++);
}
// 返回字符串长度
int Strlen(char *s)
{
int n=0,i=0;
while(s[i++]!='\0') n++; //while(*s++ !='\0') n++;
return n;
}
// 将字符串s拷贝到字符数组d
void Strcpy(char *d,char *s)
{
while(*s!='\0')
*d++ = *s++;
*d = '\0'; // 别忘了结束标记
}
int main()
{
int n;
char s[10];
n = Strlen("1234");
printf("%d ", n);// 4
Strcpy(s,"abc");
Puts(s);
return 0;
}
头文件 string.h 中的字符串函数
puts gets strlen strcpy strcmp strcat
sizeof("abc") //4 占4个字节
strlen("abc") //3 长度为3
char s[100],*p;
gets(s);
puts(s);
strcpy(s,"hello"); // ok ,注意 s="hello"错误
strcpy(p,"hello"); gets(p) ; //均为错误,因p是野指针
p = s; strcpy(p,"hello"); // p指向数组s ok,拷贝到数组s中
例:输入N个字符串,按照字典顺序递增排序后输出。
分析:二维字符数组,每行存储一个字符串。采用冒泡排序,字符串的比较和复制调用strcmp和strcpy函数
#include <string.h>
#define N 4
int main()
{
char s[N][100]; //二维数组每行存储一个字符串
int i,j;
for(i=0; i<N; i++)
gets(s[i]);
for(i=0; i<N; i++)
for(j=0; j<N-i; j++)
if(strcmp(s[j],s[j+1]))
{
char t[100];
strcmp(t,s[j]);
strcmp(s[j],s[j+1]);
strcmp(s[j+1],t);
}
for(i=0; i<N; i++)
puts(s[i]);
return 0;
}
元素为指针的数组就是指针数组。字符指针数组,用来管理多个字符串。
例:依次输出N个字符串
#include <string.h>
#define N 4
int main()
{
char *ps[N]={"Beijing","Shanghai","Guangzhou","Wuhan"};
int i;
for(i=0;i<N;i++)
puts(ps[i]);
}
指针的指针。
指针数组名,即为二级指针。
结构体是自定义类型,自定义类型 使用如同基本类型。
struct Position
{
double x;
double y;
};
// 传结构体值,输出点
void disp(struct Position d)
{
printf("<%.1f, %.1f>", d.x,d.y);
}
// 点移动后,返回结构体
struct Position move(struct Position d, double deltax, double deltay)
{
d.x += deltax;
d.y += deltay;
return d;
};
// 传结构体地址,进行交换
void swap(struct Position *p, struct Position *q)
{
struct Position t;
t = *p;
*p = *q;
*q = t;
}
int main()
{
int i;
//定义结构体变量,指针,数组
struct Position d1= {1,2},d2,*p = &d1,a[5]= {{1,2},{3,4},{5,6}};
d1.x += 10; // 成员运算符
p->x += 10; // 等价于 (*p).x
disp(d1); // 或disp(*p);
d2 = move(d1,10,20);
swap(&d1, &d2);
// 下面输出 <1.0, 2.0> <3.0, 4.0> <5.0, 6.0> <0.0, 0.0> <0.0, 0.0>
for(i=0; i<5; i++)
disp(a[i]);
return 0;
}
struct Date
{
int year;
int month;
int day;
};
struct Student
{
int id; //学号
char name[10]; //姓名
//struct Date birthday;
struct //匿名类型
{
int year;
int month;
int day;
}birthday;
int scores[5]; // 成绩表
}stu = {100,"Xiaoming",{2000,3,23},{70,80,75,90}};
int main()
{
int i;
struct Student t;
printf("%d",stu.id);
printf("%s", stu.name);
printf("%d-%d-%d", stu.birthday.year, stu.birthday.month, stu.birthday.month);
for(i=0;i<5;i++)
printf("%d ", stu.scores[i] );
t.id = 100;
strcpy(t.name,"Laowang"); //!!!!!!!!!!
t.birthday.year = 1999;
t.birthday.month = 3;
t.birthday.day = 25;
t.scores[0] =80;
}
一个简单的例子
struct
{
int k;
struct
{
int r;
char s[10];
}t;
}obj;
int main()
{
obj.k = 100;
obj.t.r = 99;
strcpy( obj.t.s , "hello");
return 0;
}
文件分为:文本文件(t),二进制文件(b)
FILE *
int main()
{
FILE *fp;
fp = fopen("abc.txt","wt");
if (fp == NULL)
{
printf("error");
exit(-1);
}
fprintf(fp,"hello");
fclose(fp);
return 0;
}
// 统计a.txt中的字符数量
int main()
{
FILE *fp;
int count=0;
fp = fopen("a.txt","rt");
if( fp == NULL )
{
printf("erro.");
exit(-1);
}
while( fgetc(fp) != EOF )
count++;
fclose(fp);
printf("count is %d.\n",count);
return 0;
}
宏替换 是预编译语句,它是简单的字符串替换,不分析语法。
#define F(a,b) a*b
Add(2, 5+3) 被替换为 2 * 5 + 3
#define F(a,b) ((a)* (b)) 方才不会出错,否则很可能预想不到的结果!
将类型取一个类型别名。
typedef
struct
{
int x;
int y;
} Pair;
typedef int INTGER
Pair a;
INTGER b;
枚举类型
enum Week
{
Mon=1,Tue,Wed,THU,FRI,SAT,SUN
};
共用类型
各成员公用存储空间
union T
{
int a;
char t[10];
}d;
sizeof(d) // 10个字节
void *malloc(int size);
void*
类型返回分配的内存区域的基地址。int n, *p,i;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) *n);
for(i=0;i<n;i++)
p[i] = i;
free(p);
建立附带头结点的链表h
#include <stdio.h>
#include <stdlib.h>
typedef int T;
typedef struct Node
{
T data;
struct Node *next;
}Node;
// 链表类型就是Node*
int main()
{
// 建立附带头结点的空链表h
Node * h;
h = (Node *)malloc(sizeof(Node));
h->next = NULL;
return 0;
}
输入n个数据,构造结点,装载数据,依次追加到空链表h
void create(Node * h, int n)
{
int i;
T e;
Node *p,*tail = h; // tail是尾指针,初始头结点即末尾结点
for(i=0; i<n;i++)
{
// 建立新结点
scanf("%d", &e);
p = (Node *)malloc(sizeof(Node));
p->data = e;
p->next = NULL;
// 新结点插到尾部
tail->next = p;
tail = p; // 新结点成为尾结点,为下次循环更新尾指针
}
}
依次输出链表h中的元素值
void dispList(Node *h)
{
Node *p;
for(p=h->next; p!=NULL; p=p->next)
{
printf("%d ", p->data);
}
}
链表h中查找值为x的元素,查找成功返回指针,不成功返回NULL
Node * search(Node *h, T x)
{
Node *p;
for(p=h->next; p!=NULL; p=p->next)
{
if(p->data == x)
break;
}
return p;
}
删除链表h中,值为x的结点。删除成功返回1,否则返回0
思路:删除某元素,应该查找其前驱p,从而转换删除p的后继结点。
int del(Node *h, T x)
{
Node *p,*t;
// 令p指向待删结点的前驱
p=h;
for(p=h; p->next!=NULL; p=p->next)
{
if(p->next->data==x)
break;
}
// 删除p的后继结点
if(p->next)
{
t = p->next; // t是待删结点
p->next = t->next;
free(t); // 回收结点的内存
return 1;
}
return 0;
}
主函数里建立空链表,然后调用链表的各种操作 。
int main()
{
// 建立附带头结点的空链表
Node * h,*p;
h = (Node *)malloc(sizeof(Node));
h->next = NULL;
// 输入5个数据,建立链表
create(h, 5);
dispList(h);
// 查询值为3的元素,如果查找成功,则将其增加100
p = search(h,3);
if(p)
p->data += 100;
// 删除值为3的元素
del(h,3);
dispList(h);
return 0;
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.cnblogs.com/wzgwzg/p/17015871.html
内容来源于网络,如有侵权,请联系作者删除!