C语言入门小册子
C语言的数据类型、变量声明和特殊操作符
基本数据类型
C语言支持多种基础数据类型及其变体,具体如下:
类型 | 用途 | 大小(位) | 范围 |
---|---|---|---|
char |
字符 | 8 | -128 到 127 |
unsigned char |
无符号字符 | 8 | 0 到 255 |
short |
短整型 | 16 | -32,768 到 32,767 |
unsigned short |
无符号短整型 | 16 | 0 到 65,535 |
int |
整型 | 32 | –2,147,483,648 到 2,147,483,647 |
unsigned int |
无符号整型 | 32 | 0 到 4,294,967,295 |
long |
长整型 | 32 | -2,147,483,648 到 2,147,483,647 |
unsigned long |
无符号长整型 | 32 | 0 到 4,294,967,295 |
float |
浮点型 | 32 | 1.2 × 10⁻³⁸ 到 3.4 × 10³⁸ |
double |
双精度浮点型 | 64 | 2.2 × 10⁻³⁰⁸ 到 1.8 × 10³⁰⁸ |
long double |
长双精度浮点型 | 128 | 3.4 × 10⁻⁴⁹³² 到 1.2 × 10⁴⁹³² |
不同系统中这些数据类型的定义可能有所差异,但以下规则始终成立:
short <= int <= long
float <= double <= long double
变量声明
变量声明格式为:
int myval, tmp1, tmp2;
unsigned int marker1 = 1, marker2 = 10;
float magnitude, phase;
命名规则:
- 变量名由字母、数字和下划线组成,首字符必须是字母。
- 变量名可以使用大写字母,但约定俗成的做法是将大写字母保留给常量名。
- 下划线开头的变量名通常保留给内部库使用。
常量声明:
#define PI 3.141592654 // 预处理器定义符号常量
const int a; // 定义只读变量
数组声明与初始化
数组可以用以下形式声明和初始化:
int myarray[10];
unsigned int list[5] = { 10, 15, 12, 19, 23 };
float rdata[128], grid[5][5];
- 数组索引从 0 开始。例如,
list
的索引为 0 到 4。 - 可以使用循环访问数组:
for( int i = 0; i <= 127; i = i + 1 )
{
printf ( "%f\n", rdata[i] );
}
注意: C语言对数组边界访问不进行严格检查,越界访问可能导致程序行为异常。
字符数组和字符串:
char s[128];
strcpy( s, "This is a test!" );
字符串以 \0
(null字符)作为结束标志,许多C函数依赖此标志确定字符串的末尾。
指针
指针存储变量或数组的地址,其声明方式如下:
int *ptr; // 定义一个指向整型变量的指针
*ptr = 345; // 通过指针修改地址中的值
取地址操作符:
int tmp;
somefunc( &tmp ); // 获取变量 tmp 的地址,并传递给函数
指针与变量关系:
- 声明指针形式:
*myptr
&myvar
表示变量myvar
的地址*myptr
表示指针myptr
指向的变量值
数组与指针的关系: 数组名本质上是指向数组首元素的指针。例如:
char s[256];
somefunc( s ); // 实际上传递的是数组首地址
但直接访问数组元素如 s[12]
,表示索引为 12 的值(即第 13 个元素)。
C语言中的特殊字符
C语言定义了一些特殊字符:
转义字符 | 描述 |
---|---|
\a |
报警(响铃) |
\b |
退格符 |
\f |
换页符 |
\n |
换行符 |
\r |
回车符 |
\t |
水平制表符 |
\v |
垂直制表符 |
\\ |
反斜杠 |
\? |
问号 |
\' |
单引号 |
\" |
双引号 |
\0NN |
八进制字符代码 |
\xNN |
十六进制字符代码 |
\0 |
空字符 |
总结
- 数据类型:C支持多种基本类型,不同系统可能存在实现差异。
- 变量声明:可以同时声明多个变量,初始化时可以选择不同格式的常量。
- 数组与字符串:数组索引从0开始,字符串需要以
\0
作为结束标志。 - 指针:指针是C语言的重要特性,可以访问和修改变量地址存储的值,同时与数组密切相关。
C语言中的字符串、结构体和向量操作
字符串的特殊性
-
字符串字面量作为指针:
字符串字面量实际被编译器存储为一个指针。例如:char *p; p = "Life, the Universe, & Everything!";
在此,
p
是指向字符串的指针,p[0]
的值为'L'
。类似地:char ch; ch = "Life, the Universe, & Everything!"[0];
会将
'L'
存储到变量ch
中。 -
错误的字符串初始化:
以下操作是错误的:char s[128] = "This is a test!";
这种操作会导致未使用的内存浪费,并且可能引发越界访问问题。正确的做法是使用
strcpy
将字符串拷贝到数组中:char s[128]; strcpy(s, "This is a test!");
-
传递字符串到函数:
字符串作为参数传递时需特别小心。以下示例展示了如何正确处理字符串:/* strparm.c */ #include <stdio.h> #include <string.h> char *strtest( char *a, char *b ); int main () { char a[256], b[256], c[256]; strcpy(a, "STRING A: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); strcpy(b, "STRING B: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); strcpy(c, "STRING C: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); printf("Initial values of strings:\n"); printf(" a = %s\n b = %s\n c = %s\n\n", a, b, c); strcpy(c, strtest(a, b)); printf("Final values of strings:\n"); printf(" a = %s\n b = %s\n c = %s\n\n", a, b, c); return 0; } char *strtest( char *x, char *y ) { printf("Values passed to function:\n x = %s\n y = %s\n\n", x, y); strcpy(y, "NEWSTRING B: abcdefghijklmnopqrstuvwxyz0123456789"); return "NEWSTRING C: abcdefghijklmnopqrstuvwxyz0123456789"; }
结构体
结构体是C语言中用于存储不同类型数据集合的方式。例如:
/* struct.c */
#include <stdio.h>
#include <string.h>
struct person
{
char name[50];
int age;
float wage;
};
void display(struct person);
int main()
{
struct person m;
strcpy(m.name, "Coyote, Wile E.");
m.age = 41;
m.wage = 25.50f;
display(m);
return 0;
}
void display(struct person p)
{
printf("Name: %s\nAge: %d\nWage: %4.2f\n", p.name, p.age, p.wage);
}
-
定义和使用:
- 使用
struct
定义结构体类型。 - 使用
struct
关键字和类型名声明变量。 - 使用点符号访问成员。
- 使用
-
结构体赋值:
可以直接将一个结构体变量赋值给另一个结构体变量:struct person m, n; m = n;
-
结构体数组和嵌套:
结构体可以组成数组或嵌套其他结构体:struct person group[10]; strcpy(group[5].name, "McQuack, Launchpad"); struct trip_rec { struct person traveler; char dest[50]; int date[3]; int duration; float cost; }; struct trip_rec t1; strcpy(t1.traveler.name, "Martian, Marvin");
-
指针访问结构体成员:
使用->
操作符通过指针访问结构体成员:struct person *sptr; sptr->age = 50;
-
联合体 (Union):
类似结构体,但存储的数据共享同一块内存:union usample { char ch; int x; };
向量操作示例
以下程序演示了如何用结构体实现三维向量的常见操作:
/* vector.c */
#include <stdio.h>
#include <math.h>
#define PI 3.141592654
struct v
{
double i, j, k;
};
void vadd(struct v, struct v, struct v*);
void vprint(struct v);
void vsub(struct v, struct v, struct v*);
double vnorm(struct v);
double vdot(struct v, struct v);
double vangle(struct v, struct v);
void vcross(struct v, struct v, struct v*);
int main()
{
struct v v1 = {1, 2, 3}, v2 = {30, 50, 100}, v3;
double a;
printf("Sample Vector 1: ");
vprint(v1);
printf("Sample Vector 2: ");
vprint(v2);
vadd(v1, v2, &v3);
printf("Vector Add: ");
vprint(v3);
vsub(v1, v2, &v3);
printf("Vector Subtract: ");
vprint(v3);
vcross(v1, v2, &v3);
printf("Cross Product: ");
vprint(v3);
printf("\nVector 1 Norm: %f\n", vnorm(v1));
printf("Vector 2 Norm: %f\n", vnorm(v2));
printf("Dot Product: %f\n", vdot(v1, v2));
a = 180 * vangle(v1, v2) / PI;
printf("Angle: %3f degrees.\n", a);
return 0;
}
/* 其他函数的实现省略,与上述代码一致 */
特点:
- 使用结构体表示三维向量。
- 各函数操作包括向量加减、点积、叉积、范数及向量间的夹角。
- 演示了如何通过指针修改结构体。
总结
- 字符串: 理解字符串作为指针的行为及使用
strcpy
初始化数组的重要性。 - 结构体: 提供了一种组织复杂数据的方式,支持数组、嵌套及指针访问。
- 向量操作: 通过结构体和函数实现复杂数学运算,使代码简洁易懂。
C语言中的静态变量、特殊声明、类型转换和枚举
静态变量 (static
)
局部变量可以使用 static
声明,使其在函数的多次调用之间保留值。例如:
#include <stdio.h>
void testfunc(void);
int main()
{
int ctr;
for( ctr = 1; ctr <= 8; ++ctr )
{
testfunc();
}
return 0;
}
void testfunc(void)
{
static int v;
printf("%d\n", 2 * v);
++v;
}
输出:
0
2
4
6
8
10
12
14
解析:
static
使变量v
在多次调用testfunc
时保留其值。- 默认情况下,
static
变量初始化为0
,但最好显式初始化以避免依赖默认值。
其他特殊声明
-
register
声明:
提示编译器将变量尽量分配到CPU寄存器中,提高访问速度:register int counter;
注意:
- 现代优化编译器会自动优化寄存器的使用,因此
register
声明很少被使用。 - 如果无法将变量分配到寄存器中,变量会像普通变量一样存储在内存中。
- 现代优化编译器会自动优化寄存器的使用,因此
-
volatile
声明:
声明变量的值可能随时被外部因素修改,例如硬件寄存器:volatile int clock;
用途:
用于表示实时数据或与外部设备交互的变量。
类型转换
C语言在许多情况下会自动进行类型转换。例如:
- 将
char
转换为int
。 - 将
int
转换为float
。
注意:
- 从较大类型转换为较小类型(如
int
转换为char
)可能导致数据丢失或意外错误。 - 签名类型和无符号类型之间的转换可能导致问题。
显式类型转换:
使用强制类型转换(cast
)避免隐式转换问题:
int a;
float b;
b = (float)a;
枚举类型 (enum
)
枚举类型用于定义一组具名的常量。例如:
enum day
{
saturday, sunday, monday, tuesday, wednesday, thursday, friday
};
特点:
-
默认从
0
开始递增,saturday = 0
,sunday = 1
,依此类推。 -
可以指定自定义值:
enum temps { zero = 0, freeze = 32, boil = 220 };
-
定义后的枚举类型可以声明变量:
enum day today = wednesday; // `today` 为枚举类型变量,值为 3。
优点:
- 比
#define
定义常量更整洁。 - 方便代码的可读性和维护性。
自定义类型 (typedef
)
typedef
用于为复杂类型定义别名。例如:
typedef char str[128];
之后,可以使用新类型声明变量:
str name; // 等价于 `char name[128];`
总结
- 静态变量 (
static
):使局部变量在函数间调用时保留值。 - 特殊声明:
register
:提示编译器优化寄存器分配,但现代编译器通常无需显式声明。volatile
:声明值可能随时变化的变量,用于处理硬件寄存器等场景。
- 类型转换: 谨慎处理隐式类型转换,建议使用显式转换避免问题。
- 枚举类型 (
enum
):定义具名的常量集,提高代码可读性。 - 自定义类型 (
typedef
):通过别名简化复杂类型声明,使代码更简洁。