程序设计基础

在程序设计中,基本数据类型决定了变量可以存储的值和可执行的操作。

运算符与表达式用于执行各种计算和逻辑操作,如加减乘除、比较和逻辑运算,是编写有效程序的核心。

选择和循环结构控制程序流程,通过条件判断和重复执行代码块,实现复杂的逻辑和功能。

基本数据类型

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() 函数

缓冲区 是一段内存空间,分为

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);
}

位运算(左移/右移/异或)

#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

其他重点:指针自增(偏移)

选择循环

选择结构程序设计

ifswitch

#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;
}

whilefor 循环

题目:输入日期,输出该日期是今年的第几天

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 语句:跳出循环,执行判断