当前位置: 首页 > article >正文

【C语言入门】解锁核心关键字的终极奥秘与实战应用(二)

目录

一、sizeof

1.1. 作用

2.2. 代码示例

二、const

2.1. 作用

2.2. 代码示例

三、signed 和 unsigned 

3.1. 作用

3.2. 代码示例

四、struct、union、enum

4.1. struct(结构体)

4.1.1. 作用

4.1.2. 代码示例

4.2. union(联合体)

4.2.1. 作用

4.2.2. 代码示例

4.3. enum(枚举类型)

4.3.1. 作用

4.3.2. 代码示例

五、typedef

5.1. 作用

5.2. 代码示例


接着上一篇【C语言入门】解锁核心关键字的终极奥秘与实战应用(一)-CSDN博客继续分析核心关键字。

一、sizeof

1.1. 作用

  • 数据类型sizeof 可以直接作用于基本数据类型(如 intcharfloatdouble 等)以及用户定义的数据类型(如结构体、联合体等)。

  • 变量sizeof 也可以作用于变量,此时它返回的是该变量类型所占的内存大小,而不是变量的值。

  • 编译时计算sizeof 的计算是在编译时进行的,而不是在运行时。意味着它不会增加程序的运行时间开销。

  • 括号:在使用 sizeof 时,通常建议将其操作数放在括号中,以避免潜在的解析歧义。例如,sizeof(int) 而不是 sizeof int

  • 指针:当 sizeof 作用于指针时,它返回的是指针类型本身所占的内存大小,而不是指针所指向的数据的大小。

2.2. 代码示例

示例1:基本数据类型

#include <stdio.h>  
  
int main() {  
    printf("Size of int: %zu bytes\n", sizeof(int));  
    printf("Size of char: %zu bytes\n", sizeof(char));  
    printf("Size of float: %zu bytes\n", sizeof(float));  
    printf("Size of double: %zu bytes\n", sizeof(double));  
    return 0;  
}
  • 运行结果:

sizeof 被用来计算基本数据类型 intcharfloat, 和 double 的大小,并将结果打印出来。

示例2:变量 

#include <stdio.h>  
  
int main() {  
    int a = 10;  
    char b = 'c';  
    printf("Size of variable a (int): %zu bytes\n", sizeof(a));  
    printf("Size of variable b (char): %zu bytes\n", sizeof(b));  
    // 或者直接使用变量类型  
    printf("Size of type of variable a: %zu bytes\n", sizeof(int));  
    printf("Size of type of variable b: %zu bytes\n", sizeof(char));  
    return 0;  
}
  • 运行结果:

sizeof 被用来计算变量 a 和 b 的大小,分别是 int 类型和 char 类型。注意,sizeof(a) 和 sizeof(int) 返回的是相同的结果。

示例3:指针 

#include <stdio.h>  
  
int main() {  
    int *ptr = NULL;  
    printf("Size of pointer: %zu bytes\n", sizeof(ptr));  
    // 注意:sizeof(*ptr) 将返回 ptr 所指向的 int 类型的大小  
    printf("Size of type pointed to by ptr: %zu bytes\n", sizeof(*ptr));  
    return 0;  
}
  • 运行结果: 

 

sizeof(ptr) 返回的是指针 ptr 本身所占的内存大小,而 sizeof(*ptr) 返回的是 ptr 所指向的 int 类型的大小。

示例4:结构体 

#include <stdio.h>  
  
struct MyStruct {  
    int a;  
    char b;  
    double c;  
};  
  
int main() {  
    struct MyStruct s;  
    printf("Size of struct MyStruct: %zu bytes\n", sizeof(struct MyStruct));  
    printf("Size of variable s of type struct MyStruct: %zu bytes\n", sizeof(s));  
    return 0;  
}
  • 运行结果:  

sizeof 被用来计算结构体 MyStruct 的大小。注意,结构体的大小可能会因为内存对齐(padding)而大于其成员大小的总和。

二、const

const 关键字在C/C++等编程语言中用于定义常量,即其值在初始化后不能被修改的变量。使用 const 可以提高代码的可读性和安全性,因为它明确了哪些变量是不应该被修改的。

2.1. 作用

1. 定义常量const 修饰的变量必须在声明时初始化,之后其值就不能被改变了。

2. 类型安全:通过 const,编译器可以在编译时检查对常量的非法修改,从而提高代码的类型安全性。

3. 作用域const 常量的作用域取决于其声明位置。在函数内部声明的 const 常量具有局部作用域,而在文件范围或全局范围内声明的 const 常量则具有相应的全局作用域。

4. 指针与 const

  • const 指针:指向常量的指针,不能通过该指针修改所指向的值。
  • 指向 const 的指针:指针本身是常量,不能改变其指向的地址,但可以修改所指向的值(如果所指向的不是 const)。
  • 指向 const 的 const 指针:既不能改变指针的指向,也不能通过指针修改所指向的值。

5. 与 #define 的区别const 定义的常量有类型,可以进行类型检查;而 #define 定义的常量是简单的文本替换,没有类型信息。

2.2. 代码示例

示例1:基本常量

#include <stdio.h>  
  
int main() {  
    const int MAX_VALUE = 100; // 定义常量 MAX_VALUE  
    printf("MAX_VALUE: %d\n", MAX_VALUE);  
    // MAX_VALUE = 200; // 这将导致编译错误,因为 MAX_VALUE 是常量  
    return 0;  
}

运行结果:  

 

示例2:const 指针

#include <stdio.h>  
  
int main() {  
    const int a = 5;  
    int b = 10;  
    const int *ptr1 = &a; // ptr1 指向常量 a,不能通过 ptr1 修改 a 的值  
    int *ptr2 = &b;       // ptr2 指向变量 b,可以通过 ptr2 修改 b 的值  
    // *ptr1 = 20;        // 这将导致编译错误,因为 ptr1 指向的是常量  
    *ptr2 = 20;           // 这将修改 b 的值为 20  
    printf("a: %d, b: %d\n", a, b);  
    return 0;  
}

运行结果: 

示例3:指向 const 的指针

#include <stdio.h>  
  
int main() {  
    int a = 5;  
    const int *ptr = &a; // ptr 指向变量 a,但 ptr 被声明为指向 const,因此不能通过 ptr 修改 a 的值  
    // ptr = &b;         // 假设 int b; 已声明,这将是合法的,但前提是 ptr 没有被声明为指向 const 的 const 指针  
    // *ptr = 10;        // 这将导致编译错误,因为 ptr 指向的值被视为常量  
    printf("a: %d\n", a);  
    return 0;  
}

运行结果:  

 

示例4:指向 const 的 const 指针

#include <stdio.h>  
  
int main() {  
    const int a = 5;  
    const int *const ptr = &a; // ptr 是指向 const 的 const 指针,既不能改变 ptr 的指向,也不能通过 ptr 修改所指向的值  
    // ptr = &b;               // 这将导致编译错误,因为 ptr 是指向 const 的 const 指针  
    // *ptr = 10;             // 这也将导致编译错误,因为 ptr 指向的值被视为常量  
    printf("a: %d\n", a);  
    return 0;  
}

运行结果:   

通过 const 关键字,我们可以定义在程序执行期间其值不应改变的变量,从而提高代码的可读性和健壮性。

三、signed 和 unsigned 

在C/C++等编程语言中,signed 和 unsigned 关键字用于定义整数类型的符号性。signed 表示有符号数,可以表示正数、负数和零;而 unsigned 表示无符号数,只能表示非负数(即零和正数)。

3.1. 作用

1. 有符号数(signed)

  • 默认情况下,整数类型(如 intshortlong)都是有符号的。
  • 有符号数使用最高位作为符号位,0 表示正数,1 表示负数。
  • 有符号数的取值范围包括负数、零和正数。

2. 无符号数(unsigned)

  • 无符号数不使用符号位,因此可以表示更大的正数范围。
  • 无符号数的取值范围从0开始,一直到该类型能表示的最大正数。
  • 在声明变量时,可以使用 unsigned 关键字来指定无符号类型,如 unsigned intunsigned shortunsigned long 等。

3. 类型转换

  • 当有符号数和无符号数进行运算时,有符号数可能会被隐式转换为无符号数,可能会导致意外的结果。
  • 为了避免这种情况,应该显式地进行类型转换,确保运算的正确性。

4. 溢出

  • 当整数超出其类型的取值范围时,会发生溢出。
  • 对于有符号数,溢出可能导致结果变为负数或另一个正数。
  • 对于无符号数,溢出会导致结果从最大值回绕到0。

3.2. 代码示例

示例1:基本的有符号和无符号整数


#include <stdio.h>  
  
int main() {  
    signed int a = -10;       // 有符号整数,值为-10  
    unsigned int b = 20;      // 无符号整数,值为20  
  
    printf("Signed int a: %d\n", a);  
    printf("Unsigned int b: %u\n", b);  
  
    // 有符号和无符号整数相加(注意可能的溢出和类型转换)  
    int sum_signed = a + b;   // 结果为10(有符号运算)  
    unsigned int sum_unsigned = a + b; // 结果取决于系统,但通常为一个大正数(无符号运算)  
  
    printf("Sum (signed): %d\n", sum_signed);  
    printf("Sum (unsigned): %u\n", sum_unsigned);  
  
    return 0;  
}

运行结果:

示例2:类型转换和溢出

#include <stdio.h>  
#include <limits.h>  
  
int main() {  
    unsigned int u_max = UINT_MAX; // 无符号整数的最大值  
    int s_max = INT_MAX;           // 有符号整数的最大值  
  
    printf("Unsigned int max: %u\n", u_max);  
    printf("Signed int max: %d\n", s_max);  
  
    // 溢出示例  
    unsigned int u_overflow = u_max + 1; // 结果为0(无符号溢出)  
    int s_overflow = s_max + 1;          // 结果为INT_MIN(有符号溢出)  
  
    printf("Unsigned overflow: %u\n", u_overflow);  
    printf("Signed overflow: %d\n", s_overflow);  
  
    // 类型转换示例  
    unsigned int u = 10;  
    int s = -5;  
  
    // 当有符号数和无符号数进行运算时,有符号数可能会被隐式转换为无符号数  
    unsigned int result = u + s; // 结果可能不是预期的5,而是一个大正数  
  
    printf("Result of u + s (unsigned): %u\n", result);  
  
    // 为了避免这种情况,应该显式地进行类型转换  
    unsigned int result_correct = u + (unsigned int)s; // 仍然可能不是5(因为s是负数),但避免了隐式转换的陷阱  
    int result_signed = (int)u + s; // 正确的结果为5(因为先将u转换为有符号数,再进行运算)  
  
    printf("Corrected result (unsigned to signed): %u\n", result_correct);  
    printf("Corrected result (signed): %d\n", result_signed);  
  
    return 0;  
}

 运行结果:

在进行有符号和无符号整数的运算时,应该特别小心类型转换和溢出的问题,以避免意外的结果。在实际编程中,应该根据具体的需求选择合适的整数类型,并确保运算的正确性。

四、struct、union、enum

在C/C++等编程语言中,structunion 和 enum 是用于定义复合数据类型的关键字。它们允许程序员将多个不同类型的数据组合在一起,或者定义一组命名的整型常量。

4.1. struct(结构体)

struct 关键字用于定义结构体,它是一种用户自定义的数据类型,可以包含多个不同类型的数据成员。结构体通常用于表示具有多个属性的实体,如人、车等。

4.1.1. 作用

  • 结构体定义使用 struct 关键字,后跟结构体标签(可选)和大括号内的成员列表。
  • 结构体成员可以是任何有效的数据类型,包括基本数据类型、指针、数组、甚至其他结构体。
  • 结构体变量可以通过点运算符(.)访问其成员。
  • 结构体可以嵌套定义,即一个结构体成员可以是另一个结构体类型。

4.1.2. 代码示例

#include <stdio.h>  
  
// 定义一个结构体类型 Person  
struct Person {  
    char name[50];  
    int age;  
    float height;  
};  
  
int main() {  
    // 创建一个结构体变量  
    struct Person person1;  
  
    // 给结构体成员赋值  
    snprintf(person1.name, sizeof(person1.name), "Alice");  
    person1.age = 30;  
    person1.height = 5.5;  
  
    // 打印结构体成员的值  
    printf("Name: %s\n", person1.name);  
    printf("Age: %d\n", person1.age);  
    printf("Height: %.1f\n", person1.height);  
  
    return 0;  
}
  • 运行结果:

4.2. union(联合体)

union 关键字用于定义联合体,它是一种特殊的数据结构,允许在相同的内存位置存储不同的数据类型。联合体的大小等于其最大成员的大小,且所有成员共享同一块内存。

4.2.1. 作用

  • 联合体定义使用 union 关键字,后跟联合体标签(可选)和大括号内的成员列表。
  • 联合体成员可以是任何有效的数据类型。
  • 联合体变量通过点运算符(.)访问其成员,但一次只能存储一个成员的值,因为所有成员共享内存。
  • 联合体通常用于节省内存或实现多态。

4.2.2. 代码示例

#include <stdio.h>  
  
// 定义一个联合体类型 Data  
union Data {  
    int i;  
    float f;  
    char str[20];  
};  
  
int main() {  
    // 创建一个联合体变量  
    union Data data;  
  
    // 给联合体成员赋值(注意:同时只能有一个成员有效)  
    data.i = 100;  
    printf("Integer: %d\n", data.i);  
  
    data.f = 3.14;  
    printf("Float: %.2f\n", data.f);  
  
    snprintf(data.str, sizeof(data.str), "Hello");  
    printf("String: %s\n", data.str);  
  
    // 注意:同时访问多个成员可能会导致未定义行为  
    // 例如:printf("Integer after string: %d\n", data.i); // 未定义行为  
  
    return 0;  
}
  • 运行结果:

4.3. enum(枚举类型)

enum 关键字用于定义枚举类型,它是一种用户定义的类型,由一组命名的整型常量组成。枚举类型使得代码更加清晰易读,并限制了变量的取值范围。

4.3.1. 作用

  • 枚举定义使用 enum 关键字,后跟枚举标签(必须)和大括号内的枚举成员列表。
  • 枚举成员可以是任何有效的标识符,它们自动被赋予一个整型值,从0开始递增(除非显式指定)。
  • 枚举变量可以通过赋值或使用枚举成员来初始化。
  • 枚举类型通常用于表示一组相关的常量,如颜色、状态等。

4.3.2. 代码示例

#include <stdio.h>  
  
// 定义一个枚举类型 Color  
enum Color {  
    RED,  
    GREEN,  
    BLUE,  
    YELLOW = 3, // 显式赋值  
    PURPLE    // 自动赋值为4(因为YELLOW=3,所以PURPLE=4)  
};  
  
int main() {  
    // 创建一个枚举变量  
    enum Color favoriteColor = GREEN;  
  
    // 打印枚举变量的值(注意:打印的是整型值)  
    printf("Favorite color: %d\n", favoriteColor);  
  
    // 使用枚举成员进行比较  
    if (favoriteColor == RED) {  
        printf("You like red!\n");  
    } else if (favoriteColor == GREEN) {  
        printf("You like green!\n");  
    } else {  
        printf("You like some other color.\n");  
    }  
  
    return 0;  
}
  •  运行结果:

在上面的示例中,展示了如何使用 structunion 和 enum 来定义复合数据类型,并展示了如何初始化和使用这些类型的变量。这些特性使得C/C++等编程语言非常灵活和强大,能够处理各种复杂的数据结构和常量集合。

五、typedef

typedef 是 C/C++ 语言中的一个关键字,它允许程序员为现有的数据类型定义一个新的名称(别名)。这样做的好处是,它可以使代码更加清晰易读,特别是当处理复杂的数据类型(如结构体、联合体、指针等)时。

5.1. 作用

  • typedef 的基本语法是 typedef existing_type new_type_name;,其中 existing_type 是已经存在的数据类型,new_type_name 是想要定义的新名称。
  • 使用 typedef 定义的别名,就像使用任何基本数据类型一样,可以用于变量声明、函数参数、返回值类型等。
  • typedef 经常与结构体(struct)和联合体(union)一起使用,以简化对这些复合数据类型的引用。
  • 还可以为指针类型定义别名,这在处理函数指针和复杂数据结构时特别有用。

5.2. 代码示例

示例1:为结构体定义别名:

#include <stdio.h>  
  
// 定义一个结构体类型  
struct Point {  
    int x;  
    int y;  
};  
  
// 使用 typedef 为结构体类型定义别名  
typedef struct Point Point;  
  
int main() {  
    // 使用别名创建结构体变量  
    Point p1;  
  
    // 给结构体成员赋值  
    p1.x = 10;  
    p1.y = 20;  
  
    // 打印结构体成员的值  
    printf("Point p1: (%d, %d)\n", p1.x, p1.y);  
  
    return 0;  
}
  • 运行结果:

typedef struct Point Point; 实际上有点冗余,因为当 struct 标签(Point)和 typedef 定义的别名相同时,可以直接在 typedef 中定义结构体,如下所示:

typedef struct {  
    int x;  
    int y;  
} Point;

 示例2:为指针类型定义别名:

#include <stdio.h>  
  
// 定义一个函数类型  
typedef int (*FuncPtr)(int, int);  
  
// 定义一个函数,该函数符合 FuncPtr 类型的签名  
int add(int a, int b) {  
    return a + b;  
}  
  
int main() {  
    // 使用别名创建函数指针变量  
    FuncPtr fp = add;  
  
    // 通过函数指针调用函数  
    int result = fp(3, 4);  
  
    // 打印结果  
    printf("Result: %d\n", result);  
  
    return 0;  
}
  • 运行结果:


http://www.kler.cn/a/531139.html

相关文章:

  • 数据库、数据仓库、数据湖有什么不同
  • 智能小区物业管理系统打造高效智能社区服务新生态
  • 标准IO与文件IO 进程与线程
  • 高温环境对电机性能的影响与LabVIEW应用
  • 网络原理(3)—— 传输层详解
  • 增删改查(CRUD)操作
  • js数据结构与算法
  • 房屋中介管理系统的设计与实现
  • 图形学笔记 - 5-光线追踪 - 辐射度量学
  • 高频文件更新数据实时同步实现原理
  • TensorFlow 示例平方米转亩
  • OpenCV:FLANN与暴力特征匹配
  • leetcode——二叉树的最近公共祖先(java)
  • HTML5教程之标签(2)
  • Java_类加载器
  • deep generative model stanford lecture note3 --- latent variable
  • 半导体器件与物理篇7 微波二极管、量子效应和热电子器件
  • SynchronousQueue 与 LinkedBlockingQueue区别及应用场景
  • DeepSeek-R1 低成本训练的根本原因是?
  • CTF-web: php-session临时文件特性
  • Spring MVC学习——发送请求(@RequestMapping注解及请求参数绑定)
  • Android学习19 -- 手搓App
  • The Simulation技术浅析(三):数值方法
  • Hive修复分区
  • 亚博microros小车-原生ubuntu支持系列:20 ROS Robot APP建图
  • 【IocDI】_存储Bean的五大类注解及getBean的使用