C语言笔记(翁恺版本)(五)

简介: C语言笔记(翁恺版本)(五)

C语言的类型


整数


  1. char:一字节(8比特)-128至127
  2. short:2字节   -32768至32767
  3. int:取决于编译器(CPU),通常的意义都是"1个字"
  4. long:取决于编译器(CPU),通常的意义是"1个字"(4或8字节)
  5. long long:8字节


*整数的内部表达


  1. 计算机内部一切都是二进制
  2. 18 -> 00010010
  3. 0 -> 00000000
  4. -18 ->?


*如何表示负数


  1. 十进制用"-"来表示负数,在做计算的时候
  2. 加减是做相反的运算
  3. 乘除时当作正数,计算完毕后对结果的符号取反


*二进制负数


  1. 1个字节可以表达的数:0000 0000 - 1111 1111(0-255)


三种方案:


  1. 仿照十进制,有一个特殊的标志表示负数
  2. 取中间的数为0,如100 0000表示0,比它小的是负数,比它大的是正数
  3. 补码(实际上使用的,方案1和2都有缺陷)


*补码


补码的意义就是拿补码和原码可以加出一个溢出的0


例子:因为0 - 1->-1,所以-1 = (1)0000 0000 - 0000 0001 ->1111 1111


         1111 1111被当作纯二进制看待时,是255,被当作补码看待时是-1


          同理,对于-a,其补码就是0-a,实际是2的n次方-a,n是这种类型的位数


数的范围


  1. 对于一个字节(8位),可以表达的是:0000 0000 ->1111 1111
  2. 其中0000 0000->0   、1111 1111至1000 0000 ->-1至-128   、 0000 0001至0111 1111->1至127


浮点数


float、double、long double


逻辑


bool


指针


自定义类型


类型有何不同


  1. 类型名称:int、long、double
  2. 输入输出时的格式化:%d、%ld、%lf
  3. 所表达的数的范围:char
  4. 内存中所占据的大小:1个字节到16个字节
  5. 内存中的表达形式:二进制数(补码)、编码


sizeof


  1. 是一个运算符,给出某个类型或者变量在内存中所占据的字节数


例如:sizeof(int)、size(i)


  1. 是静态运算符,它的结果在编译时刻就决定了
  2. 不要在sizeof的括号里做运算,这些运算不会进行的


unsigned


  1. 在整形类型前加上unsigned使得它们变成无符号的整数
  2. 内部的二进制表达没变,变的是如何看待它们。如何输出
  3. 1111 1111对于char来说是-1,对于unsigned char来说是255
  4. 如果一个字面量常数想要表达自己是unsigned,可以在后面加上U或者u,255U
  5. 用L或者l表示long( long)
  6. unsigned初衷并非扩展数能表达的范围,而是为了做纯二进制运算,主要是为了移位


整数越界


整数是以纯二进制方式进行计算的,所以:


  1. 1111 1111+1 -> 1 0000 0000 ->0
  2. 0111 1111+1 -> 1 0000 0000 ->-128
  3. 1000 0000-1  -> 0111 1111  ->127


整数的输入输出


只有两种形式:int或者long long


  1. %d:int
  2. %u:unsigned
  3. %ld:long long
  4. %lu:unsigned long long


8进制和16进制


  1. 一个以0开始的数字字面量是8进制
  2. 一个以0x开始的数字字面量是16进制
  3. %o用于8进制,%x用于16进制
  4. 8进制跟16进制只是如何把数字表达为字符串,与内部如何表达数字无关
  5. 16进制很适合表达二进制数据,因为4位二进制正好是一个16进制位
  6. 8进制的一位数字正好表达3位二进制
  7. 因为早期计算机的字长是12的倍数,而非8


选择整数类型


  1. 为什么整数要有那么多种?为了准确表达内存,做底层程序的需要
  2. 没有特殊需要就选择int
  3. 现在CPU的字长普遍是32位或者64位,一次内存读写就是一个int,一次计算也是一个int,选择更短的类型不会更快,甚至可能更慢
  4. 现代的编译器一般会设计内存对齐,所以更短的类型实际在内存中有可能也占据一个int的大小(虽然sizeof告诉你更小)
  5. unsigned与否只是输出的不同,内部计算是一样的


浮点数


浮点类型

类型

字长

范围

有效数字

float

32

正负(1.20×100,正负inf,NaN)

7

double

64

正负(2.20×100,正负inf,NaN)

15


浮点的输入输出

类型

scanf

printf

float

%f

%f,%e

double

%lf

%f,%e


科学计数法


  1. 可选的+或者-符号
  2. 可以用e或者E
  3. 整个词不能有空格
  4. 小数点也是可选的
  5. 符号可以是正负也可以省略(表示+)
  6. 例如:-5.67E+16


输出精度


在%和f之间加上.n可以指定输出小数点后几位,这样的输出是做四舍五入的


  1. printf("%.3f\n",-0.0049);
  2. printf("%.30f\n",-0.0049);
  3. printf("%.3f\n",-0.00 49);


超出范围的浮点数


  1. printf输出inf表示超过范围的浮点数:+无穷
  2. printf输出nan表示不存在的浮点数


浮点运算的精度


  1. 带小数点的字面量是double而非float
  2. float需要用f或者F后缀来表明身份


浮点数的内部表达


  1. 浮点数在计算时是由专用的硬件部件实现的
  2. 计算double和float所用的部件是一样的


选择浮点类型


  1. 如果没有特殊需要,只使用double
  2. 现代CPU能直接对double做硬件运算,性能不会比float差,在64位的机器上,数据存储的速度也不比float慢


字符


字符类型


char是一种整数,也是一种特殊的类型:字符。这是因为:


  1. 用单引号表示的字符字面量:'a','1'
  2. "也是一个字符
  3. printf和scanf里用%c来输入输出字符


字符的输入输出


  1. 如何输入'1'这个字符给char c?


scanf("%c",&c);->1


scanf("%d",&i);c=i ;->49


  1. '1'的ASCII编码是49,所以当c==49时,它代表'1'
  2. 这是一个49的各自表述


混合输入


有何不同


scanf("%d %c",&i,&c);


scanf("%d%c",&i,&c);


字符计算


  1. 一个字符加一个数字得到ASCII码表中那个数之后的字符
  2. 两个字符的减,得到它们在表中的距离


大小写转化


  1. 字母在ASCII表中是顺序排列的
  2. 大写字母和小写字母是分开排列的,并不在一起
  3. ‘a’-‘A’可以得到两段之间的距离,于是a+‘a’-‘A’可以把一个大写字母变成小写字母,反之把这个运算中的‘a’与‘A’互相调换即可把一个小写字母变成大写字母


逃逸字符


用来表达无法印出来的控制字符或特殊字,在那些具有特殊符号的前面加上\即可将其当作普通字符使用

字符

意义

\b

回退一格

\t

到下一个表格位

\n

换行

\r

回车

"

双引号

'

单引号

\

反斜杠本身


制表位


  1. 每行的固定位置
  2. 一个\t使得输出从下一个制表位开始
  3. 用\t能使上下两行对齐


逻辑类型


bool


  1. 在宏定义中加上#include
  2. 之后就可以使用bool和true跟false


bool的运算


  1. bool实际上还是以int的手段实现的,所以可以当作int来计算
  2. 也只能当作int来输入输出


类型转换


自动类型转换


  1. 当运算符的两边出现不一致的类型时,会自动转换成较大的类型
  2. 大的意思是能表达的数的范围更大
  3. char->short->int->long->long long
  4. int->float->double
  5. 对于printf,任何小于int的类型会被转化成int;float会被转换成double
  6. 但是scanf不会,要输入short,需要%hd


强制类型转换


  1. 要把一个量强制转换成另一个类型(通常是较小的类型),需要:(类型)值
  2. 比如(int)10.2   (short)32
  3. 需要注意这个时候的安全性,小的变量不总能表达大的量
  4. 这个只是从那个变量计算出了一个新的类型的值,它并不改变那个变量,无论值还是类型都不改变
  5. 强制类型转换的优先级高于四则运算


有些运算


逻辑运算


  1. 逻辑运算是对逻辑量进行的运算,结果只有0或者1
  2. 逻辑量是关系运算或逻辑运算的结果

运算符

描述

示例

结果

逻辑非

!a

如果a是true结果就是false如果a是fasle结果就是true

&&

逻辑与

a&&b

如果a和b都是是true结果就是true否则就是false

||

逻辑或

a||b

如果a和b有一个是true结果就是true两个都是false,结果才是false


try(尝试)


如何判断一个字符c是否是大写字母?


c >= ‘A’&&c<=‘Z’


逻辑优先级


!>&&>||


总体优先级排名

优先级

运算符

结合性

1

()

从左到右

2

!+- ++ --

从右到左(单目的+和-)

3

/ %

从左到右

4

+   -

从左到右

5

< <= > >=

从左到右

6

== !=

从左到右

7

&&

从左到右

8

||

从左到右

9

= += -= *= /= %=

从右到左


短路


  1. 逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算了
  2. 对于&&,左边是false时就不会做右边的运算了
  3. 对于||,左边是true时就不会做右边的运算了
  4. 不要把赋值,包括复合赋值组合进表达式


条件运算符


  1. count = (count>20)?count-10:count+10;
  2. 当count>20是真的时候执行前者,是假执行后者
  3. 优先级:条件运算符的优先级高于赋值运算符,但是低于其他运算符
  4. 不要嵌套条件表达式(很容易出问题)


逗号运算符


  1. 逗号用来连接两个表达式,并以其右边的表达式的值作为它的结果。逗号的优先级是所有运算符中最低的,所以它两边的表达式会先计算;逗号的组合关系是自左像右,所以左边的表达式会先计算,而右边的表达式的值就留下来作为逗号运算的结果
  2. 比如在for中使用
  3. for(i = 1,j=8;i


第七周:函数


7.1函数的定义与使用

#include<stdio.h>
int main() { 
  int m,n;
  int sum = 0;
  int cnt = 0;
  int i;
  scanf("%d %d",&m, &n);
  // m=10,n=31;
  if(m == 1) m=2;
  for(i=m; i<=n; i++){
    int isPrime = 1;
    int k;
    for(k=2; k<i-1; k++){
      if(i%k == 0){
        isPrime = 0;
        break;
      }
    }
    if(isPrime){
      sum += i;
      cnt++;
    }
  }
  printf("%d %d\n", cnt, sum); 
  return 0;
} 
// 把这段代码取出来
int isPrime = 1;
    int k;
    for(k=2; k<i-1; k++){
      if(i%k == 0){
        isPrime = 0;
        break;
      }
    }
#include<stdio.h>
//求1到10、20到30和35到45的三个和
void sum(int begin,int end)
{
    int i;
    int sum = 0;
    for( i = begin;i <= end; i++){
        sum += i;
    }
    printf("%d到%d的和是%d\n",begin,end,sum);
}
int main()
{
    sum(1,10);
    sum(20,30);
    sum(35,40);
    return 0;
}


什么是函数?


  1. 函数是一块代码,接收零个或者多个参数,做一件事情,并返回零个或一个值
  2. 可以先想像成数学中的函数:y=f(x)


函数定义



调用函数


  1. 函数名(参数值);
  2. ()起到了表示函数调用的重要性,即使没有参数也需要()
  3. 如果有参数,则需要给出正确的数量和顺序
  4. 这些值会被按顺序依次用来初始化函数中的参数


函数返回


函数知道每一次是在哪里调用它,会返回到正确的地方


从函数中返回值


  1. return停止函数的执行,并送回一个值
  2. return;
  3. return 表达式;
  4. 一个函数里可以出现多个return语句
  5. 例如:int c, c = max(10,12);=>可以赋值给变量或者再传递给函数甚至可以丢弃,有的时候要的是副作用


没有返回值的函数


  1. void 函数名(参数表)
  2. 不能使用带值的return
  3. 可以没有return
  4. 调用的时候不能做返回值的赋值
  5. 如果函数有返回值则必须使用带值的return


7.2函数的参数和变量


函数原型


函数的先后关系

#include<stdio.h>
void sum(int begin,int end)
{
    int i;
    int sum = 0;
    for( i = begin; i <= end; i++){
        sum += i;
    }
    printf("%d到%d的和是%d\n",begin,end)
}
int main()
{
    sum(1,10);
    sum(20,30);
    sum(35,45);
    return 0;
}


  1. 像这样把sum()写在上面,是因为:c的编译器自上而下顺序分析你的代码
  2. 在看到sum(1,10)的时候,它需要知道sum()的样子
  3. 也就是sum()要几个参数,每个参数的类型如何,返回什么类型。这样它才能检查你对sum()的调用是否正确


此函数原型

#include<stdio.h>
double max(double a,double b)   =>  这是函数原型
    int main()
{
int a,b,c;
    a = 5;
    b = 6;
    c = max(10,12);       =>这中间一段是根据原型判断
    printf("%d\n",c);
    max(12,13);
    return 0;
}
double max(double a, double b)  =>这是实际的函数头


  1. 函数头:以分号“;”结尾,就构成了函数的原型
  2. 函数原型的目的是告诉编译器这个函数长什么样子:名称、参数(数量及类型)、返回类型
  3. 旧标准习惯把函数原型写在调用它的函数里面
  4. 现在一般写在调用它的函数前面
  5. 原型里可以不写参数的名字,但是一般仍然写上


参数传递


调用函数


  1. 如果函数有参数,调用函数的时候必须传递给它数量、类型正确的值
  2. 可以传递给函数的值是表达式的结果,这包括:字面量、变量、函数的返回值、计算的结果
int a,b,c;
a = 5;
b = 6;
c = max(10,12);
c = max(a,b);
c = max(c,23);
c = max(max(23,45),a);
c = max(23+45,b);

类型不匹配


  1. 调用函数时给的值与参数的类型不匹配是C语言传统上最大的漏洞
  2. 编译器总是悄悄替你把类型转换好,但是这很可能不是你所期望的
  3. 后续的语言,C++/java在这方面很严格


C语言在调用函数时,永远只能传值给函数

#include<stdio.h>
void swap(int a,int b);  //这是参数
int main()
{
    int a = 5;
    int b = 6;
    swap(a,b);   //这是值
    printf("a = %d b = %d\n",a,b);
    return 0;
}
void swap(int a,int b)   //参数
{
    int t = a;
    a = b;
    b = t;
}


传值


  1. 每个函数有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系
  2. 过去,对于函数参数表中的参数,叫做"形式参数",调用函数时给的值叫做"实际参数"(实参与形参)
  3. 由于容易让初学者误会实际参数就是实际在函数中进行计算的参数,误会调用函数的时候把变量而不是值传进去,所以我们不建议继续使用这种古老的方式来称呼它
  4. 我们认为,它们是参数和值的关系


本地变量


  1. 函数的每次运行,就会产生一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量
  2. 定义在函数内部的变量就是本地变量
  3. 参数也是本地变量


变量的生存期和作用域


  1. 生存期:什么时候这个变量开始出现了,到什么时候它消亡了
  2. 作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
  3. 对于本地变量,这两个问题的答案是统一的:大括号内——块


本地变量的规则


1.本地变量是定义在块内的


  1. 它可以是定义在函数的块内
  2. 也可以定义在语句的块内
  3. 甚至可以随便拉一对大括号来定义变量


2.程序运行进入这个块之前,其中的变量不存在,离开这个块,其中的变量就消失了


3.块外面定义的变量在里面仍然有效


4.块里面定义了和外面同名的变量则覆盖了外面的变量(块内的变量优先度更高)


5.不能在一个块内定义同名的变量


6.本地变量不会被默认初始化


7.参数在进入函数的时候被初始化了


其他细节


没有参数时


  1. void f(void)还是void f();
  2. 在传统C中,它表示f函数的参数表示未知,并不表示没有参数


逗号运算符?


  1. 调用函数时的逗号跟逗号运算符字母区分?
  2. 如下 =>  调用函数时的圆括号内的逗号是标点符号,不是运算符
  3. 逗号:f(a,b)  逗号运算符:f((a,b))


函数里的函数?


C语言不允许函数嵌套定义


这是什么?


  1. int i,j,sum(int a,int b);  //可以这样写但是不建议
  2. return(i);   //加不加括号其实都无所谓,值都一样的,但加上括号会让人误以为这是一个函数,不要这样写没有好处


关于main


  1. int main()也是一个函数
  2. 要不要写成int main(void)?  //void加不加都一样,但如果上面参数都不打算加,不妨把void写下去
  3. return的0有人看吗?  //是可以看的起作用的,返回0表示正常的运行结束了,返回任何非0的值都是错误的


windows:if error level 1...


Unix Bash:echo $?


Csh:echo $status


第八周:数组


8.1-1初试数组


如何写入一个程序计算用户输入的数字的平均数?(不需要记录输入的每一个数)

#include<stdio.h>
int main(){
    int x;
  double sum = 0;
  int cnt = 0;
  scanf("%d",&x);
  while( x != -1){
      sum += x;
      cnt++;
      scanf("%d",&x);
  }
  if( cnt > 0 ){
      printf("%f\n",sum/cnt);
  }
    return 0;
}
  1. 如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数?
  2. 思路:必须先记录每一个输入的数字,计算平均数之后,再检查记录下来的每一个数字,与平均数比较,决定是否输出
  3. 该使用数组啦
#include<stdio.h>
int main(){
    int x;
  double sum = 0;
  int cnt = 0;
  int number[100];  //=>这里定义了数组,但具有隐患,当存放的内容超过了100个的时候就会出错,所有后续可以进行动态调整
  scanf("%d",&x);
  while( x != -1){
      number[cnt] = x;  //对数组中的元素赋值
      sum += x;
      cnt++;
      scanf("%d",&x);
  }
  if( cnt > 0){
      int i;
      double average = sum/cnt;
      for( i = 0;i < cnt; i++){
          if( number[i] > average){
              printf("%d",number[i]);  //遍历数组跟使用数组里面的元素
          }
      }
  }
    return 0;
}

8.1-2数组的定义和使用


定义数组


1.<类型>变量名称[元素数量];


  1. int grades[100];
  2. double weight[20];


2.元素数量必须是整数


3.C99之前:元素数量必须是编译时刻确定的字面量(了解下就行)


数组


是一种容器(放东西的东西),特点是:


  1. 其中所有的元素具有相同的数据类型;
  2. 一旦创建,不能改变大小
  3. *(数组中的元素在内存中是连续依次排列的)


int a[10];


  1. 一个int的数组
  2. 10个单元:a[0],a[1],a[2]....a[9];
  3. 每一个单元就是一个int类型的变量
  4. 可以出现在赋值的左边或右边:a[2] = a[1]+6;
  5. *在赋值左边的值叫左值(右边就叫右值咯)


数组的单元


  1. 数组的每个单元就是数组类型的一个变量
  2. 使用数组时放在[]中的数字叫做下标或索引,下标从0开始计数:例如1.grades[0]2.grades[99],average[5]


有效的下标范围


  1. 编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
  2. 一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃segmentation fault
  3. 也可能运气好,没造成严重的后果
  4. 所有这是程序员的责任来保证程序只使用有效的下标值:[0,数组的大小-1]


计算平均数

int x;
double sum = 0;
int cnt;
printf("请输入数字的数量:");
scanf("%d",&cnt);
if( cnt > 0){
    int number[cnt];
    scanf("%d",&x);
    while( x != -1){
        number[cnt] = x;
        sum += x;
        cnt++;
        scanf("%d",&x);
    }
}


长度为0的数组?


int a[0];可以存在,但是没有卵用


8.1-3数组的例子:投票统计


写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束


#include<stdio.h>
int main(){
    const int number = 10;  //数组的大小
    int x;
    int count[number];  //定义数组
    int i;
    for( i=0; i<number; i++){    //初始化数组
        count[i] = 0;
    }
    scanf("%d",&x);
    while( x != -1){
        if( x >= 0 && x <= 9){
            count[x]++;  //数组参与运算
        }
        scanf("%d",&x);
    }
    for( i = 0;i < number; i++){
        printf("%d:%d\n",i,count[i]);   //遍历数组输出
    }
    return 0;
}


8.2-1数组的运算


在一组给定的数据中,如何找出某个数据是否存在?

#include<stdio.h>
/**
找出key在数组a中的位置
@param key 要寻找的数字
@param a 要寻找的数组
@param length 数组a的长度
@return 如果找到,返回其在a中的位置;如果找不到则返回-1
*/ 
int search(int key,int a[], int length);
int main(void)
{
    int a[] = {2,4,6,7,1,3,5,9,11,13,23,14,32};
    int x;
    int loc;
    printf("请输入一个数字:");
    scanf("%d",&x);
    loc = search(x,a,sizeof(a)/sizeof(a[0]));
        if( loc != -1){
            printf("%d在第%d个位置上\n",x,loc);
        }else{
            printf("%d不存在\n",x);
        }
    return 0;
}
int search(int key, int a[], int length)
{
    int ret = -1;
    int i;
    for( i = 0; i <= length; i++){
        if( a[i] == key){
            ret = i;
            break;
        }
    }
    return ret;
}


数组的集成初始化


int a[] = {2,4,6,7,1,3,5,9,11,13,23,14,32};


  1. 直接用大括号给出数组的所有元素的初始值
  2. 不需要给出数组的大小,让编译器替你数


int b[20] = {2};


  1. 如果给出了数组的大小,但是后面的初始值数量不足,则其后的元素被初始化为0


集成初始化时的定位

int a[10] = {
    [0] = 2,[2] = 3,6,
};
  1. 用[n]在初始化数据中给出定位
  2. 没有定位的数据接在前面的位置后面
  3. 其他位置的值补零
  4. 也可以不给出数组大小,让编译器算
  5. 特别适合初始数据稀疏的数组


数组的大小


  1. sizeof给出整个数组所占据的内容的大小,单位时字节


得到数组的个数: sizeof(a)/sizeof(a[0])


  1. sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
  2. 这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码


数组的赋值


  1. 数组变量本身不能被赋值
  2. 要把一个数组的所有元素交给另一个数组,必须采用遍历
for( i = 0; i<length;i++){
    b[i] = a[i];
}

遍历数组


  1. 通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的i正好是数组最大的有效下标
  2. 常见错误:循环结束条件是 <= 数组长度,或;     离开循环后,继续使用i的值来做数组元素的下标(错误的)


数组作为函数参数时:


  1. 不能在[]中给出数组的大小
  2. 不能再利用sizeof来计算数组的元素个数
  3. 往往必须再用另一个参数来传入数组的大小


8.2-2数组的例子:素数


判断素数

int is Prime(int x);
int main(void)
{
    int x;
    scanf("%d",&x);
    if( isPrime(x)){
        printf("%d是素数\n",x);
    }else{
        printf("%d不是素数\n",x);
    }
    return 0;
}

从2到x-1测试是否可以整除

int isPrime(int x)
{
    int ret = 1;
    int i;
    if( x == 1) ret = 0;
    for( i = 2;i < x; i++){
        if( x % i == 0 ){
            ret = 0;
            break;
        }
    }
    return ret;
}
//对于n要循环n-1遍,当n很大的时候就是n遍
int isPrime(int x)
{
    int ret = 1;
    int i;
    if( x == 1 || (x%2 == 0 && x != 2)) ret = 0;
    for( i = 3;i < x; i+=2){
        if( x % i == 0 ){
            ret = 0;
            break;
        }
    }
    return ret;
}
//如果x是偶数,立刻结束。否则要循环(n-3)/2+1遍。当n很大的时候就是n/2遍
int isPrime(int x)
{
    int ret = 1;
    int i;
    if( x == 1 || (x%2 == 0 && x != 2)) ret = 0;
    for( i = 3;i < sqrt(x); i+=2){
        if( x % i == 0 ){
            ret = 0;
            break;
        }
    }
    return ret;
}
//只需要循环sqrt(x)遍,sqrt(x)是x的平方根的意思


判断是否可以被已知的且


#include<stdio.h>
#include<math.h>
int isPrime(int x, int knownPrimes[],int numberOfKnownPrimes);
int main(void){
    const int number = 10;
    int prime[number] = {2};
    int count = 1;
    int i = 3;
    // 为了输出好看,添加测试语句
  {
    int i;
    printf("\t\t");
    for(i=0; i<10; i++){
      printf("%d\t", i);
    }
    printf("\n");
  } 
    while(count < number){
        if(isPrime(i,prime,count)){
            prime[count++] = i;
        }
        // 加入调试输出的语句
    {
      printf("i=%d \tcnt=%d\t", i, count);
      int i;
      for(i=0; i<number; i++){
        printf("%d\t", prime[i]);
      }
      printf("\n");
    } 
        i++;
    }
    for(i=0; i<number; i++){
        printf("%d",prime[i]);
        if((i+1)%5) printf("\t");
        else printf("\n");
    }
    return 0;
}
int isPrime(int x,int knownPrimes[],int numberOfKnownPrimes){
    int ret = 1;
    int i;
    for( i=0;i<numberOfKnownPrimes; i++){
        if(x%knownPrimes[i] == 0){
            ret = 0;
            break;
        }
    }
    return ret;
}


构造素数表


欲构造n以内的素数表


  1. 令x为2
  2. 将2x,3x,4x直至ax
  3. 令x为下一个没有被标记为非素数的数,重复2;直到所有的数都已经尝试完毕


欲构造n以内(不含)的素数表


  1. 开辟prime[n],初始化其所有元素为1,prime[x]为1表示x是素数
  2. 令x = 2;
  3. 如果x是素数,则对于(i = 2;xi < n; i++)令prime[ix]=0
  4. 令x++,如果x
#include<stdio.h>
int main(void){
const int maxNumber = 25;
int isPrime[maxNumber];
int i;
int x;
for( i = 0;i < maxNumber;i++){
    isPrime[i] = 1;
}
for( x = 2;x < maxNumber;x++){
    if( isPrime[x] ){
        for(i = 2; i*x<maxNumber; i++){
            isPrime[i*x] = 0;
        }
    }
}
for( i = 2;i < maxNumber; i++){
    if( isPrime[i]){
        printf("%d\t",i);
    }
}
printf("\n");
    return 0;
}


8.2-3二维数组


二维数组


  1. int a[3][5];
  2. 通常理解为a是一个3行5列的矩阵

a[0][0]

a[0][1]

a[0][2]

a[0][3]

a[0][4]

a[1][0]

a[1][1]

a[1][2]

a[1][3]

a[1][4]

a[2][0]

a[2][1]

a[2][2]

a[2][3]

a[2][4]


二维数组的遍历

for( i = 0;i < 3;i++){
    for( j = 0;j < 5; i++){
        a[i][j] = i*j;
    }
}
  1. a[i][j]是一个int
  2. 表示第i行第j列上的单元
  3. a[i,j]是什么?     //是一个表达式,不是正确的表达二维数组的方式


二维数组的初始化

int a[][5] = {
    {0,1,2,3,4},
    {2,3,4,5,6},
};
  1. 列数是必须给出的,行数可以由编译器来数
  2. 每隔一个{},逗号分隔
  3. 最后一组逗号可以存在,以前的传统
  4. 如果数组有部分省略没写,编译器会自动补零(表示补零)
  5. 也可以使用定位


tic-tac-toe游戏


  1. 读入一个3×3的矩阵,矩阵中的数字为1表示该位置有一个X,为0表示为O
  2. 程序判断这个矩阵中是否有获胜的一方,输出表示获胜一方的字符X或者O,或输出无人获胜


读入矩阵

const int size = 3;
int board[size][size];
int i,j;
int numOfX;
int numOfO;
int result = -1;   //-1:没人赢,1:X赢了,0:O赢了
//读入矩阵
for( i = 0;i < size;i++){
    for( j = 0;j < size; j++){
        scanf("%d",&board[i][j]);
    }
}

检查行

//检查行
for( i = 0; i < size && result == -1; i++){
    numOfO = numOfX = 0;
    for( j = 0;j < size; j++){
        if( board[i][j] == 1 ){
            numOfX ++;
        }else{
            numOfO ++;
        }
    }
    if( numOfO == size){
        result = 0;
    }else if(numOfX == size){
        result = 1;
    }
}


检查列

if( result == -1){
    for( j = 0; j < size && result == -1; j++){
        numOfO = numOfX = 0;
        for( i = 0;i < size;i++){
            if( board[i][j] == 1){
                numOfX ++;
            }else{
                numOfO ++;
            }
        }
        if( numOfO == size){
            result == 0;
        }else if( numOfX == size){
            reshult == 1;
        }
    }
}

检查对角线

numOfO = numOfX = 0;
for( i = 0; i < size; i++){
    if( borad[i][size-i-1] == 1){
        numOfX ++;
    }else{
        numOfO ++;
    }
}
if( numOfO == size){
    result = 0;
}else if(numOfX == size){
    result == 1;
}
目录
相关文章
|
8月前
|
编译器 C语言
C语言习题----不同版本的差别
C语言习题----不同版本的差别
45 0
|
8月前
|
存储 人工智能 算法
【C语言】自学终极笔记
【C语言】自学终极笔记
114 0
|
5月前
|
测试技术 C语言 C++
【C语言刷题训练——6】鹏哥C语言刷题训练营笔记,含代码讲解改进
【C语言刷题训练——6】鹏哥C语言刷题训练营笔记,含代码讲解改进
|
5月前
|
存储 C语言
【C语言】鹏哥C语言刷题训练营——第5节内容笔记(含代码全面分析和改进,讲解)
【C语言】鹏哥C语言刷题训练营——第5节内容笔记(含代码全面分析和改进,讲解)
|
6月前
|
前端开发 C语言
C语言04---第一个HelloWorld(vc版本)
C语言04---第一个HelloWorld(vc版本)
|
7月前
|
C语言
|
7月前
|
C语言
|
8月前
|
搜索推荐 C语言 C++
【排序算法】C语言实现归并排序,包括递归和迭代两个版本
【排序算法】C语言实现归并排序,包括递归和迭代两个版本
109 1
|
8月前
|
C语言
C语言(指针详解)重点笔记:指针易错点,都是精华
C语言(指针详解)重点笔记:指针易错点,都是精华
94 0
|
8月前
|
C语言
【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)
【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)
57 0