目录
一、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
可以直接作用于基本数据类型(如int
,char
,float
,double
等)以及用户定义的数据类型(如结构体、联合体等)。 -
变量:
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
被用来计算基本数据类型 int
, char
, float
, 和 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):
- 默认情况下,整数类型(如
int
、short
、long
)都是有符号的。 - 有符号数使用最高位作为符号位,0 表示正数,1 表示负数。
- 有符号数的取值范围包括负数、零和正数。
2. 无符号数(unsigned):
- 无符号数不使用符号位,因此可以表示更大的正数范围。
- 无符号数的取值范围从0开始,一直到该类型能表示的最大正数。
- 在声明变量时,可以使用
unsigned
关键字来指定无符号类型,如unsigned int
、unsigned short
、unsigned 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++等编程语言中,struct
、union
和 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;
}
- 运行结果:
在上面的示例中,展示了如何使用 struct
、union
和 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;
}
- 运行结果: