结构体
- 定义结构体变量的几种方法:
- 先定义结构体类型,再定义变量名
struct student { int ... char ... long ... }; struct student stu;
- 在定义结构体类型的同时,定义变量
struct student { int ... char ... long ... } stu;
- 直接定义结构体变量(不指定结构体标签)【不推荐】
struct { int ... char ... long ... } stu;
-
用typedef给数据类型定义一个别名
一般用全大写表示别名
写法一:
struct student { ... }; typedef struct student STUDENT;
写法二:
typedef struct student { ... } STUDENT;
写法三:
typedef struct { ... } STUDENT;
-
结构体的特性:
- 使用成员选择运算符“.”来访问结构体变量
- 支持同类结构体变量的整体赋值
- 不能使用==和!=来直接判定两个结构体变量是否相等
枚举类型
是一种基本数据类型(整型、字符型、浮点型、枚举类型)。
应用场合:当某些量仅由有限个整型数据值组成时。
typedef enum weeks {SUN, MON, TUE, WED, THU, FRI, SAT} WEEKS;
enum weeks today;
WEEKS today = MON;
联合/共用体
- 将结构体类型声明时的struct改为union即可对联合类型进行声明。
- 同一内存在每一瞬时只能保存一个成员,起作用的成员是最后一次赋值的成员。
union sample { short i; char ch; float f; };
指针
指针和一维数组
数组名a就是数组第一个元素a[0]的地址:&a[0]
&a[i]
与a + i
等价
一位数组元素的等价引用形式:a[i]
与*(a+i)
- 指针运算+循环:实现数组元素的访问
int a[10]; int *p; for (p = a; p < a+10; p++) { // 访问数组元素x=*p }
a+1中的“1”不是1Byte,而是一个数组元素所占的大小。(根据指针变量的即基类型而定,如int型就是4Byte)
a+i
实际为a+i*sizeof(基类型)
- 易错
假设有:
那么:int a[5] = {5, 6, 7, 8, 9}; int *p = a;
-
printf("%d\n", ++(*p));
的输出是6对*p加1,不改变p的指向
-
printf("%d\n", (*p)++);
的输出是5对*p加1,不改变p的指向
-
printf("%d\n", *p++);
的输出是5相当于
printf("%d\n", *(p++));
,即两步操作:printf("%d\n", *p);
p++;
p指向了下一个数组元素
-
指针和二维数组
-
对二维数组a[][],a代表二维数组的首地址,即第0行的地址(行地址)
- a+i代表第i行的地址
- *(a+i)代表第i行第0列的地址
- 第i行第j列的元素:
a[i][j]=*(*(a+i)+j)
-
按照行指针访问数组元素:
假设二维数组a[][]共3列,则定义指针p,其指向的基类型为“int[3]”:int (*p)[3]; p = a;
注意不能写成
int *p[3];
,否则定义的是指针数组,而非数组指针 -
二维数组的行指针作为函数参数,形参声明:
// 这里的列数N必须是个常数! void ArrayTest(int (*p)[N], int m, int n);
-
也可以用列指针来访问二维数组元素(类似于用计算一维数组下标的形式来处理)
内存映像
- 只读存储区(代码区和常量存储区)
- 静态存储区(存放全局变量和静态变量)
- 动态存储区:
- 堆:自上向下(由低地址端向高地址端生长),由程序员自行通过动态内存分配函数申请(生存期由程序员决定)
- 栈:自下向上(由高地址端向低地址端生长),由编译系统自动分配(生存期由函数决定),存储函数参数值、局部变量等
动态内存分配函数
- 向系统申请大小为size的地址块:
void* malloc(unsigned int size);
系统找到一块未占用的内存,将其标记为已占用,然后把首地址返回,若申请不成功,则返回NULL。 - 例:申请一块可存放10个整型变量的内存:
malloc(10 * sizeof(int));
void*型指针不指定其指向哪一种类型,可指向任意类型的变量,是一种通用型或无类型的指针(区分值为NULL的空指针)。使用时,需强转为其他类型。如:
p = (int*)malloc(n * sizeof(int));
- 释放内存:
void free(void* p);
动态数组
- 程序运行中动态申请的连续内存空间
- 长度动态可变
/*
动态数组的创建、增长、打印、释放
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *array;
int length;
} DArray;
DArray create(int);
void grow(DArray*, int);
void print(DArray*);
void release(DArray*);
int main()
{
int n;
DArray a;
scanf("%d", &n);
a = create(n); // 创建n长动态数组
print(&a);
grow(&a, 2 * n); // 新输入n个数,扩为2n长动态数组
print(&a);
release(&a); // 释放动态数组
return 0;
}
// 动态数组的创建
DArray create(int n) {
DArray a;
int i;
a.array = (int *)malloc(sizeof(int) * n);
if (a.array == NULL) {
printf("Allocation Error");
exit(0);
}
else {
a.length = n;
for (i = 0; i < a.length; i++) {
scanf("%d", a.array + i);
// 或等价地写成:
// scanf("%d", &a.array[i]);
}
}
return a;
}
// 动态数组的增长
void grow(DArray *aPtr, int n) {
int *p, i;
p = (int *)malloc(sizeof(int) * n); // 申请新数组的空间
if (p == NULL) {
return;
}
else {
for (i = 0; i < aPtr->length; i++){ // 复制之前的数组元素到新数组里
p[i] = aPtr->array[i];
}
for (i = aPtr->length; i < n; i++) { // 在新数组中,将新元素添加到旧元素之后
scanf("%d", &p[i]);
}
free(aPtr->array); // 释放旧数组的空间
aPtr->array = p;
aPtr->length = n;
}
return;
}
// 动态数组的输出
void print(DArray *p) {
int i;
for (i = 0; i < p->length; i++) {
printf("%d ", p->array[i]);
// 或等价地写成:
// printf("%d ", *(p->array + i));
}
printf("\n");
return;
}
// 动态数组的释放
void release(DArray *p) {
free(p->array);
return;
}
其他
- 库函数——exit:
exit(0)
:正常运行程序并退出程序。
exit(1)
:非正常运行导致退出程序; - 库函数——memcpy:
void *memcpy(void *str1, const void *str2, size_t n)
从存储区 str2 复制 n 个字节到存储区 str1