一、整数的概念

整数是我们生活中常用的数据类型,也是编程中常用的一种数据,C语言用int关键字来定义整数变量(int 是 integer 的简写)。

在定义整型变量的时候,可以在int关键字之前加signed、unsigned、short和long四种修饰符。

signed有符号的,可以表示正数和负数。

unsigned无符号的,只能表示正数,例如数组的下标、人的身高等。

short短的,现在主流的64位操作系统下,整数占用内存4个字节,使用 4 个字节保存较小的整数绰绰有余,会空闲出两个字节来,这些字节就白白浪费掉了。在C语言被发明的早期,或者在单片机和嵌入式系统中,内存都是非常稀缺的资源,所有的程序都在尽可能节省内存。

long长的,更长的整数。

二、整数的取值范围

整数的取值范围与计算机操作系统和C语言编译器有关,没有一个固定的数值,我们可以根据它占用的内存大小来推断它的取值范围。

一个字节有8个位,表示的数据的取值范围是28-1,即255。

如果占用的内存是两个字节,无符号型取值范围是28ⅹ28-1。

如果占用的内存是四个字节,无符号型取值范围是28ⅹ28ⅹ28ⅹ28-1。

如果占用的内存是八个字节,无符号型取值范围是28ⅹ28ⅹ28ⅹ28ⅹ28ⅹ28ⅹ28ⅹ28-1。

如果是有符号,取值范围减半,因为符号占一个位。

下面用一个示例代码来测试各种整数占用内存的大小。

示例(book60.c)

/*
 * 程序名:book60.c,此程序演示整数占用内存的大小和取值范围。
 * 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
 
int main()
{
  short si;   // 短整数
  int   ii;   // 整数
  long  li;   // 长整数
 
  printf("sizeof short is %d\n",sizeof(short));
  printf("sizeof int is %d\n",sizeof(int));
  printf("sizeof long is %d\n",sizeof(long));
}

运行效果

image.png

sizeof是C语言中保留关键字,是一种运算符,不是函数,sizeof实际上是获取了数据在内存中所占用的存储空间,以字节为单位。

int ii;       // sizeof(int)和sizeof(ii)都可以。

根据book60.c的测试结果,我们可以得到各种整数的取值范围。

类型简写

类型全称

长度

取值范围

short

[signed] short [int]

2字节

-32768~32767

unsigned short

unsigned short [int]

2字节

0~65535

int

[signed] int

4字节

-2147483648~2147483647

unsigned int

unsigned [int]

4字节

0~4294967295

long

[signed] long [int]

8字节

-9223372036854775808~

9223372036854775807

unsigned long

unsigned long [int]

8字节

0~18446744073709551615

注意:

1)计算机用最高位1位来表达符号(0-正数,1-负数),unsigned修饰过的正整数不需要符号位,在表达正整数的时候比signed修饰的正整数取值大一倍。

2)在写程序的时候,上表中括号[]的单词可以省略不书写。

3)在写程序的时候,给整数变量赋值不能超出变量的取值范围,编译的时候会出现类似以下的错误,程序运行也可能产生不可预后的后果。

image.png

4)现在计算机的内存不值钱,建议程序员少用short,慎用int,多用long,内存不是问题,程序的稳定高于一切。

三、二进制数、八进制数和十六进制数的书写

一个数字默认就是十进制的,表示一个十进制数字不需要任何特殊的格式。但是,表示一个二进制、八进制或者十六进制数字就不一样了,为了和十进制数字区分开来,必须采用某种特殊的写法,具体来说,就是在数字前面加上特定的字符,也就是加前缀。

1、二进制

二进制由 0 和 1 两个数字组成,书写时必须以0b或0B(不区分大小写)开头,例如:

  // 以下是合法的二进制
  int a = 0b101;      // 换算成十进制为 5
  int b = -0b110010;  // 换算成十进制为 -50
  int c = 0B100001;   // 换算成十进制为 33

// 以下是非法的二进制

  int m = 101010;  // 无前缀 0B,相当于十进制
  int n = 0B410;    // 4不是有效的二进制数字

请注意,标准的C语言并不支持上面的二进制写法,只是有些编译器自己进行了扩展,才支持二进制数字。换句话说,并不是所有的编译器都支持二进制数字,只有一部分编译器支持,并且跟编译器的版本有关系。

2、八进制

八进制由 0~7 八个数字组成,书写时必须以0开头(注意是数字 0,不是字母 o),例如:

  // 以下是合法的八进制数
  int a = 015;      // 换算成十进制为 13
  int b = -0101;    // 换算成十进制为 -65
  int c = 0177777;  // 换算成十进制为 65535
 
  // 以下是非法的八进制
  int m = 256;  // 无前缀 0,相当于十进制
  int n = 03A2;  // A不是有效的八进制数字

3、十六进制

十六进制由数字 0~9、字母 A~F 或 a~f(不区分大小写)组成,书写时必须以0x或0X(不区分大小写)开头,例如:

  // 以下是合法的十六进制
  int a = 0X2A;   // 换算成十进制为 42
  int b = -0XA0;  // 换算成十进制为 -160
  int c = 0xffff;   // 换算成十进制为 65535
 
  // 以下是非法的十六进制
  int m = 5A;    // 没有前缀 0X,是一个无效数字
  int n = 0X3H;  // H不是有效的十六进制数字

4、需要注意的坑

在现实生活和工作中,我们在写十进制数的时候,为了对齐或其它原因,在数值前面加0是无关紧要的,但是,在C语言中,不要在十进制数前加0,会被计算机误认为是八进制数。

四、整数的输出

以下表格中,重点记住第一、二行十进制的输出格式,二十年来,八进制数我从来没有用过,十六进制数只在显示内存的地址时见过,所以大家不必关心八进制和十六进制的相关知识,了解即可。

%hd、%d、%ld

以十进制、有符号的形式输出short、int、long 类型的整数。

%hu、%u、%lu

以十进制、无符号的形式输出short、int、long 类型的整数。

%ho、%o、%lo

以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数

%#ho、%#o、%#lo

以八进制、带前缀、无符号的形式输出   short、int、long 类型的整数

%hx、%x、%lx
  %hX、%X、%lX

以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。

%#hx、%#x、%#lx
  %#hX、%#X、%#lX

以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。

注意一个坑:输出格式控制符的类型最好与变量的类型一一对应,否则会出现意外的后果,示例:

  int i=32767;
  printf("i %hd,%d\n",i,i);
  int j=32768;
  printf("j %hd,%d\n",j,j);

输出结果:

i 32767,32767
j -32768,32768     // 得到了意外的输出结果,32768超出了short的取值范围。

%hd用于输出短整数,最大值是32767,可以输出32767,但不能正常的输出32768。

五、常用的库函数

C语言提供了几个常用的库函数,声明如下:

int  atoi(const char *nptr);   // 把字符串nptr转换为int整数
long atol(const char *nptr);     // 把字符串nptr转换为long整数
int  abs(const int j);            // 求int整数的绝对值
long labs(const long int j);     // 求long整数的绝对值

示例(book61.c)

/*
 * 程序名:book61.c,此程序演示整数的atoi atol abs labs函数的使用
 * 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <stdlib.h>   // 如果不包含这个头文件,会出现意外的结果。
 
int main()
{
  int  ii;
  long ll;
 
  ii=atoi("-2147483647");
  ll=atol("-9223372036854775807");
  printf("ii=%d\n",ii);
  printf("ll=%ld\n",ll);
 
  ii=abs(ii);
  ll=labs(ll);
  printf("ii=%d\n",ii);
  printf("ll=%ld\n",ll);
}

运行效果

image.png

六、数据类型的别名

C语言许程序员使用 typedef 关键字来给数据类型定义一个别名,别名一般有两个特点:1)名称更短;2)更符合程序员的习惯。

例如unsigned int起个size_t的别名。

typedef unsigned int size_t;
size_t ii; 等同于 unsigned int ii;

我们来看看strlen函数的帮助,strlen的返回值就是size_t类型。

image.png

七、随机数

在实际开发中,会用到随机数这个功能,例如编写游戏类的程序时就需要用到随机数。

1、生成随机数

在C语言中,我们使用 <stdlib.h> 头文件中的 srand和rand 函数来生成随机数。

void srand(unsigned int seed);   // 随机数生成器的初始化函数
int  rand();                        // 获一个取随机数

srand函数初始化随机数发生器(俗称种子),在实际开发中,我们可以用时间作为参数,只要每次播种的时间不同,那么生成的种子就不同,最终的随机数也就不同,通常我们采用 <time.h> 头文件中的 time 函数即可得到一个精确到秒的时间作为种子。

示例(book63.c)

/*
 * 程序名:book63.c,此程序用于演示随机数
 * 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
  int ii;
 
  srand(time(0));  // 播下随机种子
 
  for (ii=0;ii<5;ii++)  // 生成5个随机数
  {
    printf("%d ", rand());  // 获取随机数
  }
 
  printf("\n");
}

运行效果

image.png

2、生成一定范围随机数

在实际开发中,需求往往是一定范围内的随机数,对于产生一定范围的随机数,就需要使用一定的技巧,常用的方法是取模运算(取余数),再加上一个加法运算:

  int a = rand() % 50;   // 产生0~49的随机数

如果要规定上下限:

  int a = rand() % 51 + 100;   // 产生100~150的随机数

取模即取余数,rand()%51+100,rand()%51是产生 0~50 的随机数,后面+100保证 a 最小只能是 100,最大就是 50+100=150。

八、课后作业

1)编写示例程序,判断short、unsigned short、int、unsigned int、long、unsigned long占用内存的字节数。

2)选择题:请问int的取值范围是多少?

(A)二十多亿   (B) -2147483648~2147483647  (C) 0~4294967295

3)选择题:请问long的取值范围是多少?

(A)很多个亿   (B) 足够大  (C) -9223372036854775808~9223372036854775807

4)编写示例程序,从界面上输入数字的字符串,存放在字符串变量中,然后用atoi函数转换为整数,加上100后再输出到屏幕。

5)在C语言中,还有一种long long int的整数,各位写一个程序,测试它占用内存的字节数和取值范围,并思考long long int类型是否具备实用价值。

6)编写示例程序,测试short、unsigned short、int、unsigned、long、unsigned long赋值超出了取值范围的后果。

7)重写整数的abs和labs库函数,实现其功能,函数的声明如下:

int   ABS(const int j);                 // 求int整数的绝对值
long LABS(const long int j);           // 求long整数的绝对值

8)利用已经学习的知识,自定义一个函数,函数名是ctoi,把字符的'0'、'1'、'2'、'3'、'4'、'5'、'6'、'7'、'8'、'9'转换为整数的0、1、2、3、4、5、6、7、8、9。函数的声明如下:

// 用于把数字的字符转换为整数,chr为用字符方式表示的数字,函数的返回值为数字的整数。
// 如果chr不是数字的字符,函数返回-1。
int ctoi(const char chr);

提示:采用if或switch语句,判断chr的值,直接返回结果。

调用示例:

  printf("'0' is %d\n",ctoi('0'));    // 输出结果是'0' is 0
  printf("'9' is %d\n",ctoi('9'));    // 输出结果是'9' is 9

以下作业题难度较大,如果无法完成,不要过于纠结,以后功力提升了再做。

9)自定义一个函数,函数名是POW,利用已经学习的知识,求一个数的n次幂,函数的声明如下:

// 求x的y次幂,函数返回值为x的y次幂。
unsigned long  POW(const unsigned int x,unsigned const int y);

调用示例:

  printf("POW(2,3) is %lu\n",POW(2,3));      // 输出结果是8
  printf("POW(10,3) is %lu\n",POW(10,5));    // 输出结果是100000

10)编写示例程序,把字符串里的数字全部加起来,例如字符串是"90576483975423",全部加起来结果是72。

11)重写整数的atoi和atol库函数,实现其功能,函数的声明如下:

int   ATOI(const char *nptr);     // 把字符串nptr转换为int整数
long ATOL(const char *nptr);     // 把字符串nptr转换为long整数

提示:例如字符串的"12305",转为整数12305,拆开就是10000+2000+300+0+5,即1*104+2*103+3*102+0*101+5*100

12)生成五十二个随机数,存放在数组中,范围是1-52,不允许重复,最后在屏幕上显示出来。

源代码一:

/*
 * 程序名:task.c,此程序演示生成1-52之间不重复的随机数。
 * 作者:C语言技术网(www.freecplus.net) 日期:20200510
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
 
int main()
{
  srand(time(0));  // 播下随机种子
 
  int result[52];  // 用于存放结果的数组。
  memset(result,0,sizeof(result));
 
  int ii=0;
  int pos=0; // 已经生成的有效的随机数的个数。
 
  for (ii=0;pos<52;ii++)
  {
    int irand=rand()%52+1;  // 生成1-52之间的随机数。
 
    // 判断irand在result数组中是否已经存在,如果存在,循环结束时jj必定小于52。
    int jj=0;
    for (jj=0;jj<52;jj++)
    {
      if (irand==result[jj]) break;
    }
 
    if (jj<52) continue;
 
    // 如果jj的值是52,表示本次irand的值在数组中不存在。
    result[pos]=irand;
    pos++;
    printf("%d ",irand);
  }
}

源代码二:

/*
 * 程序名:task.c,此程序演示生成1-52之间不重复的随机数。
 * 作者:C语言技术网(www.freecplus.net) 日期:20200510
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
 
int main()
{
  srand(time(0));  // 播下随机种子。

  int result[52];  // 存放已生成的不重复的随机数。
  memset(result,0,sizeof(result));

  int ii;  // 循环的计数器。

  int pos=0;  // 已生成有效随机数的个数。

  while (1)
  {
    int itmp=rand()%52+1;

    // 除去重复的数据。
    for (ii=0;ii<pos;ii++)
    {
      if (itmp==result[ii]) break;  // 找到了重复的数。
    }

    if (ii<pos) continue;  // 如果ii<pos,表示上面的循环是跳出的。

    // 以下代码表示是新的不重复的数。
    printf("%d ",itmp);
    result[pos++]=itmp;

    if (pos==52) break;  // 已经得到了52个有效的数,循环跳出。
  }

  printf("\n");

  for (ii=0;ii<52;ii++)  // 显示结果。
  {
    printf("%d ",result[ii]);
  }

  printf("\n");
}

13)编写一个扑克的发牌程序,一副牌除了大小王,还有52张牌,随机洗牌,再发给四个人。

       提示:

(1)把一副牌的全部牌面可以用1-52的数值表示,数组是一个好选择;

(2)洗牌就是生成范围在1-52之间不重复的随机数。

(3)定义四个数组,代表四个人,把洗好的52个数按顺序赋给四个数组就行了。如果不想定义四个数组,用一个二维数组也行。

(4)把四个数组的值显示出来,就是每个人的牌面。

源代码:

/*
 * 程序名:task.c,此程序演示随机发牌功能。
 * 作者:C语言技术网(www.freecplus.net) 日期:20200510
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
 
int main()
{
  srand(time(0));  // 播下随机种子。

  int result[52];  // 存放已生成的不重复的随机数。
  memset(result,0,sizeof(result));

  int ii;  // 循环的计数器。

  int pos=0;  // 已生成有效随机数的个数。

  while (1)
  {
    int itmp=rand()%52+1;

    // 除去重复的数据。
    for (ii=0;ii<pos;ii++)
    {
      if (itmp==result[ii]) break;  // 找到了重复的数。
    }

    if (ii<pos) continue;  // 如果ii<pos,表示上面的循环是跳出的。

    // 以下代码表示是新的不重复的数。
    result[pos++]=itmp;

    if (pos==52) break;  // 已经得到了52个有效的数,循环跳出。
  }

  for (ii=0;ii<52;ii++)  // 显示结果。
  {
    printf("%d ",result[ii]);
  }

  printf("\n");

  /*
  int play1[13],play2[13],play3[13],play4[13];
  memset(play1,0,sizeof(play1));
  memset(play2,0,sizeof(play2));
  memset(play3,0,sizeof(play3));
  memset(play4,0,sizeof(play4));

  for (ii=0;ii<13;ii++)
  {
    play1[ii]=result[ii*4+0];
    play2[ii]=result[ii*4+1];
    play3[ii]=result[ii*4+2];
    play4[ii]=result[ii*4+3];
  }

  for (ii=0;ii<13;ii++) printf("%d ",play1[ii]);
  printf("\n");

  for (ii=0;ii<13;ii++) printf("%d ",play2[ii]);
  printf("\n");

  for (ii=0;ii<13;ii++) printf("%d ",play3[ii]);
  printf("\n");

  for (ii=0;ii<13;ii++) printf("%d ",play4[ii]);
  printf("\n");
  */

  int play[4][13];
  memset(play,0,sizeof(play));
  
  for (ii=0;ii<13;ii++)
  {
    play[0][ii]=result[ii*4+0];
    play[1][ii]=result[ii*4+1];
    play[2][ii]=result[ii*4+2];
    play[3][ii]=result[ii*4+3];
  }

  for (ii=0;ii<13;ii++) printf("%d ",play[0][ii]);
  printf("\n");

  for (ii=0;ii<13;ii++) printf("%d ",play[1][ii]);
  printf("\n");

  for (ii=0;ii<13;ii++) printf("%d ",play[2][ii]);
  printf("\n");

  for (ii=0;ii<13;ii++) printf("%d ",play[3][ii]);
  printf("\n");
}

九、版权声明

C语言技术网原创文章,转载请说明文章的来源、作者和原文的链接。

来源:C语言技术网(www.freecplus.net

作者:码农有道

如果文章有错别字,或者内容有错误,或其他的建议和意见,请您联系我们指正,非常感谢!!!

 


C语言技术网(www.freecplus.net),粤ICP备19156379号

友情链接: 猿说编程    程序分享   

点击关闭