程序设计基础
在程序设计中,基本数据类型决定了变量可以存储的值和可执行的操作。
运算符与表达式用于执行各种计算和逻辑操作,如加减乘除、比较和逻辑运算,是编写有效程序的核心。
选择和循环结构控制程序流程,通过条件判断和重复执行代码块,实现复杂的逻辑和功能。
基本数据类型
C 语言的数据类型包括:基本类型(整型、浮点型、字符型等)、构造类型(数组、结构体)、指针类型、空类型。
整型
int
4 字节
short
2 字节
long
4 字节(Windows 32位/64位)、8 字节(Linux 64位)
- ILP32 LP64 LLP64 ILP64 char 8 8 8 8 short 16 16 16 16 int 32 32 32 64 long 32 64 32 64 long long 64 64 64 64 void * 32 64 64 64
补码
补码是对原码进行取反加一用来表示相应的负数。
CPU 只能计算加法,减法通过与负数相加实现。
溢出
静态性语言中,当整型数值增加超过定义类型所能表示的范围,引起二进制最高位发生变化,计算机输出时,对无符号类型先读取最高位作为符号位,导致溢出错误。
浮点数
类型 | 大小 | 精确度 |
---|---|---|
float |
32位 | 6~7 |
double |
64位 | 15~16 |
long double |
128位 | 18~19 |
浮点数的存储原理
数符 + 小数部分 + 指数
对 32 位而言,首位存符号(数符),最后 8 位为指数,中间 23 位用于小数部分数值的存储。
精度丢失
判断是否相等的代码示例:
float x = 1.23456789e10;
// x 的有效数字为 9,浮点型的精度只有 6-7 位
double y = 1.23456789e10;
printf("x = %f",x);
//x = 12345678848
printf("x + 20 = %f",x + 20);
//x + 20 = 12345678848
printf("y = %f",y);
//y = 12345678900
printf("y + 20 = %f",y + 20);
//y + 20 = 12345678920
flaot f = 1.456;
if (f - 1.456 > -1e-6 && f - 1.456 < 1e-6){
printf("f is equal to 1.456\n");
}
else {
printf("f is not equal to 1.456\n");
}
字符型
字符占一个字节,使用单引号('
)赋值,字符串用双引号("
)赋值。
字符串常量是一段固定的文本,通常用于表示程序中的不可变字符串。
转义字符
printf("abc\b \n");
//退格,输出" "(空格)替换掉”c“
scanf()
函数
缓冲区 是一段内存空间,分为
- 全缓冲:磁盘读写
- 行缓冲:输出和输出遇到换行符(
\n
)时,执行 I/O 操作。stdin
和stdout
- 不带缓冲:标准出错情况
stderr
scanf("%d", &i);
printf("i = %d\n", i);
fflush(stdin); /*rewind(stdin);*/
scanf("%c", &c);
printf("c = %c\n", c);
第一次输入时,使用回车键(\n
)确认,
在缓冲区中写入的内容包括int类型的数字(i
)以及 换行符(\n
),
scanf
函数会匹配相同类型数字(i
),留下换行符(\n
)在缓冲区中,
使用 fflush
或者 rewind
(VS 2019 使用)对缓冲区进行刷新,
清除掉第一次输入时保留在缓冲区的换行符(\n
),顺利进入第二次输入,
否则,换行符(\n
)会在第二个 scanf
函数开始执行时匹配为字符串类型写入到 c 的地址中。
scanf()
循环读取
while (rewind(stdin), (ret = scanf("%d", &i)) != EOF)
{
printf("%d\n", i);
}
循环读取可以通过多次输入,对程序进行测试。
当输入类型与 scanf
函数中的类型不匹配时,scanf
不能将其读入指定的地址中,此时返回值 ret
为 0。如果不对缓冲区进行刷新,将会陷入死循环。
当从键盘输入 Crtl+Z 时,返回 EOF(-1)
,循环结束。
多类型混合输入
scanf
函数 在读取 字符型 char
时,&c
不能够忽略输入的空格与回车
双精度浮点型 double
的读取需使用 &lf
运算符与表达式
算术运算符
#include <stdio.h>
int main()
{
int i, j=0, k;
scanf("%d", &i);
while(i != 0)
{
k = i % 10; // 求余
printf("%d\n", k);
j = j * 10 + k;
i = i / 10;
}
printf("%d\n", j); // 对称数判断
}
混合运算
int i = 5;
float f;
f = i/2;
/*以整型计算,f的结果为2*/
f = (float)i/2
/*先对i进行强制类型转换(优先级更高),再计算i/2.f的结果为2.500000*/
关系运算符与逻辑运算符
if (year%4 == 0 && year%100 || year%400 == 0)
// 四年一闰,百年不闰,四百再闰
{
printf("%d is a leap year.\n",year);
}
else
{
printf("%d is not a leap year.\n",year);
}
位运算(左移/右移/异或)
- 右移(除 2,负数:减 1 除 2)
- 按位与/或/异或 满足交换律
#include <stdio.h>
int main()
{
int i;
int a,b;
int j[5]={1,2,3,2,1};
int result = 0;
scanf("%d",&i);
//左移:乘上2的n次方 —— left shift: i*(2^n)
printf("i * 64 = i << 6 = %d\n",i << 6);
//右移:除以2的n次方 —— right shift: i/(2^n)
printf("i / 8 = i >> 3 = %d\n", i >> 3);
scanf("%d%d",&a,&b);
//按位异或:交换变量的值 —— XOR: swap
printf("Swap a and b:\n");
printf("BEFORE: a = %d,b = %d\n", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("AFTER: a = %d,b = %d\n", a, b);
//按位取反、异或:改变符号的两种方法 —— NOT~ XOR^: a = -a, two different ways
printf("-a = %d, -b = %d\n", ~a + 1, (b ^ -1) + 1);
//按位与:求余 —— AND: i % n
printf("i / 4 = %d ... %d\n", i/4, i & (4 - 1));
//按位与:判断奇偶数 —— AND: isEven
if ((i & 1) == 0)
{
printf("%d is even\n",i);
}
else
{
printf("%d is a odd number.\n",i);
}
//按位异或:找出数组中单独出现的数字 —— XOR: exclusive
//任何数与自己异或,结果为0;任何数与0异或,结果仍为本身
for (i = 0; i < 5; i++) {
result = result^j[i];
printf("j[%d] = %d\n", i, j[i]);
}
printf("%d is exculsive.\n", result);
return 0;
}
三目运算符
#include <stdio.h>
int main()
{
int a[5];
//任意非0数组初始化
int max1, max2, i;
max1 = a[0]>a[1]?a[0]:a[1];
max2 = a[0]<a[1]?a[0]:a[1];
i=2;
while(i < 5)
{
if(a[i] > max1)
{
max1 = a[i];
}
else if(a[i] > max2)
{
max2 = a[i];
}
i++;
}
printf("max1=%d, max2=%d", max1, max2);
}
自增自减
j = i++;
等价于 j = i; i = i+1
其他重点:指针自增(偏移)
选择循环
选择结构程序设计
if
和 switch
#include<stdio.h>
int main()
{
int year,mon;
while (scanf("%d%d", &year, &mon) != EOF) {
switch (mon)
{
case 2:printf("%d is %d days\n", mon, 28+(year%4==0 && year%100 || year%400==0)); break;
case 4:
case 6:
case 9:
case 11:printf("%d is 30 days\n", mon); break;
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:printf("%d is 31 days\n", mon); break;
default:printf("error.\n");
}
}
}
switch
语句中只能匹配整型数或字符
使用 break
跳出其他语句的执行
调整输出结果相同的语句顺序
循环结构程序设计
goto
语句:无条件转向
goto
使程序跳转到 label
汇编后的地址
#include <stdio.h>
int main()
{
int i=1;
int total=0;
lable:
total = total + i;
i++;
if (i <= 100) {
goto lable;//向上跳转,实现循环
}
printf("total = %d\n", total);
return 0;
}
goto
语句只能用在同一个函数中跳转
#include <stdio.h>
int main()
{
int disk;
scanf("%d",&disk);
if (disk == 0)
{
goto disk_error;//向下跳转
}
printf("write success\n");
disk_error:
printf("system disk is error\n");
return 0;
}
while
和 for
循环
题目:输入日期,输出该日期是今年的第几天
int today(int y,int m,int d)
{
int i;
int today=0;
int a[12]={31,28,31,30,31,30,31,31,30,31,30,31};
for(i = 0; i < m-1; i++)
{
today = today + a[i];
}
today = today + d;
if(m>2)
{
today = today + (y%4==0 && y%100 || y%400==0);
}
return today;
}
continue
语句:跳出循环,执行判断