C语言基础-变量常量和数据类型

变量常量和数据类型

常量

常量指的是在程序运行期间不会改变的值。
常量分类:

  1. 字面常量:直接写在代码中的值,如 10, 'A', 3.14
  2. const常量
const int max = 100; // max不能再被赋新值
  1. define宏定义
#define PI 3.14159
  • const 修饰的变量会占用内存;
  • #define 是预处理指令,不占用内存。

变量

变量是程序中用于存储数据的命名内存空间。变量的值可以在程序运行过程中改变。
变量定义格式:

数据类型 变量名;
数据类型 变量名 = 初始值;
int age = 25;
float price = 19.99;
char grade = 'A';
int r = 3;   // 变量定义
int r;    // 没有变量值的变量定义叫做声明
extern int r;  // 添加了关键字extern

  1. 变量定义会开辟内存空间。变量声明不会开辟内存空间。
  2. 变量想要使用必须有定义。
    • 当编译器编译程序时,在变量使用之前,必须看到变量的定义,如果没有,编译器会自动找寻一个变量声明提升为定义。
    • 如果该变量的声明前有extern关键字,则无法提升为定义。

练习1:圆的面积与周长

#include<stdio.h>
#define PI 3.1415
int main(void) {
	const int r = 3;  //只读变量

	float s = PI * r * r;
	float l = 2 * PI * r;

	printf("圆的周长为:%f\n", l);
	printf("圆的面积为:%f\n", s);

	return 0;
}

标识符

变量与常量的统称。
命名规则:

  1. 通常常量使用大写、变量使用小写。大小写严格区分。
  2. 只能使用字母、数字、下划线命名标识符。且,数字不能开头。

sizeof运算符

  • sizeof不是函数,不需要包含任何头文件,用于获取某个类型或变量所占的内存字节数
  • sizeof其结果的类型是 size_t(无符号整数类型),而不是 int 类型。
#include <stdio.h>

int main(void) {
    int i = 0;
    short s = 0;
    long l = 0;
    long long ll = 0;
    char c = 'A';
    float f = 3.14f;
    double d = 3.14;
    long double ld = 3.14;
    unsigned char uc = 0;
    unsigned short us = 0;
    unsigned int ui = 0;
    unsigned long ul = 0;
    unsigned long long ull = 0;

    printf("sizeof(int) = %zu\n", sizeof(i));
    printf("sizeof(short) = %zu\n", sizeof(s));
    printf("sizeof(long) = %zu\n", sizeof(l));
    printf("sizeof(long long) = %zu\n", sizeof(ll));
    printf("sizeof(unsigned int) = %zu\n", sizeof(ui));
    printf("sizeof(char) = %zu\n", sizeof(c));
    printf("sizeof(float) = %zu", sizeof(f));
    printf("sizeof(double) = %zu\n", sizeof(d));
    printf("sizeof(long double) = %zu\n", sizeof(ld));
    printf("sizeof(unsigned char) = %zu\n", sizeof(uc));
    printf("sizeof(unsigned short) = %zu\n", sizeof(us));
    printf("sizeof(unsigned int) = %zu\n", sizeof(ui));
    printf("sizeof(unsigned long) = %zu\n", sizeof(ul));
    printf("sizeof(unsigned long long) = %zu\n", sizeof(ull));

    // 也可以直接对类型使用 sizeof
    printf("sizeof(void *) = %zu\n", sizeof(void*));  // 指针类型(平台相关)

    return 0;
}

整型

整型用于表示整数数值。

类型 占用字节 范围(32位系统) 输出
int 4 -2,147,483,648 ~ 2,147,483,647 %d
short 2 -32,768 ~ 32,767 %hd
long 4 同int(Windows) %ld
long long 8 -9 × 10¹⁸ ~ 9 × 10¹⁸ %lld
unsigned 同上所有 只表示非负数,范围翻倍 上面+u

字符型

字符型用于表示单个字符。

类型

#include <stdio.h>
#include <ctype.h>   // 提供字符处理函数

int main(void) {
    char ch = 'A';
    char ch2 = 'z';
    char numChar = '5';

    // 输出字符本身
    printf("字符 ch 的值为:%c\n", ch);

    // 获取字符的 ASCII 值(整数)
    printf("字符 ch 的 ASCII 值为:%d\n", ch);

    // 通过 ASCII 值创建字符
    char ch_from_ascii = 66; // ASCII 66 是 'B'
    printf("ASCII 值 66 对应的字符是:%c\n", ch_from_ascii);

    // 判断字符类型
    printf("ch2 是否是字母?%s\n", isalpha(ch2) ? "是" : "否");
    printf("numChar 是否是数字字符?%s\n", isdigit(numChar) ? "是" : "否");

    // 转换大小写
    printf("ch2 的大写是:%c\n", toupper(ch2));
    printf("ch 的小写是:%c\n", tolower(ch));

    // 使用 sizeof 查看 char 类型大小
    printf("char 类型所占字节数:%zu\n", sizeof(char));
    printf("ch 变量所占字节数:%zu\n", sizeof(ch));

    return 0;
}

注意

  • char 本质是一个整型,占 1 字节;
  • 字符用单引号括起,如 'x',而不是 "x"(后者是字符串)。

转义字符

转义字符是在字符或字符串中表示特殊意义的字符,写法以反斜杠 \ 开头,用来表示一些不能直接输入的字符,如换行、制表、双引号等。

常用转义字符一览表:

转义字符 描述 ASCII码(十进制)
\n 换行符 10
\r 回车符 13
\t 水平制表符(Tab) 9
\b 退格符 8
\a 响铃(警报) 7
\\ 反斜杠(\) 92
\' 单引号(') 39
\" 双引号(") 34
\? 问号 63
\0 空字符(null) 0
  • 字符 '\\' 实际上是表示反斜杠这个字符;
  • 字符串 "\0" 会使字符串提前结束;
  • 响铃符 \a 在大多数现代系统中不会发出声音,但在某些终端(或启用了声音的IDE)中仍可能发出“哔”声。

实型(浮点型)

用于表示小数(实数)。

类型 占用空间(通常) 有效位数(十进制) 输出
float 4 字节 6~7 位 f
double 8 字节 15~16 位 lf
long double 16 字节(有的平台为12) 18~21 位 L

示例

#include <stdio.h>
#include <float.h>  // 提供浮点数相关的宏常量

int main(void) {
    float f = 3.1415926f;
    double d = 3.14159265358979;
    long double ld = 3.1415926535897932384626L;

    // 基本输出
    printf("float 类型变量 f = %f\n", f);
    printf("double 类型变量 d = %f\n", d);
    printf("long double 类型变量 ld = %Lf\n", ld);

    // 控制小数点精度
    printf("\n精度控制输出:\n");
    printf("f 保留2位:%.2f\n", f);
    printf("d 保留6位:%.6f\n", d);
    printf("ld 保留10位:%.10Lf\n", ld);

    // 科学计数法输出
    printf("\n科学计数法:\n");
    printf("f = %e\n", f);
    printf("d = %e\n", d);

    // 查看类型占用空间
    printf("\nsizeof(float) = %zu 字节\n", sizeof(float));
    printf("sizeof(double) = %zu 字节\n", sizeof(double));
    printf("sizeof(long double) = %zu 字节\n", sizeof(long double));

    // 浮点数范围和精度
    printf("\nfloat 的最小值:%e\n", FLT_MIN);
    printf("float 的最大值:%e\n", FLT_MAX);
    printf("float 的有效位数:%d 位\n", FLT_DIG);

    printf("\ndouble 的最小值:%e\n", DBL_MIN);
    printf("double 的最大值:%e\n", DBL_MAX);
    printf("double 的有效位数:%d 位\n", DBL_DIG);

    printf("\nlong double 的最小值:%Le\n", LDBL_MIN);
    printf("long double 的最大值:%Le\n", LDBL_MAX);
    printf("long double 的有效位数:%d 位\n", LDBL_DIG);

    return 0;
}

进制

C语言中整数常量的进制表示

表示方式 前缀 示例 实际含义(十进制)
二进制 0b / 0B 0b1010 10(C99或GCC扩展)
八进制 0 012 10
十进制 10 10
十六进制 0x / 0X 0xA 10
标准的C并不支持二进制字面量(如 `0b1010`),但 **GCC 和 Clang 支持**。MSVC 则不支持。

二进制(C99标准及以上)

int a = 0b1010; // 等价于10(需要编译器支持)

八进制

int b = 012; // 以0开头,等价于10(十进制)

十六进制

int c = 0x1A; // 等价于26(十进制)

进制转换

十进制 ➡ 其他进制

十进制 ➡ 二进制:不断除以 2,记录余数,倒序排列

示例: 10 ➡ 二进制

10 ÷ 2 = 5 余 0  
5 ÷ 2 = 2 余 1  
2 ÷ 2 = 1 余 0  
1 ÷ 2 = 0 余 1  
倒序写:1010(即 0b1010)

十进制 ➡ 八进制:不断除以 8,记录余数

10 ÷ 8 = 1 余 2;1 ÷ 8 = 0 余 1 → 012

十进制 ➡ 十六进制:不断除以 16

10 ÷ 16 = 0 余 10 → 0xA

其他进制 ➡ 十进制

二进制 0b1010 =

1×2³ + 0×2² + 1×2¹ + 0×2⁰ = 8 + 0 + 2 + 0 = 10

八进制 012 =

1×8¹ + 2×8⁰ = 8 + 2 = 10

十六进制 0xA =

A = 10 → 10×1 = 10

C语言中进制转换的示例代码

#include <stdio.h>

int main(void) {
    int dec = 255;

    printf("十进制:%d\n", dec);
    printf("八进制:%o\n", dec);
    printf("十六进制(小写):%x\n", dec);
    printf("十六进制(大写):%X\n", dec);

    return 0;
}

计算机中数值的存储方式

1. 原码

原码是直接按数值的绝对值转换为二进制,最高位为符号位:

  • 正数:符号位为0;
  • 负数:符号位为1。

2. 反码

  • 正数:与原码相同;
  • 负数:符号位不变,其余位按位取反

3. 补码(计算机内部存储使用的方式)

  • 正数:与原码相同;
  • 负数:在反码基础上 +1

示例

(以8位为例,-5的表示):

十进制:   -5
原码:     10000101
反码:     11111010
补码:     11111011

💡 为什么用补码?

  • 统一了加减运算的电路设计;
  • 避免“负零”的问题;
  • 补码的加法溢出特性正好用于环绕存储。

数据溢出

数据溢出是指:一个变量存储的数值超出了它类型所能表示的最大或最小范围时,发生的数值错误或异常行为
数据溢出是导致 程序逻辑错误、安全漏洞(如缓冲区溢出)等的常见原因。

整数类型溢出

整型变量只能表示一定范围的整数,超出后会发生“循环”,通常按 补码规则 重新从最小值开始。

#include <stdio.h>
#include <limits.h>

int main(void) {
    int a = INT_MAX;
    printf("a = %d\n", a);
    a += 1;  // 整型溢出
    printf("a + 1 = %d(已溢出)\n", a);

    unsigned int b = UINT_MAX;
    printf("b = %u\n", b);
    b += 1;
    printf("b + 1 = %u(已回绕)\n", b);

    return 0;
}

浮点类型溢出和精度误差

  • 浮点型有非常大的表示范围;
  • 超过最大值会变为 正无穷 (+inf)
  • 过小的非零值可能变为 0.0(称为“下溢”);
  • 不能准确表示某些小数(如 0.1),存在精度误差
#include <stdio.h>
#include <float.h>

int main(void) {
    float f = 3.4e38f;  // 超过 float 最大值(~3.402823e+38)
    f *= 10;
    printf("f = %e\n", f);  // 输出 inf

    double d = 0.1 + 0.2;
    if (d == 0.3)
        printf("相等\n");
    else
        printf("不相等(浮点误差)\n");

    return 0;
}

类型限制符

类型限制符用于进一步控制变量的属性,比如读写权限、存储方式或生命周期。

限制符 说明
const 表示变量只读,值不能被修改
volatile 表示变量可能随时变化(多线程/硬件),防止编译器优化代码
static 表示变量具有静态存储期,局部静态变量生命周期延续至程序结束
extern 声明一个在其他文件中定义的全局变量
register 提示编译器将变量存储在寄存器中(已废弃)

字符串格式化输出输入

字符串格式化输出:printf

printf 是最常用的输出函数之一,可以按格式将变量值输出到终端。

基本格式:

printf("格式字符串", 参数1, 参数2, ...);

常用格式说明符

类型 格式符 示例值
整数(十进制) %d / %i 42
无符号整数 %u 42
八进制 %o 052
十六进制 %x / %X 0x2a / 0X2A
字符 %c 'A'
字符串 %s "hello"
浮点数 %f 3.141593
科学计数法 %e / %E 3.14e+00
指针 %p 地址
size_t 类型 %zu sizeof 返回值

精度与宽度控制

printf("%6.2f", 3.1415); // 总宽度6,小数点后保留2位,输出 "  3.14"
printf("%-10s", "abc");  // 左对齐,占10字符宽度,输出 "abc       "

字符串格式化输入:scanf

scanf 从标准输入中读取格式化数据,并存储到变量中。

基本格式

scanf("格式字符串", &变量1, &变量2, ...);

常用格式说明符

输入类型 格式符 示例
整数 %d int i; scanf("%d", &i);
字符 %c char c; scanf("%c", &c);
字符串 %s char str[20]; scanf("%s", str);
浮点数 %f float f; scanf("%f", &f);
双精度 %lf double d; scanf("%lf", &d);

注意事项

  • 读取字符串时不加 &char str[20]; scanf("%s", str);
  • %s 会读取非空白字符直到空格、回车或Tab
  • scanf 读取字符后 不会跳过换行符,需用空格或 getchar() 处理

printf vs putchar:输出函数对比

项目 printf putchar
功能 格式化输出,支持多种类型 仅输出一个字符
参数类型 字符串格式 + 可变参数 单个字符(char 或其值)
返回值 输出的字符数 成功返回输出的字符,失败返回 EOF
使用场景 多变量输出、复杂格式 简单输出单个字符或循环输出
所属头文件 <stdio.h> <stdio.h>

示例

#include <stdio.h>

int main(void) {
    char ch = 'A';

    // printf 输出字符串和多个变量
    printf("Hello, number = %d, char = %c\n", 42, ch);

    // putchar 输出单个字符
    putchar('H');
    putchar('i');
    putchar('\n');

    // 输出字符串的每个字符
    char str[] = "C语言";
    for (int i = 0; str[i] != '\0'; i++) {
        putchar(str[i]);
    }
    putchar('\n');

    return 0;
}

scanf vs getchar:输入函数对比

项目 scanf getchar
功能 格式化输入,支持多种类型 读取一个字符(包括回车)
参数类型 格式字符串 + 参数地址 无参数
返回值 成功读取的变量数量 成功返回读取的字符,失败返回 EOF
注意事项 自动跳过空白(除 %c 外) 会读取换行符等所有字符
使用场景 多变量输入、格式化读取 一次一个字符读取、控制台暂停
所属头文件 <stdio.h> <stdio.h>

示例

#include <stdio.h>

int main(void) {
    int age;
    char ch;

    // scanf 格式化读取整数
    printf("请输入年龄:");
    scanf("%d", &age);
    printf("你输入的年龄是:%d\n", age);

    // 读取多余换行符(因为 scanf 不读取 '\n')
    getchar(); // 吃掉回车

    // getchar 读取一个字符
    printf("请输入一个字符:");
    ch = getchar();
    printf("你输入的字符是:%c\n", ch);

    return 0;
}

总结对比表

功能 推荐函数 原因说明
格式化多类型输出 printf 输出格式灵活,功能强
输出单字符 putchar 简洁快速,适合循环输出
格式化输入 scanf 适合读取整型、浮点型、字符串等
单字符读取 getchar 精准读取每个字符,包括空格、换行等
读取控制台暂停 getchar 常用于输入后暂停(防止窗口自动关闭)
  • 想读取一整行字符串,推荐使用 fgets() 替代 scanf("%s"),防止缓冲区溢出;
  • 多次使用 scanf() 后紧跟 getchar() 是常用的“清理输入缓冲区”技巧。
posted @ 2025-05-18 22:28  ffff5  阅读(53)  评论(0)    收藏  举报