C程序设计 谭浩强 第三章

变量类型

常量

注:一般变量初始化不是在编译阶段完成的(只有静态存储变量和外部变量的初始化时在编译阶段完成的),而是在程序运行时执行本函数时赋予初值的,相当于执行一个赋值语句。

  • 整型常量

  • 实型常量:十进制小数形式、指数形式,如12.34e3(代表12.34*10^3)

  • 字符常量

    • 普通字符:用单撇号括起来的一个字符
    • 转义字符:
    转义字符字符值输出结果
    \’
    \"""
    \\\\
    \a警告产生声音或视觉信号
    \b退格光标当前位置后退一个字符
    \f换页光标当前位置移到下一页的开头
    \n换行将光标当前位置移动到下一行的开头
    \r回车将光标当前位置移到本行的开头
    \t水平制表符将光标当前位置移到下一个Tab位置
    \v垂直制表符将光标当前位置移到下一个垂直制表对齐点
    \o \O \ooo与该八进制码对应的ASCII字符与该八进制码对应的字符
    \xh[h…]与该十六进制码对应的ASCII字符与该十六进制码对应的字符
  • 字符串常量

    "China";
    "boy";
    "123";
    
  • 符号常量

#define PI 3.1416

常变量

C99允许常变量,方法是在定义变量时,前面加一个关键字const

const int a=3;
#define Pi 3.1415
const float pi = 3.1415;

符号常量Pi和常变量pi都代表3.1415,在程序中都能使用。但二者性质不同:定义符号常量用#define ,它是预编译指令,它只是用符号常量代表一个字符串,在预编译时仅仅进行字符替换,在与编译后,符号常量就不存在了(全置换成了3.1415),对符号常量的名字是不分配存储单元的。而常量变量要占用存储单元,有变量值,只是该值不改变而已。从使用的角度看,常变量具有符号常量的优点,而且使用方便。有了常变量以后,可以不必多用符号常量。

标识符

C语言规定标识符只能由字母、数字和下画线3种字符组成,且第1个字符必须为字母或下画线。

下面是不合法的标识符和变量名:

M.D.John ¥123 #33 3D64 a>b

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JljGMeBZ-1645273375886)(C:\Users\zhizh\Downloads\C变量类型大全.png)]

整型

如何求一个负数?

方法:先将此数的绝对值写成二进制形式,然后对其所有二进制按位取反,再加1。

5的原码: 0 0 0 0 0 0 0 0 | 0 0 0 0 0 1 0 1 |

按位取反:1 1 1 1 1 1 1 1 | 1 1 1 1 1 0 1 0 |

再加1: 1 1 1 1 1 1 1 1 | 1 1 1 1 1 0 1 1 |

在存放整数的存储单元中,最左面一位是用来表示符号的。如果该位为0,表示数值为正;如果该位为1,表示数值为负。

Visual C++的安排

类型字节数取值范围
int4-231~231-1
unsigned int40~2^32-1
short2-215~215-1
unsigned short20~2^16-1
long4-231~231-1
unsigned long40~2^32-1
long long8-263~263-1
unsigned long long80~2^64-1

C标准没有具体规定各种类型数据所占有的存储单元的长度,这是由各编译系统自行决定的。C标准只要求long型数据长度不短于int型,short型不长于int型。即

sizeof(short)<=sizeof(int)<=sizeof(long)<=sizeof(long long)

//长整型(long int)
long int = 123L;
//在一个整数的末尾加大写字母L或小写字母l,表示它是长整型(long int)

**注:**对无符号整型数据用“%u”格式输出。%u表示用无符号十进制数的格式输出。

字符型

ASCII字符集基本集包括了127个字符。

  • 字母:大写英文字幕AZ,小写英文字幕az
  • 数字:0~9
  • 专门符号:29个
  • 空格符:空格、水平制表符tab、垂直制表符、换行、换页
  • 不能显示的字符:空字符(’\0’表示),警告(’\a’表示)、退格、回车’\r’
类型字节数取值范围
signed char1-27~27-1
unsigned char10~2^8-1

浮点型

在C中把实数称为浮点数?在C语言中,实数是以指数形式存放在存储单元中的。一个实数表示为指数可以有不止一种形式,如3.14159可以表示为3.14159*10^0 0.314159 * 10^2 31.4159 * 10^-1 314.159 * 10^-2等,它们表示同一个值。可以看到:小数点的位置是可以在314159几个数字之间、之前或之后浮动的,只要在小数点位置浮动的同时改变指数的值。

类型字节数有效数字数字范围(绝对值)
float460以及1.2*10^-38~3.4 * 10^38
double8150以及2.3*10^-308~1.7 * 10^308
long double8150以及2.3*10^-308~1.7 * 10^308
long double16190以及3.4*10^-4932~1.7 * 10^4932

**注:**C程序中的实型常量都作为双精度浮点型常量

如果有

float a = 3.14159;

在进行编译时,对float变量分配4个字节,但对于浮点型常量3.14159,则按双精度处理,分配8个字节。编译系统会发出“警告”(warning truncation from ’ const double ’ to ’ float ')。意为“把一个双精度常量转换为float型”,提醒用户注意这种转换可能会损失精度。这样的“警告”,一般不会影响程序运行结果的正确性,但会影响程序运行结果的精确度。

可以在常量的末尾加专用字符,强制指定常量的类型。如在3.14159后面加字幕F或者f,就表示是float型常量,分配4个字节。如果在实型常量后面加大写或小写的L,则指定此常量为long double型。如:

float a = 3.14149f;//把此3.14159按单精度浮点常量处理,编译时不出现“警告”
long double a=1.23L;//把此1.23作为long double型处理

枚举

如果一个变量只有几种可能的值,则可以定义为枚举类型,所谓”枚举“就是指把可能的值一一列举出来,变量的值只限于列举出来的值的范围内。

enum [枚举名] {枚举元素列表}

enum Weekday{sun,mon,true,wed,thu,fri,sat};
enum Weekday workday,weekend;

workday和weekend被定义为枚举变量,花括号称为枚举元素枚举常量

注:

  • C编译对枚举类型的枚举元素按常量处理,故称枚举常量。不要因为它们是标识符(有名字)而把它们看作变量,不能对它们赋值。

    sun = 0;
    mon = 1;
    
  • 每一个枚举元素都代表一个整数,C语言编译按定义时的顺序默认它们的值为0,1,2,3,4,5……。在上面的定义中,sun的值自动设为0,mon的值为1,……,sat的值为6。如果有赋值语句:

    ​ workday = mon;

    相当于

    ​ workday = 1;

    enum Weekday{sun=7,mon=1,tue,wed,thu,fri,sat}workday,week_end;
    //指定枚举常量sun的值为7,mon为1,以后顺序加1,sat为6
    
  • 枚举元素可以用来作判断比较

运算符和表达式

(常见的不写了)

位运算符:<< >> ~ | ^ &

条件运算符:?:

强制类型转换运算符:(类型)

位运算符

  • 按位与运算

    按位与运算符"&"是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。

    例如:9&5可写算式如下: 00001001 (9的二进制补码)&00000101 (5的二进制补码) 00000001 (1的二进制补码)可见9&5=1。

    按位与运算通常用来对某些位清0或保留某些位。例如把a 的高八位清 0 , 保留低八位, 可作 a&255 运算 ( 255 的二进制数为0000000011111111)。

    int main()
    {
    	int a=9,b=5,c;
    	c=a&b;
    	printf("a=%d/nb=%d/nc=%d/n",a,b,c);
    }
    
  • 按位或运算

    按位或运算符“|”是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。
    例如:9|5可写算式如下: 00001001|00000101
    00001101 (十进制为13)可见9|5=13

    int main()
    {
    	int a=9,b=5,c;
    	c=a|b;
    	printf("a=%d/nb=%d/nc=%d/n",a,b,c);
    }
    
  • 按位异或运算

    按位异或运算符“”是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。参与运算数仍以补码出现,例如95可写成算式如下: 00001001^00000101 00001100 (十进制为12)

    int main(){
    int a=9;
    a=a^15;
    printf("a=%d/n",a);
    }
    
  • 求反运算

    求反运算符~为单目运算符,具有右结合性。 其功能是对参与运算的数的各二进位按位求反。例如~9的运算为: ~(0000000000001001)结果为:1111111111110110

  • 左移运算

    左移运算符“<<”是双目运算符。其功能把“<< ”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。例如: a<<4 指把a的各二进位向左移动4位。如a=00000011(十进制3),左移4位后为00110000(十进制48)。

  1. 右移运算

    右移运算符“>>”是双目运算符。其功能是把“>> ”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。
    例如:设 a=15,a>>2 表示把000001111右移为00000011(十进制3)。 应该说明的是,对于有符号数,在右移时,符号位将随同移动。当为正数时, 最高位补0,而为负数时,符号位为1,最高位是补0或是补1 取决于编译系统的规定。Turbo C和很多系统规定为补1。

    #include<stdio.h>
    
    int main()
    {
    	int a,b;
    	printf("input a number: ");
    	scanf("%d",&a);
    	b=a>>5;
    	printf("a=%d\tb=%d\n",a,b);
    	
    	return 0;
    }
    

不同类型数据混合运算

  • float和double经过+ - * /运算结果是double
  • int和float或double型,先把int型和float型转换为double型,然后进行运算。
  • char数据类型与整数型运算,就是把字符的ASCII码与整型数据进行运算。

赋值过程中的类型转换

  • 将浮点型数据(包括单、双精度)赋给整型变量时,先对浮点数取整,即舍弃小数部分,然后赋予整型变量。
  • 将整型数据赋给单、双精度变量时,数值不变,但以浮点数形式存储到变量中。
  • 将一个double型数据赋给float变量时,先将双精度数转换为单精度,即取6~7位有效数字,存储到float型变量的4个字节中。应注意双精度数值的大小不能超过float型变量的数值范围。
  • 字符型数据赋给整型变量时,将字符的ASCII码赋给整型变量。
  • 将一个占字节多的整型数据赋给一个占字节少的整型变量或字符变量(例如把占4个字节的int型数据赋给占2个字节的short变量或占1个字节的char变量时),只将其低字节原封不动地送到被赋值的变量。

头文件

在对程序进行编译预处理时,系统会把在该头文件中存放的内容调出来,取代本行的#include指令。这些内容就成为了程序中的一部分。调用不同的库函数,应当把不同的头文件包含进来。

#include “stdio.h”

#include <stdio.h>

这两种#include指令形式的区别是:用尖括号形式如<stdio.h>时,编译系统从存放C编译系统的子目录中去找所要包含的文件,这称为标准方式。如果用双撇号形式(如”stdio.h"),在编译时,编译系统先在用户的当前目录(一般时用户存放源程序文件的子目录)中寻找要包含的文件,若找不到,再按标准方式查找。如果用#include指令时为了使用系统库函数,因而包含系统提供的相应的头文件,这时以用标准方式为宜,以提高效率。如果用户想包含的头文件不是系统提供的相应的头文件,而是用户子集编写的文件(这种文件一般都存放再用户当前目录中),这时应当双撇号形式,否则会找不到所需的文件。如果该文件不在当前目录中,可以在双撇号中写出文件路径(如#include “C:\temp\file1.h”,以便系统能从中找到所需的文件。

格式输出

格式字符说明
d,i以带符号的十进制形式输出整数(正数不输出符号)
o以八进制无符号形式输出整数(不输出前导符0)
x,X以十六进制无符号形式输出整数(不输出前导符0x),用x则输出十六进制数的a~f时以小写形式输出,用X时,则以大写字母输出。
u以无符号十进制形式输出整数
c以字符形式输出,只输出一个字符
s输出字符串
f以小数形式输出单、双精度数,隐含输出6位小数
e,E以指数形式输出实数,用e时指数以"e"表示(如1.2e+02),用E时指数以“E”表示(如1.2E+02)
g,G选用%f或%e格式中输出宽度较短的一种格式,不输出无意义的0。用G时,若以指数形式输出,则指数以大写表示
l长整型整数,可加在格式符d o x u 前面
m(代表一个正整数)数据最小宽度
n(代表一个正整数)对实数,表示输出n位数小数,对字符串,表示截取的字符个数
输出的数字或字符在域内向左靠

指定数据宽度和小数位数,用%m.nf

例3.5已经用了“%7.2”格式指定了输出的数据占7列,其中包括2位小数。对其后一位采取四舍五入方法处理,即向上或向下取近似值。如果把小数部分指定为0,则不仅不输出小数,而且小数点也不输出。

格式输入

格式字符说明
d,i输入有符号的十进制整数
u输入无符号的十进制整数
o输入无符号的十进制整数
x,X输入无符号的十六进制整数(大小写作用相同)
c以字符形式输出,只输入一个字符
s输入空字符串,将字符串送到一个字符数组中,在输入时以非空白字符开始,以第一个空白字符结束。字符串以串结束标志’\0’作为其最后一个字符
f输入实数,可以用小数形式或指数形式输入
e,E,g,G与f作用相同,可以用小数形式或指数形式输入
l输入长整型数据(可用%ld %lo %lx %lu)以及double型数据(用%lf或%le)
h输入短整型数据(可用%hd %ho %hx)
域宽指定输入数据所占宽度(列数),域宽应为正整数
*本输入项在读入后不赋给相应的变量

注:

  • scanf函数中的格式控制后面应当是变量地址,而不是变量名。
scanf("%f%f%f",&a,&b,&c);
  • 如果在格式控制字符串中除了格式声明以外还有其他字符,则在输入数据时在对应的位置上应输入与这些字符相同的字符。
scanf("a=%f,b=%f,c=%f",&a,&b,&c);
//应输入a=1,b=3,c=2
//若输入 1 3 2 会报错
  • 在用“%c”格式声明输入字符时,空格字符和“转义字符”中字符都作为有效字符输入。
scanf("%c%c%c",&c1,&c2,&c3);
//abc中间不要有空格
//a b c 错误
  • 在输入数值数据时,如输入空格、回车、Tab键或者非法字符(不属于数值的字符)认为该数据的结束。
scanf("%d%c%f",&a,&b,&c);
//若输入
//1234a123o.26	a=1234	b=a	c=123

习题

1.假如我国国民生产总值的年增长率7%,计算10年后我国国民生产总值与现在相比增长多少百分比。计算公式为 p = ( 1 + r ) n p=(1+r)^n p=(1+r)n,其中r为年增长率,n为年数,p为与现在相比的倍数。

#include <stdio.h>
#include <math.h>

int main() {

    float p, r;
    int n;
    r = 0.07; n = 10;

    p = pow(1 + r, n);

    printf("十年后与现在相比的倍数是 %f \n", p);

    return 0;
}

2.存款利息的计算。有1000元,想存5年,可按以下5种办法存:

(1)一次存5年期

(2)先存2年期,到期后将本息再存3年期

(3)先存3年期,到期后将本息再存2年期

(4)存1年期,到期后将本息再存1年期,连续存5次

(5)存活期存款,活期利息每一季度结算一次

2017年银行存款利息如下:

1年期定期存款利息为1.5%;

2年期定期存款利息为2.1%;

3年期定期存款利息为2.75%;

5年期定期存款利息为3%;

活期存款利息为0.35%(活期存款每一-季度结算一-次利息)

如果r为年利率,n为存款年数,则计算本息的公式如下:

1年期本息和: P= 1000* (1+r);

n年期本息和: P= 1000* (1+n* r);

存n次1年期的本息和: p = 1000 ∗ ( 1 + r ) n p = 1000 * (1 + r)^n p=1000(1+r)n

活期存款本息和: P = 1000 ∗ ( 1 + r 4 ) 4 n P = 1000 * (1+ \frac r4)^{4n} P=1000(1+4r)4n

说明: 1000 ∗ ( 1 + r 4 ) 1000*(1+\frac r4) 1000(1+4r)是一个季度的本息和。

#include <stdio.h>
#include <math.h>

int main() {

    float r1, r2, r3, r5, rh;
    float p5, p2, p3, p1, ph;
    r1 = 0.015; r2 = 0.021; r3 = 0.0275; r5 = 0.03; rh = 0.0035;

    printf("一次存5年期\n");
    p5 = 1000 * (1 + 5*r5);
    printf("5年期的本息和%f\n", p5);
    printf("================\n");

    printf("先存2年期,到期后将本息再转存3年期\n");
    p2 = 1000 * (1 + 2 * r2);
    p2 = p2 * (1 + 3 * r3);
    printf("先存2年期,到期后将本息再转存3年期的本息和是%f\n", p2);
    printf("================\n");

    printf("先存3年期,到期后将本息再存2年期\n");
    p3 = 1000 * (1 + 3 * r3);
    p3 = p3 * (1 + 2 * r2);
    printf("先存3年期,到期后将本息再存2年期的本息和是%f\n", p3);
    printf("================\n");

    printf("存1年,到期后将本息再存1年期,连续存5次\n");
    p1 = 1000 * pow(1 + r1, 5);
    printf("存1年,到期后将本息再存1年期,连续存5次的本息是%f\n", p1);
    printf("================\n");

    printf("活期存款方案,活期利息每季度结算一次\n");
    ph = 1000 * pow(1 + rh/4, 4*5);
    printf("活期存款方案,连续存5年的本息是%f\n", ph);
    printf("================\n");

    return 0;
}

3、购房从银行贷了一笔款d,准备每月还款额为p,月利率为r,计算多少月能还清。设d为300 000元,p为6000元,r为1%。对求得的月份取小数点后一位,对第2位按四舍五人处理。

提示:计算还清月数m的公式如下:
m = log  p − log ( p − d ∗ r ) log ( 1 + r ) m = \frac {\text{log }p-\text{log}(p-d*r)}{\text{log}(1+r)} m=log(1+r)log plog(pdr)
可以将公式改写为
m = log ( p p − d ∗ r ) log ( 1 + r ) m = \frac {\text{log}(\dfrac {p}{p-d*r})}{\text{log}(1+r)} m=log(1+r)log(pdrp)
C的库函数中有求对数的函数log10,是求以10为底的对数,log§表示 log p。

#include <stdio.h>
#include <math.h>

int main() {
   float d, p, r, m;
    printf("请按顺序输入d,p,r:");
    fflush(stdout);
    scanf_s("%f,%f,%f", &d, &p, &r);

    r = 0.01 * r;
    m = ((log(p) - log(p - d*r))) / log(1 + r);
    printf("m:%6.1f\n", m);

    return 0;
}

分析下面的程序

#include<stdio.h>
int main()
{
	char c1, c2;
	c1 = 97;
	c2 = 98;
	printf("c1=%c, c2=%c\n", c1, c2);
	printf("c1=%d, c2=%d\n", c1, c2);
	return 0;
}

(1)运行时会输出什么信息?为什么?

//c1=a, c2=b;	ASCII编码97对应'a' ASCII编码97对应'b'
//c1=97, c2=98 	char型int型输出就是对应的数字

(2)如果将程序第4,5行改为

  • c1 = 197;
  • c2 = 198;

运行时会输出什么信息?为什么?

//c1=�, c2=�
//c1=-59, c2=-58
//ASCII编码总共128个

(3)如果将程序第3行改为

int c1,c2;

运行时会输出什么信息?为什么?

//c1=a, c2=b;	ASCII编码97对应'a' ASCII编码97对应'b'
//c1=97, c2=98 	char型int型输出就是对应的数字

5、用下面的scanf 函数输入数据,使a=3,b=7,x=8.5,y=71. 82,cl=‘A’,c2=‘a’。在键盘上应如何输入?

#include <stdio.h>

int main() {

    int a, b;
    float x, y;
    char c1, c2;
    scanf("%d%d%f%f %c %c", &a, &b, &x, &y, &c1, &c2);
    fflush(stdin);
    printf("%d, %d, %f, %f, %c, %c\n", a, b, x, y, c1, c2);

    return 0;
}

6、请编程序将“China"译成密码,密码规律是:用原来的字母后面第4个字母代替原来的字母。例如,字母“A”后面第4个字母是“E”,用“E”代替“A”。因此,“China"应译为“Glmre”。请编一程序,用赋初值的方法使cl,c2,c3,c4,c5这5个变量的值分别为’C’,‘h’,‘i’,‘n’,‘a’ ,经过运算,使c1,c2,c3,c4,c5 分别变为’G’,‘l’,‘m’,‘r’,‘e’。分别用putchar函数和printf函数输出这5个字符。

#include <stdio.h>
#include <math.h>

int main() {

    char c1 = 'C', c2 = 'h', c3 = 'i', c4 = 'n', c5 = 'a';
    printf("原字符串是%c%c%c%c%c\n", c1, c2, c3, c4, c5);

    c1 = c1 + 4;
    c2 = c2 + 4;
    c3 = c3 + 4;
    c4 = c4 + 4;
    c5 = c5 + 4;

    printf("变形后的字符串为:");
    putchar(c1);
    putchar(c2);
    putchar(c3);
    putchar(c4);
    putchar(c5);

    return 0;
}

7、设圆半径r=1.5,圆柱高h=3,求圆周长、圆面积、圆球表面积、圆球体积、圆柱体积。用scanf输入数据,输出计算结果,输出时要求有文字说明,取小数点后2位数字。请编程序。

#include <stdio.h>
#include <math.h>

int main() {

    float r, h, zhouchang, ymianji, qmianji, qtiji, zhutiji;
    float pi = 3.141526;
    printf("请输入圆的半径和圆柱的高:");
    fflush(stdout);
    scanf("%f%f", &r, &h);
    fflush(stdin);
    zhouchang = 4 * pi *r;
    ymianji = pi * pow(r, 2);
    qmianji = 4 * pi * pow(r, 2);
    qtiji = 3.0 / 4.0 * pi * pow(r, 3);
    zhutiji = pi * pow(r, 2) * h;

    printf("圆周长=%6.2f, 圆面积=%6.2f, 圆球表面积=%6.2f, 圆球体积=%6.2f, 圆柱体积=%6.2f\n", zhouchang, ymianji, qmianji, qtiji, zhutiji);

    return 0;
}

8.编程序,用getchar函数读入两个字符给c1和c2,然后分别用putchar函数和printf函数输出这两个字符。思考以下问题:

(1) 变量cl和c2应定义为字符型、整型还是二者皆可?

(2) 要求输出cl和c2值的ASCII码,应如何处理?用putchar函数还是printf函数?

(3) 整型变量与字符变量是否在任何情况下都可以互相代替?如:
char c1,c2;

int cl,c2;
是否无条件地等价?

//1.在0-128的区间中,二者都可,但是超过了128,应定义为整型
//2.二者都可
//3.不是,因为两者的存储范围不同,如果整数超出了字符的存储范围,则不能相互替换
  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值