C语言数据类型、运算符、表达式以及其他小知识点

1. 数据类型概述

C语言的数据类型系统是其核心特性之一,它决定了如何存储数据以及可以对数据执行哪些操作。C语言的数据类型可以分为以下几大类:

2. 基本数据类型

2.1 整型 (Integer Types)

类型大小(通常)取值范围格式说明符
char1字节-128 到 127 或 0 到 255%c, %d
unsigned char1字节0 到 255%c, %u
short2字节-32,768 到 32,767%hd
unsigned short2字节0 到 65,535%hu
int4字节-2,147,483,648 到 2,147,483,647%d
unsigned int4字节0 到 4,294,967,295%u
long4或8字节取决于系统%ld
unsigned long4或8字节取决于系统%lu
long long8字节-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807%lld
unsigned long long8字节0 到 18,446,744,073,709,551,615%llu

示例代码:

#include <stdio.h>

int main() {
    int a = 10;
    unsigned int b = 20;
    long c = 100000L;
    
    printf("int: %d, unsigned int: %u, long: %ld\n", a, b, c);
    return 0;
}

运行结果:(以vs2022为例)

2.2 浮点型 (Floating-Point Types)

类型大小(通常)精度取值范围格式说明符
float4字节6-7位小数±3.4e±38%f
double8字节15-16位小数±1.7e±308%lf
long double10或16字节19-20位小数取决于系统%Lf

示例代码:

#include <stdio.h>

int main() {
    float f = 3.14f;
    double d = 3.1415926535;
    long double ld = 3.141592653589793238L;
    
    printf("float: %.2f, double: %.10lf, long double: %.15Lf\n", f, d, ld);
    return 0;
}

运行结果:(以vs2022为例)

2.3 字符型 (Character Type)

char类型用于存储单个字符,实际上存储的是ASCII码值。

示例代码:

#include <stdio.h>

int main() {
    char grade = 'A';
    printf("Grade: %c, ASCII value: %d\n", grade, grade);
    
    // 字符运算
    char next_grade = grade + 1;
    printf("Next grade: %c\n", next_grade);
    
    return 0;
}

2.4 布尔型 (Boolean Type)

C99标准引入了_Bool类型,需要包含stdbool.h头文件来使用bool、true和false。

示例代码:

#include <stdio.h>
#include <stdbool.h>

int main() {
    bool is_c_programming_fun = true;
    bool is_math_hard = false;
    
    printf("%d\n", is_c_programming_fun); // 输出 1
    printf("%d\n", is_math_hard);        // 输出 0
    
    return 0;
}

3. 派生数据类型

3.1 数组 (Arrays)

存储相同类型元素的集合。

示例代码:

#include <stdio.h>

int main() {
    // 一维数组
    int numbers[5] = {1, 2, 3, 4, 5};
    
    // 二维数组
    int matrix[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };
    
    // 访问数组元素
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    return 0;
}

运行结果:(以vs2022为例)

3.2 指针 (Pointers)

存储变量内存地址的变量。

示例代码:

#include <stdio.h>

int main() {
    int var = 20;
    int *ptr = &var;
    
    printf("变量值: %d\n", var);
    printf("指针地址: %p\n", ptr);
    printf("通过指针访问值: %d\n", *ptr);
    
    return 0;
}

3.3 结构体 (Structures)

允许存储不同类型的数据项。

示例代码:

#include <stdio.h>
#include <string.h>

// 定义结构体
struct Student {
    char name[50];
    int age;
    float gpa;
};

int main() {
    // 声明结构体变量
    struct Student student1;
    
    // 访问结构体成员
    strcpy(student1.name, "张三");
    student1.age = 20;
    student1.gpa = 3.8;
    
    printf("姓名: %s\n", student1.name);
    printf("年龄: %d\n", student1.age);
    printf("GPA: %.2f\n", student1.gpa);
    
    return 0;
}

3.4 联合体 (Unions)

类似结构体,但所有成员共享同一内存空间。

示例代码:

#include <stdio.h>

union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    union Data data;
    
    data.i = 10;
    printf("data.i: %d\n", data.i);
    
    data.f = 220.5;
    printf("data.f: %.2f\n", data.f);
    
    // 注意:此时data.i的值已被覆盖
    printf("data.i after assigning float: %d\n", data.i);
    
    return 0;
}

3.5 枚举 (Enums)

定义一组命名的整数常量。

示例代码:

#include <stdio.h>

enum Weekday {
    MONDAY,    // 0
    TUESDAY,   // 1
    WEDNESDAY, // 2
    THURSDAY,  // 3
    FRIDAY,    // 4
    SATURDAY,  // 5
    SUNDAY     // 6
};

int main() {
    enum Weekday today = WEDNESDAY;
    
    printf("Today is: %d\n", today);
    
    if (today == WEDNESDAY) {
        printf("It's Wednesday!\n");
    }
    
    return 0;
}

4. 类型限定符

4.1 const

定义不可修改的常量。

const int MAX_VALUE = 100;
// MAX_VALUE = 200; // 错误:不能修改const变量

4.2 volatile

告诉编译器变量可能在程序之外被修改。

volatile int hardware_register;

4.3 restrict

C99引入,用于指针,表示指针是访问数据的唯一方式。

void copy_array(int *restrict dest, const int *restrict src, size_t n);

5. 类型转换

5.1 隐式类型转换

编译器自动进行的类型转换。

int i = 10;
float f = i; // int自动转换为float

5.2 显式类型转换(强制类型转换)

程序员明确指定的类型转换。

float f = 3.14;
int i = (int)f; // float强制转换为int,i的值为3

6. 类型定义 (typedef)

为现有类型创建别名。

#include <stdio.h>

typedef unsigned int uint;
typedef struct {
    char name[50];
    int age;
} Person;

int main() {
    uint count = 10;
    Person p1 = {"李四", 25};
    
    printf("Count: %u\n", count);
    printf("Name: %s, Age: %d\n", p1.name, p1.age);
    
    return 0;
}

C语言运算符与表达式 完整学习指南

一、运算符分类总览

类别运算符说明

算术运算符

+,-,*,/,%

基本数学运算

关系运算符

==,!=,<,>,<=,>=

比较大小,返回布尔值(0/1)

逻辑运算符

&&, `

位运算符

&, `

,^,~`,<<,>>

赋值运算符

=,+=,-=,*=,/=,%=

将值赋给变量

条件运算符

? :

三元条件表达式

逗号运算符

,

顺序执行多个表达式

自增/自减运算符

++,--

前置与后置形式

指针与地址运算符

*,&

解引用与取地址

sizeof 运算符

sizeof

获取类型或变量所占字节数

下标运算符

[]

数组访问

成员访问运算符

.,->

结构体/联合体成员访问

函数调用运算符

()

调用函数

⚠️ 注意:C语言没有布尔类型(C99前),逻辑结果用 0(假)和 非0(真)表示。

二、算术运算符(Arithmetic Operators)

运算符含义示例说明

+

加法

a + b

可用于数值和指针(指针偏移)

-

减法

a - b

可用于数值、指针差值

*

乘法

a * b

也可用于指针解引用

/

除法

a / b

整数除法截断小数,浮点保留

%

取模(余数)

a % b

仅限整型,不能用于浮点

1. 整数除法

int a = 7, b = 3;
printf("%d\n", a / b);   // 输出 2(不是2.333)
printf("%d\n", a % b);   // 输出 1

2. 取模运算符 %

  • 被除数为负时,结果符号取决于编译器(C99标准规定:结果符号与被除数一致

(-7) % 3  → -1  (C99标准)
7 % (-3)  → 1
(-7) % (-3) → -1

3. 指针算术

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;       // p 指向 arr[0]
p++;                // p 指向 arr[1],地址 + sizeof(int)
printf("%d\n", *(p + 2)); // 输出 30

✅ 指针 + n 表示移动 n 个元素,不是 n 字节!

三、关系运算符(Relational Operators)

运算符含义返回值

==

等于

1(真)或 0(假)

!=

不等于

——

<

小于

——

>

大于

——

<=

小于等于

——

>=

大于等于

——

✅ 示例:

int x = 5, y = 10;
printf("%d\n", x < y);   // 1
printf("%d\n", x == y);  // 0

⚠️ 重要陷阱:不要混淆 ===

if (x = 5) { ... }     // ❌ 错误!赋值,结果为5(真),永远执行
if (x == 5) { ... }    // ✅ 正确!比较

💡 编译器警告:有些编译器会提示“赋值在条件中”,建议开启 -Wall

四、逻辑运算符(Logical Operators)

运算符含义结合性说明

&&

逻辑与

左结合

短路求值:左为假则不计算右

`

`

逻辑或

!

逻辑非

右结合

单目,取反

✅ 短路求值(Short-Circuit Evaluation)——极其重要!

int a = 0, b = 5;

if (a != 0 && b / a > 1) { ... }  // ✅ 安全!不会除零
if (b > 0 || func()) { ... }      // ✅ 如果b>0,func()不会被调用

📌 应用场景:

  • 防止空指针访问
  • 避免昂贵函数调用
  • 条件链式判断

char *ptr = get_string();
if (ptr != NULL && strlen(ptr) > 5) {
    printf("Long string: %s\n", ptr);
}

⚠️ &&|| 只能用于布尔上下文,但C中任何非0值都为真。

五、位运算符(Bitwise Operators)

直接对整数的二进制位进行操作,效率极高,常用于嵌入式、网络协议、标志位等。

运算符名称说明示例(8位)

&

按位与

两数同为1才为1

5 & 3 = 0101 & 0011 = 0001 = 1

`

`

按位或

任一为1即为1

^

按位异或

相同为0,不同为1

5 ^ 3 = 0101 ^ 0011 = 0110 = 6

~

按位取反

所有位翻转

~5 = ~0101 = 11111010(补码)

<<

左移

左边丢弃,右边补0

5 << 2 = 0101 << 2 = 010100 = 20

>>

右移

右边丢弃,左边补符号位(有符号)或0(无符号)

20 >> 2 = 010100 >> 2 = 000101 = 5

✅ 实际应用案例:

1. 设置/清除/切换特定位

#define FLAG_A (1 << 0)   // 0001
#define FLAG_B (1 << 1)   // 0010
#define FLAG_C (1 << 2)   // 0100

unsigned int flags = 0;

flags |= FLAG_A;         // 设置A位 → 0001
flags |= FLAG_B;         // 设置B位 → 0011
flags &= ~FLAG_A;        // 清除A位 → 0010
flags ^= FLAG_B;         // 切换B位 → 0000

2. 快速乘除(仅适用于2的幂)

x * 8  → x << 3
x / 8  → x >> 3   (仅当x为正整数时成立)

3. 交换两个数(不用临时变量)

int a = 5, b = 10;
a ^= b;
b ^= a;
a ^= b;
// 现在 a=10, b=5

💡 异或交换只适用于整数,且不推荐用于生产代码(可读性差,现代编译器优化更好)

⚠️ 右移的符号扩展问题

char c = -1;     // 二进制:11111111(补码)
int i = c;       // 扩展为:11111111111111111111111111111111
i >>= 1;         // 右移一位:11111111111111111111111111111111 → 还是 -1

如果是无符号:

unsigned char uc = 255;  // 11111111
unsigned int ui = uc;
ui >>= 1;               // 01111111111111111111111111111111 → 2147483647

✅ 推荐:使用 unsigned 类型进行位操作,避免未定义行为。

六、赋值运算符(Assignment Operators)

运算符含义示例等价于

=

简单赋值

a = 5

+=

加后赋值

a += 3

a = a + 3

-=

减后赋值

a -= 2

a = a - 2

*=

乘后赋值

a *= 4

a = a * 4

/=

除后赋值

a /= 2

a = a / 2

%=

取模后赋值

a %= 3

a = a % 3

&=

按位与后赋值

a &= b

a = a & b

`

=`

按位或后赋值

`a

^=

按位异或后赋值

a ^= b

a = a ^ b

<<=

左移后赋值

a <<= 2

a = a << 2

>>=

右移后赋值

a >>= 1

a = a >> 1

七、逗号运算符(Comma Operator)

最易被忽视,但非常有用!

语法:expr1, expr2, ..., exprN

  • 按顺序求值
  • 整个表达式的值是最后一个子表达式的值
  • 优先级最低

int a, b, c;
a = (b = 3, c = 5, b + c);  // a = 8,先执行 b=3, c=5,最后 b+c=8

for (i = 0, j = 10; i < j; i++, j--) {
    // 初始化和更新部分使用逗号
}

九、自增与自减运算符(Increment/Decrement)

形式名称含义

++a,--a

前置

先自增/减,再使用值

a++,a--

后置

先使用值,再自增/减

int a = 5;
int b = ++a;  // a=6, b=6
int c = a++;  // c=6, a=7

printf("a=%d, b=%d, c=%d\n", a, b, c); // 输出:a=7, b=6, c=6

关键区别:

表达式计算过程返回值

++x

x = x+1 → 返回新值

新值

x++

返回旧值 → x = x+1

旧值

陷阱:连续使用导致未定义行为

int x = 5;
int y = x++ + x++;   // ❌ 未定义行为!x 被修改两次
int z = ++x + ++x;   // ❌ 同上

十、指针与地址运算符

运算符名称说明

&

取地址

&var→ 返回 var 的内存地址

*

解引用

*ptr→ 访问 ptr 所指向的值

int x = 100;
int *p = &x;      // p 存储 x 的地址
*p = 200;         // 修改 x 的值 → x 现在是 200
printf("%d\n", x); // 输出 200

十一、sizeof 运算符

获取类型或变量在当前平台上的字节大小

语法:

sizeof(type)
sizeof(expression)

示例:

printf("sizeof(int): %zu\n", sizeof(int));           // 4
printf("sizeof(char): %zu\n", sizeof(char));         // 1
printf("sizeof(double): %zu\n", sizeof(double));     // 8
printf("sizeof(arr): %zu\n", sizeof(arr));           // 数组总字节数

特殊情况:数组 vs 指针

int arr[10];
int *p = arr;

printf("%zu\n", sizeof(arr));   // 40(10 * sizeof(int))
printf("%zu\n", sizeof(p));     // 8(指针大小,与平台有关)

特殊情况:数组 vs 指针

int arr[10];
int *p = arr;

printf("%zu\n", sizeof(arr));   // 40(10 * sizeof(int))
printf("%zu\n", sizeof(p));     // 8(指针大小,与平台有关)

十二、下标运算符(Array Subscript)

语法:array[index]

本质是指针算术

arr[i]  ↔  *(arr + i)

示例:

int a[3] = {10, 20, 30};
printf("%d\n", a[1]);     // 20
printf("%d\n", 1[a]);     // 20!完全合法(因为 a[1] = *(a+1) = *(1+a))

十三、成员访问运算符

运算符用途示例

.

结构体/联合体变量访问成员

s.name

->

指向结构体/联合体的指针访问成员

p->name

示例:

struct Point {
    int x, y;
};

struct Point pt = {10, 20};
struct Point *p = &pt;

printf("%d, %d\n", pt.x, pt.y);      // 使用 .
printf("%d, %d\n", p->x, p->y);      // 使用 ->

十四、函数调用运算符

int result = add(3, 5);

  • () 是函数调用运算符
  • 参数列表可为空:func()
  • 函数名本质上是函数指针

函数指针调用:

int (*fp)(int, int) = add;
int r = fp(3, 5);   // 等价于 add(3,5)

十五、运算符优先级与结合性(最重要!)

优先级决定谁先运算,结合性决定相同优先级时从左还是右开始。

完整优先级表(从高到低):

优先级运算符结合性说明

1

()[].->

左到右

函数调用、下标、成员访问

2

++--+-!~*&sizeof

右到左

单目运算符

3

*/%

左到右

乘除取模

4

+-

左到右

加减

5

<<>>

左到右

位移

6

<<=>>=

左到右

关系运算

7

==!=

左到右

相等

8

&

左到右

按位与

9

^

左到右

按位异或

10

`

`

左到右

11

&&

左到右

逻辑与

12

`

`

13

? :

右到左

条件

14

=+=-=*=/=%=&=`

=^=<<=>>=`

右到左

15

,

左到右

逗号

以上对C语言的数据类型、运算符、表达式相关的知识点可能会有遗漏与疏忽,望家人们理解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值