数据类型与变量
4. 数据类型与变量
在 C 语言中,数据类型用于定义变量或函数返回值所能存储的数据的类型。理解不同的数据类型以及如何定义和使用变量,是掌握 C 语言编程的基础。
4.1 C 语言的数据类型
C 语言提供了多种数据类型,分为基本数据类型和用户自定义数据类型。基本数据类型包括整型、浮点型、字符型、枚举类型和void类型等。
4.1.1 整型
整型用于表示整数。C 语言提供了多种整型,分别用于存储不同范围的整数。
- 基本整型:
int:标准整数类型,通常占用 4 个字节(32 位)。short:短整数类型,通常占用 2 个字节(16 位)。long:长整数类型,通常占用 4 个或 8 个字节(32 位或 64 位,取决于系统)。long long:更长的整数类型,通常占用 8 个字节(64 位)。
- 有符号与无符号:
- 默认情况下,整型是有符号的(
signed),可以表示正数、负数和零。 - 使用
unsigned关键字可以定义无符号整型,只表示零和正数,范围更大。
- 默认情况下,整型是有符号的(
示例:
#include <stdio.h>
int main() {
int a = 10; // 有符号整型
unsigned int b = 20; // 无符号整型
short c = -5; // 有符号短整型
long d = 100000L; // 有符号长整型
long long e = 10000000000LL; // 有符号长长整型
printf("int a = %d\n", a);
printf("unsigned int b = %u\n", b);
printf("short c = %d\n", c);
printf("long d = %ld\n", d);
printf("long long e = %lld\n", e);
return 0;
}
输出:
int a = 10
unsigned int b = 20
short c = -5
long d = 100000
long long e = 10000000000
4.1.2 浮点型
浮点型用于表示带有小数部分的实数。C 语言提供了几种浮点类型,以支持不同的精度需求。
float:单精度浮点数,通常占用 4 个字节。double:双精度浮点数,通常占用 8 个字节。long double:更高精度的浮点数,通常占用 12 或 16 个字节,具体取决于编译器和系统。
示例:
#include <stdio.h>
int main() {
float pi = 3.14f; // 单精度浮点数
double e = 2.718281828; // 双精度浮点数
long double phi = 1.61803398875L; // 长双精度浮点数
printf("float pi = %.2f\n", pi);
printf("double e = %.9lf\n", e);
printf("long double phi = %.11Lf\n", phi);
return 0;
}
输出:
float pi = 3.14
double e = 2.718281828
long double phi = 1.61803398875
4.1.3 字符型
字符型用于存储单个字符。C 语言提供了char类型来表示字符,同时也可以用于存储小整数(因为char实际上是一个整数类型)。
char:占用 1 个字节(8 位),可以表示 ASCII 字符。unsigned char:无符号字符类型,范围从 0 到 255。signed char:有符号字符类型,范围从-128 到 127。
示例:
#include <stdio.h>
int main() {
char letter = 'A'; // 字符'A'
unsigned char uchar = 200; // 无符号字符
signed char schar = -100; // 有符号字符
printf("char letter = %c\n", letter);
printf("unsigned char uchar = %u\n", uchar);
printf("signed char schar = %d\n", schar);
return 0;
}
输出:
char letter = A
unsigned char uchar = 200
signed char schar = -100
4.1.4 枚举类型
**枚举类型(enum)**用于定义一组具名的整数常量,使代码更加易读和易维护。通过enum可以为相关的常量赋予有意义的名字。
示例:
#include <stdio.h>
// 定义枚举类型Day
enum Day {
SUNDAY, // 0
MONDAY, // 1
TUESDAY, // 2
WEDNESDAY, // 3
THURSDAY, // 4
FRIDAY, // 5
SATURDAY // 6
};
int main() {
enum Day today = WEDNESDAY;
printf("Today is day number %d\n", today);
if (today == WEDNESDAY) {
printf("It's the middle of the week!\n");
}
return 0;
}
输出:
Today is day number 3
It's the middle of the week!
扩展:
可以为枚举类型指定具体的值:
enum ErrorCode {
SUCCESS = 0,
ERROR_NOT_FOUND = 404,
ERROR_SERVER = 500
};
int main() {
enum ErrorCode code = ERROR_NOT_FOUND;
printf("Error Code: %d\n", code);
return 0;
}
输出:
Error Code: 404
4.1.5 void 类型
void类型表示“无类型”,用于指示没有数据类型或没有返回值。它常用于函数的返回类型或指针类型。
- 函数返回类型:当函数不返回任何值时,使用
void作为返回类型。 - 指针类型:
void*指针可以指向任何类型的数据,但在使用前需要转换为具体的指针类型。
示例:
#include <stdio.h>
// 无返回值的函数
void greet() {
printf("Hello from greet function!\n");
}
int main() {
greet(); // 调用无返回值的函数
// 使用void指针
int a = 10;
void *ptr = &a; // void指针指向整数a
// 需要转换为具体类型后才能使用
printf("Value pointed by ptr: %d\n", *(int*)ptr);
return 0;
}
输出:
Hello from greet function!
Value pointed by ptr: 10
4.2 变量的定义与初始化
变量是用于存储数据的内存位置,每个变量都有特定的数据类型,决定了它可以存储的数据种类和大小。
变量的定义
变量定义时需要指定数据类型和变量名,语法如下:
数据类型 变量名;
示例:
#include <stdio.h>
int main() {
int age; // 定义一个整型变量age
float salary; // 定义一个浮点型变量salary
char grade; // 定义一个字符型变量grade
// 赋值
age = 25;
salary = 55000.50f;
grade = 'A';
// 输出变量值
printf("Age: %d\n", age);
printf("Salary: %.2f\n", salary);
printf("Grade: %c\n", grade);
return 0;
}
输出:
Age: 25
Salary: 55000.50
Grade: A
变量的初始化
变量在定义时可以同时赋予初始值,这被称为初始化。初始化可以确保变量在使用前具有确定的值。
语法:
数据类型 变量名 = 初始值;
示例:
#include <stdio.h>
int main() {
int count = 100; // 定义并初始化整型变量
double temperature = 36.6; // 定义并初始化双精度浮点型变量
char initial = 'B'; // 定义并初始化字符型变量
printf("Count: %d\n", count);
printf("Temperature: %.1lf\n", temperature);
printf("Initial: %c\n", initial);
return 0;
}
输出:
Count: 100
Temperature: 36.6
Initial: B
注意事项:
-
未初始化变量:如果变量在定义时未被初始化,其值是未定义的,可能导致不可预测的结果。
示例:
#include <stdio.h> int main() { int x; // 未初始化 printf("Value of x: %d\n", x); // 输出未定义的值 return 0; }输出(结果可能不同):
Value of x: 32767 -
初始化顺序:初始化必须在定义时进行,不能在定义后进行。
错误示例:
int main() { int a; a = 10; // 正确,赋值 int b = a; // 正确,初始化b为a的值 return 0; }
4.3 常量的定义
常量是程序中值不可以改变的数据。C 语言提供了两种方式定义常量:宏定义(#define)和**const关键字**。
4.3.1 #define
**#define**是预处理指令,用于定义宏常量或宏函数。在编译前,预处理器会将所有的宏定义替换为其对应的值。
-
定义常量:
#define 常量名 值 -
示例:
#include <stdio.h> #define PI 3.14159 #define MAX_SIZE 100 int main() { float area; float radius = 5.0f; area = PI * radius * radius; // 使用宏常量PI printf("Area of the circle: %.2f\n", area); int array[MAX_SIZE]; // 使用宏常量MAX_SIZE printf("Size of the array: %d\n", MAX_SIZE); return 0; }输出:
Area of the circle: 78.54 Size of the array: 100 -
宏函数:
#define也可以用于定义宏函数,实现简单的代码复用。#define SQUARE(x) ((x) * (x))示例:
#include <stdio.h> #define SQUARE(x) ((x) * (x)) int main() { int num = 4; int result = SQUARE(num + 1); // 注意宏展开 printf("Square of %d is %d\n", num + 1, result); // (4 + 1) * (4 + 1) = 25 return 0; }输出:
Square of 5 is 25注意:在定义宏函数时,使用括号可以避免运算优先级问题。
4.3.2 const
**const**关键字用于声明常量,表示变量的值在初始化后不能被修改。与#define不同,const定义的常量具有类型,可以进行类型检查。
-
语法:
const 数据类型 常量名 = 值; -
示例:
#include <stdio.h> int main() { const double PI = 3.14159; const int MAX_USERS = 50; // PI = 3.14; // 错误:不能修改const变量 // MAX_USERS = 100; // 错误:不能修改const变量 printf("PI: %.5lf\n", PI); printf("Max Users: %d\n", MAX_USERS); return 0; }输出:
PI: 3.14159 Max Users: 50 -
优点:
- 类型安全:编译器会进行类型检查,防止类型错误。
- 调试友好:调试时可以看到常量的类型和值。
-
示例:
#include <stdio.h> const int DAYS_IN_WEEK = 7; int main() { printf("There are %d days in a week.\n", DAYS_IN_WEEK); // 尝试修改常量会导致编译错误 // DAYS_IN_WEEK = 8; // 错误 return 0; }输出:
There are 7 days in a week.
4.4 数据类型的取值范围
不同的数据类型在内存中占用的字节数不同,因此它们能够表示的数值范围也不同。了解数据类型的取值范围有助于选择合适的类型以节省内存和防止溢出。
常见数据类型的取值范围(以 32 位系统为例,具体范围可能因系统和编译器而异):
| 数据类型 | 大小(字节) | 有符号范围 | 无符号范围 |
|---|---|---|---|
char | 1 | -128 to 127 | 0 to 255 |
short | 2 | -32,768 to 32,767 | 0 to 65,535 |
int | 4 | -2,147,483,648 to 2,147,483,647 | 0 to 4,294,967,295 |
long | 4 或 8 | -2,147,483,648 to 2,147,483,647(32 位)-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807(64 位) | 0 to 4,294,967,295(32 位)0 to 18,446,744,073,709,551,615(64 位) |
long long | 8 | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | 0 to 18,446,744,073,709,551,615 |
float | 4 | 大约 ±3.4E−38 至 ±3.4E+38 (6 位小数) | - |
double | 8 | 大约 ±1.7E−308 至 ±1.7E+308 (15 位小数) | - |
long double | 12 或 16 | 更大的范围和更高的精度 | - |
示例:
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main() {
printf("char:\n");
printf(" Signed char: %d to %d\n", SCHAR_MIN, SCHAR_MAX);
printf(" Unsigned char: 0 to %u\n\n", UCHAR_MAX);
printf("short:\n");
printf(" Signed short: %d to %d\n", SHRT_MIN, SHRT_MAX);
printf(" Unsigned short: 0 to %u\n\n", USHRT_MAX);
printf("int:\n");
printf(" Signed int: %d to %d\n", INT_MIN, INT_MAX);
printf(" Unsigned int: 0 to %u\n\n", UINT_MAX);
printf("long:\n");
printf(" Signed long: %ld to %ld\n", LONG_MIN, LONG_MAX);
printf(" Unsigned long: 0 to %lu\n\n", ULONG_MAX);
printf("long long:\n");
printf(" Signed long long: %lld to %lld\n", LLONG_MIN, LLONG_MAX);
printf(" Unsigned long long: 0 to %llu\n\n", ULLONG_MAX);
printf("float:\n");
printf(" Range: %e to %e\n", FLT_MIN, FLT_MAX);
printf(" Precision: %d digits\n\n", FLT_DIG);
printf("double:\n");
printf(" Range: %e to %e\n", DBL_MIN, DBL_MAX);
printf(" Precision: %d digits\n\n", DBL_DIG);
printf("long double:\n");
printf(" Range: %Le to %Le\n", LDBL_MIN, LDBL_MAX);
printf(" Precision: %d digits\n", LDBL_DIG);
return 0;
}
输出(具体数值可能因系统和编译器而异):
char:
Signed char: -128 to 127
Unsigned char: 0 to 255
short:
Signed short: -32768 to 32767
Unsigned short: 0 to 65535
int:
Signed int: -2147483648 to 2147483647
Unsigned int: 0 to 4294967295
long:
Signed long: -2147483648 to 2147483647
Unsigned long: 0 to 4294967295
long long:
Signed long long: -9223372036854775808 to 9223372036854775807
Unsigned long long: 0 to 18446744073709551615
float:
Range: 1.175494e-38 to 3.402823e+38
Precision: 6 digits
double:
Range: 2.225074e-308 to 1.797693e+308
Precision: 15 digits
long double:
Range: 3.362103e-4932 to 1.189731e+4932
Precision: 18 digits
注意:
- 使用
<limits.h>和<float.h>头文件可以获取各种数据类型的限制常量,如INT_MAX、FLT_MAX等。 - 不同系统和编译器可能有不同的数据类型大小,尤其是
long和long double。
4.5 类型转换与强制类型转换
类型转换是将一种数据类型的值转换为另一种数据类型的过程。C 语言支持隐式类型转换和强制类型转换。
4.5.1 隐式类型转换
隐式类型转换是由编译器自动完成的,无需程序员显式地指定。这通常发生在表达式中不同类型的数据一起使用时。
示例:
#include <stdio.h>
int main() {
int a = 5;
double b = 2.5;
double result;
// 隐式类型转换:int a 自动转换为 double
result = a + b; // 5 (int) -> 5.0 (double)
printf("Result of a + b: %.2lf\n", result); // 输出 7.50
return 0;
}
输出:
Result of a + b: 7.50
规则:
- 如果表达式中有不同类型的数据,C 语言会按照一定的规则进行转换,通常将较低精度的类型转换为较高精度的类型,以避免数据丢失。
- 整型会被提升为浮点型(
float或double)时,整型数据会被转换为对应的浮点数。
4.5.2 强制类型转换
强制类型转换(显式类型转换)是程序员明确指定将一种类型转换为另一种类型。这通过在要转换的值前加上目标类型的形式实现。
语法:
(目标类型) 值
示例:
#include <stdio.h>
int main() {
double pi = 3.14159;
int intPi;
float floatPi;
// 强制类型转换为int
intPi = (int)pi;
printf("pi as int: %d\n", intPi); // 输出 3
// 强制类型转换为float
floatPi = (float)pi;
printf("pi as float: %.5f\n", floatPi); // 输出 3.14159
// 强制类型转换中的表达式
int a = 10;
int b = 3;
double division;
division = (double)a / b; // a 被转换为 double,结果为 3.333333
printf("Division result: %.6lf\n", division);
return 0;
}
输出:
pi as int: 3
pi as float: 3.14159
Division result: 3.333333
应用场景:
-
消除精度丢失:在需要保留浮点数的精度时,将整数转换为浮点数。
int a = 7, b = 2; double result; result = (double)a / b; // 将a转换为double,避免整数除法 printf("Result: %.2lf\n", result); // 输出 3.50 -
内存优化:当需要节省内存时,可以将较大的数据类型转换为较小的数据类型。
double largeNumber = 123456.789; short smallNumber; smallNumber = (short)largeNumber; // 强制转换为short,可能导致数据溢出 printf("Small number: %d\n", smallNumber); // 输出不可预测的结果注意:强制类型转换可能导致数据丢失或溢出,应谨慎使用。
-
指针类型转换:在处理不同类型的指针时,需要进行强制类型转换。
#include <stdio.h> int main() { int a = 10; void *ptr = &a; // void指针可以指向任何类型 // 强制类型转换为int指针,然后解引用 printf("Value pointed by ptr: %d\n", *(int*)ptr); return 0; }输出:
Value pointed by ptr: 10
示例:
#include <stdio.h>
int main() {
// 隐式类型转换示例
int x = 10;
double y = 3.5;
double sum = x + y; // x 被隐式转换为 double
printf("Sum: %.2lf\n", sum); // 输出 13.50
// 强制类型转换示例
double pi = 3.14159;
int intPi = (int)pi; // 强制转换为 int,截断小数部分
printf("Integer part of pi: %d\n", intPi); // 输出 3
// 强制转换中的表达式示例
int a = 5, b = 2;
double division = (double)a / b; // a 被强制转换为 double
printf("Division: %.2lf\n", division); // 输出 2.50
// 指针类型转换示例
int num = 100;
void *ptr = #
printf("Value via void pointer: %d\n", *(int*)ptr); // 输出 100
return 0;
}
输出:
Sum: 13.50
Integer part of pi: 3
Division: 2.50
Value via void pointer: 100