强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

C/C++ Linux 开发教程(GCC + CMake) / 02 — 基本类型、运算符与表达式

基本类型、运算符与表达式

1. 基本数据类型

C 语言提供了一组基本数据类型,用于存储不同范围和精度的数值。

1.1 整型家族

类型关键字典型大小范围(典型)
短整型short2 字节-32,768 ~ 32,767
整型int4 字节-2,147,483,648 ~ 2,147,483,647
长整型long4 或 8 字节取决于平台
长长整型long long8 字节-9.2×10¹⁸ ~ 9.2×10¹⁸

1.2 浮点型家族

类型关键字典型大小精度
单精度float4 字节约 6~7 位有效数字
双精度double8 字节约 15~16 位有效数字
扩展精度long double12 或 16 字节平台相关

1.3 字符型

类型关键字典型大小说明
字符char1 字节存储 ASCII 字符或小整数
宽字符wchar_t2 或 4 字节存储 Unicode 字符

💡 提示: char 本质上是整数类型,'A' 的值就是 65。

1.4 完整示例

#include <stdio.h>

int main(void)
{
    char c = 'A';
    short s = 100;
    int i = 42;
    long l = 100000L;
    long long ll = 9876543210LL;

    float f = 3.14f;
    double d = 3.141592653589793;
    long double ld = 3.141592653589793238L;

    printf("char:        '%c' = %d, size = %zu\n", c, c, sizeof(char));
    printf("short:       %hd,       size = %zu\n", s, sizeof(short));
    printf("int:         %d,        size = %zu\n", i, sizeof(int));
    printf("long:        %ldL,      size = %zu\n", l, sizeof(long));
    printf("long long:   %lldLL,    size = %zu\n", ll, sizeof(long long));
    printf("float:       %f,        size = %zu\n", f, sizeof(float));
    printf("double:      %lf,       size = %zu\n", d, sizeof(double));
    printf("long double: %LfL,      size = %zu\n", ld, sizeof(long double));

    return 0;
}

编译运行:

gcc -Wall -std=c17 -o types types.c && ./types

2. sizeof 运算符

sizeof 返回类型或变量占用的字节数,是编译期常量

#include <stdio.h>

int main(void)
{
    printf("sizeof(char)        = %zu\n", sizeof(char));
    printf("sizeof(int)         = %zu\n", sizeof(int));
    printf("sizeof(long)        = %zu\n", sizeof(long));
    printf("sizeof(long long)   = %zu\n", sizeof(long long));
    printf("sizeof(float)       = %zu\n", sizeof(float));
    printf("sizeof(double)      = %zu\n", sizeof(double));
    printf("sizeof(void *)      = %zu\n", sizeof(void *));

    int arr[10];
    printf("sizeof(arr)         = %zu\n", sizeof(arr));        // 10 * sizeof(int)
    printf("sizeof(arr)/sizeof(arr[0]) = %zu\n", sizeof(arr) / sizeof(arr[0])); // 元素个数

    return 0;
}

⚠️ 注意: 数组传入函数后会退化为指针,sizeof 返回的是指针大小而非数组大小。

表达式32 位平台64 位平台
sizeof(int)44
sizeof(long)48 (Linux), 4 (Windows)
sizeof(void *)48
sizeof(size_t)48

3. 有符号与无符号

#include <stdio.h>

int main(void)
{
    signed int a = -10;      // 有符号,可为负
    unsigned int b = 10u;    // 无符号,仅非负

    printf("signed:   %d\n", a);
    printf("unsigned: %u\n", b);

    // 无符号与有符号混用的陷阱
    if (a < b) {
        printf("a < b\n");   // 实际上 -10 > 10(无符号比较)!
    } else {
        printf("a >= b\n");  // 这行会被执行
    }

    return 0;
}

⚠️ 注意: 当 signedunsigned 混合运算时,signed 会被隐式转换为 unsigned-10 转为无符号后变成一个极大的正数(4294967286),因此 -10 < 10 的比较结果为假。

💡 提示: 使用 -Wall -Wextra 编译时,GCC 会对有符号/无符号混用发出警告。


4. 类型转换

4.1 隐式转换

编译器自动完成的类型转换,遵循"小类型向大类型提升"规则:

char → short → int → long → long long → float → double → long double
int a = 5;
double b = 2.5;
double result = a + b;  // int 隐式提升为 double

4.2 显式转换 (Casting)

#include <stdio.h>

int main(void)
{
    double pi = 3.14159;
    int truncated = (int)pi;      // 截断小数部分,结果为 3

    printf("pi = %f\n", pi);
    printf("(int)pi = %d\n", truncated);

    // 整数除法的陷阱
    int x = 7, y = 2;
    printf("7 / 2 = %d\n", x / y);             // 3(整数除法)
    printf("7 / 2 = %f\n", (double)x / y);     // 3.500000

    return 0;
}

⚠️ 注意: (int)3.9 的结果是 3,不是四舍五入。需要四舍五入请使用 round() 函数。


5. 运算符

5.1 算术运算符

运算符说明示例结果
+加法5 + 38
-减法5 - 32
*乘法5 * 315
/除法7 / 23(整数)
%取模7 % 21
++自增i++ / ++i
--自减i-- / --i
#include <stdio.h>

int main(void)
{
    int i = 5;
    printf("i++ = %d, i = %d\n", i++, i);  // 先用后加: 5, 6
    i = 5;
    printf("++i = %d, i = %d\n", ++i, i);  // 先加后用: 6, 6
    return 0;
}

💡 提示: 尽量避免在复杂表达式中使用 ++/--,单独使用最安全。

5.2 关系运算符

运算符含义示例
==等于a == b
!=不等于a != b
<小于a < b
>大于a > b
<=小于等于a <= b
>=大于等于a >= b

⚠️ 注意: = 是赋值,== 是比较。if (a = 5) 永远为真!使用 -Wall 时 GCC 会对此发出警告。

5.3 逻辑运算符

运算符含义示例短路行为
&&逻辑与a && b左边为假,右边不求值
||逻辑或a || b左边为真,右边不求值
!逻辑非!a
#include <stdio.h>

int main(void)
{
    int x = 0;

    // 短路求值:x != 0 为假,10/x 不会被执行
    if (x != 0 && 10 / x > 2) {
        printf("条件成立\n");
    } else {
        printf("x 为零或条件不成立,安全!\n");
    }

    return 0;
}

💡 提示: 利用短路求值可以安全地检查空指针:if (ptr != NULL && ptr->value > 0)

5.4 位运算符

运算符含义示例说明
&按位与0xFF & 0x0F0x0F两个位都为 1 时结果为 1
|按位或0xF0 | 0x0F0xFF任一位为 1 时结果为 1
^按位异或0xFF ^ 0x0F0xF0两位不同则为 1
~按位取反~0x000xFF所有位反转
<<左移1 << 38左边丢弃,右边补 0
>>右移8 >> 22右边丢弃,左边补符号位
#include <stdio.h>

void print_binary(unsigned char val)
{
    for (int i = 7; i >= 0; i--) {
        printf("%d", (val >> i) & 1);
    }
}

int main(void)
{
    unsigned char a = 0xA5;  // 10100101
    unsigned char b = 0x0F;  // 00001111

    printf("a     = "); print_binary(a); printf("\n");
    printf("b     = "); print_binary(b); printf("\n");
    printf("a & b = "); print_binary(a & b); printf("\n");
    printf("a | b = "); print_binary(a | b); printf("\n");
    printf("a ^ b = "); print_binary(a ^ b); printf("\n");

    return 0;
}

实际场景:位运算常用于标志位(flags)管理:

#define FLAG_READ    (1 << 0)  // 0001
#define FLAG_WRITE   (1 << 1)  // 0010
#define FLAG_EXEC    (1 << 2)  // 0100

unsigned int perms = FLAG_READ | FLAG_WRITE;      // 设置读写权限
perms |= FLAG_EXEC;                                // 添加执行权限
perms &= ~FLAG_WRITE;                             // 移除写权限

if (perms & FLAG_READ) {
    printf("可读\n");
}

6. 运算符优先级表

优先级运算符结合性
1(最高)() [] -> .左到右
2! ~ ++ -- (type) * & sizeof右到左
3* / %左到右
4+ -左到右
5<< >>左到右
6< <= > >=左到右
7== !=左到右
8&左到右
9^左到右
10|左到右
11&&左到右
12||左到右
13?:右到左
14(最低)= += -=右到左

💡 提示: 记不住优先级?用括号 () 明确意图,既避免错误又提高可读性。


7. 字面量

// 整型字面量
int decimal = 42;          // 十进制
int octal = 052;           // 八进制(前缀 0)
int hex = 0x2A;            // 十六进制(前缀 0x)
int binary = 0b101010;     // 二进制(C23,GCC 也支持)
long big = 1000000L;       // long 后缀
unsigned long long ull = 18446744073709551615ULL;

// 浮点字面量
float f = 3.14f;           // float 后缀
double d = 3.14;           // 默认 double
long double ld = 3.14L;    // long double 后缀
double sci = 1.5e10;       // 科学计数法

// 字符字面量
char c = 'A';
char newline = '\n';       // 转义字符
char hex_char = '\x41';    // 十六进制 ASCII

// 字符串字面量
const char *str = "Hello, World!";

// 转义字符表
// \n 换行  \t 制表符  \\ 反斜杠  \' 单引号  \" 双引号
// \0 空字符  \r 回车  \a 响铃  \b 退格

8. const 常量

#include <stdio.h>

#define MAX_SIZE 100        // 宏常量(预处理期替换)
const int MIN_SIZE = 10;   // const 常量(编译期检查)

int main(void)
{
    const double PI = 3.14159265358979;
    // PI = 3.14;           // 错误!const 变量不可修改

    const int *p1 = &MIN_SIZE;    // 指向 const int 的指针
    int *const p2 = NULL;          // const 指针,指向 int
    const int *const p3 = &MIN_SIZE; // const 指针,指向 const int

    printf("MAX_SIZE = %d\n", MAX_SIZE);
    printf("MIN_SIZE = %d\n", MIN_SIZE);
    printf("PI = %f\n", PI);

    return 0;
}
声明含义
const int *p指针指向的值不可修改 (*p 不可赋值)
int *const p指针本身不可修改 (p 不可重新指向)
const int *const p两者都不可修改

9. typedef 类型别名

#include <stdio.h>
#include <stdint.h>

typedef unsigned int uint;
typedef struct Point {
    double x;
    double y;
} Point;

typedef int (*MathFunc)(int, int);  // 函数指针类型别名

int add(int a, int b) { return a + b; }

int main(void)
{
    uint count = 42;
    Point p = {1.0, 2.0};
    MathFunc op = add;

    printf("count = %u\n", count);
    printf("point = (%.1f, %.1f)\n", p.x, p.y);
    printf("add(3, 5) = %d\n", op(3, 5));

    // 使用 stdint.h 中的定宽整型
    int32_t val = -100;
    uint32_t uval = 100;
    printf("int32_t: %d, uint32_t: %u\n", val, uval);

    return 0;
}

💡 提示: 使用 <stdint.h> 中的 int32_tuint64_t 等定宽整型,可以避免 int/long 大小因平台而异的问题。


10. ASCII 与 Unicode

ASCII 码表(常用部分)

字符十进制十六进制说明
'0'~'9'48~570x30~0x39数字字符
'A'~'Z'65~900x41~0x5A大写字母
'a'~'z'97~1220x61~0x7A小写字母
' '320x20空格
'\n'100x0A换行
'\0'00x00空字符(字符串终止符)
#include <stdio.h>

int main(void)
{
    // 大小写转换
    char lower = 'a';
    char upper = lower - 32;   // 'A'
    printf("%c -> %c\n", lower, upper);

    // 字符与数字互转
    char digit_char = '7';
    int digit = digit_char - '0';  // 7
    printf("'%c' -> %d\n", digit_char, digit);

    // 打印 ASCII 表
    for (int i = 32; i < 127; i++) {
        printf("%3d: '%c'  ", i, i);
        if ((i - 31) % 8 == 0) printf("\n");
    }

    return 0;
}

11. 常见陷阱

陷阱示例说明
整数除法截断7 / 23需要浮点结果时用 (double)7 / 2
整数溢出INT_MAX + 1有符号整数溢出是未定义行为(UB)
浮点精度0.1 + 0.2 != 0.3浮点数无法精确表示所有小数
字符与整数混淆char c = 256超出范围会截断
sizeof 与数组退化函数内 sizeof(arr)返回指针大小而非数组大小
#include <stdio.h>
#include <limits.h>

int main(void)
{
    // 浮点精度问题
    double a = 0.1 + 0.2;
    printf("0.1 + 0.2 = %.20f\n", a);    // 不是精确的 0.3
    printf("0.1 + 0.2 == 0.3? %s\n", a == 0.3 ? "yes" : "no");

    // 整数溢出
    int max = INT_MAX;
    printf("INT_MAX = %d\n", max);
    // printf("%d\n", max + 1);  // 未定义行为!

    return 0;
}

12. 实际场景:温度转换程序

#include <stdio.h>

int main(void)
{
    double celsius, fahrenheit;

    printf("请输入摄氏温度: ");
    if (scanf("%lf", &celsius) != 1) {
        fprintf(stderr, "输入错误!\n");
        return 1;
    }

    fahrenheit = celsius * 9.0 / 5.0 + 32.0;

    printf("%.1f°C = %.1f°F\n", celsius, fahrenheit);

    return 0;
}

编译运行:

gcc -Wall -std=c17 -o temp temp.c && echo "36.5" | ./temp

输出:

请输入摄氏温度: 36.5°C = 97.7°F

13. 扩展阅读