C语言 学习笔记 - Day 1
指针的声明和初始化
- int* p声明一个指针变量p,p指向一个int类型的变量,p的类型是int*,p指向的值的类型是int
- &p 取p的地址,p的类型是int,&p的类型是int*
- int* ptr = (int*)malloc(n*sizeof(int))
- int* ptr = (int*)calloc(n,sizeof(int))
- ptr = (int*) realloc(ptr,new size*sizeof(int))
一维数组的名称
- 一维数组名称多数情况下被当作首元素的地址,是一个常量指针,无法赋值
- 一维数组名称在使用sizeof(arr)和&arr时代表整个数组
宏函数以及函数的入栈出栈过程
- 宏函数的定义:#define 宏名(参数) 宏体
- 宏函数分为有参数宏定义和无参数宏定义
- 有参数宏定义:#define MAX(a,b) ((a>b)? a:b),其中MAX(a,b)是有参数宏定义,a和b是参数,((a>b)? a:b)是宏体,即替换文本,结果是a和b中的较大值
- 无参数宏定义:#define pi 3.1415926
- 在宏定义中,如果替换列表是一个表达式,那么通常需要用括号将整个表达式以及表达式中的每个参数括起来,以避免在宏替换后由于运算符优先级导致的错误
- 宏函数在一定程度上效率高于普通函数,因为普通函数存在入栈和出栈的过程。因此,在需要频繁调用的短函数上使用宏函数可以提高效率
结构体
- 结构体是一种特殊的复合类型,它由一组具有相同类型的数据元素组成,结构体的每个数据元素都被称为成员,每个成员都有一个数据类型和一个数据名
//定义结构体
struct student {
char name[50]; // 存储50个字符的数组
int age;
float height;
char sex;
};
int main() {
struct student student1;
// 将源字符串复制到目标字符串中去
strcpy(student1.name, "tom");
student1.age = 18;
student1.height = 1.75;
student1.sex = 'M';
return 0;
}
结构体嵌套二级指针的应用
- 二级指针:如果一个指针指向的是另外一个指针,我们就称它为二级指针。
C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*
int a = 100;
int *p1 = &a; //p1指向a
int **p2 = &p1; //p2指向p1
- 结构体嵌套二级指针:如果一个结构体的成员变量是一个二级指针,我们就说这个结构体嵌套了一个二级指针。例如,假设有一个结构体Teacher,它有一个成员变量students,students是一个二级指针,指向一个字符串数组。它们的关系可以用以下的C语言代码表示:
struct Teacher {
char *name; // 导师名字,需要动态分配空间
char **students; // 学生,需要动态分配空间,堆区数组
};
int main() {
// 定义一个二级指针,指向Teacher结构体的指针
struct Teacher **teachers = malloc(4 * sizeof(struct Teacher*));
// 为每位老师分配内存
for (int i = 0; i < 4; i++) {
teachers[i] = malloc(sizeof(struct Teacher));
teachers[i]->name = malloc(50 * sizeof(char));
sprintf(teachers[i]->name, "Teacher_%d", i + 1);//将格式化的输出发送到指定的字符串
// 为每位老师的学生分配内存
teachers[i]->students = malloc(4 * sizeof(char*));
for (int j = 0; j < 4; j++) {
teachers[i]->students[j] = malloc(50 * sizeof(char));
sprintf(teachers[i]->students[j], "Student_%d", j + 1);
}
}
// 打印老师和学生的名字
......
// 释放内存
......
return 0;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5d61e818ac397ae25ecca858460f16c5.jpeg#pic_center)
链表基础概念
- 链表是线性表的非连续存储结构,相较于数组存在以下优点:
- 数组是一个静态的空间,不可以动态扩展,链表可以动态扩展
- 数组元素的插入和删除效率很低,链表可以方便地在任意位置插入和删除元素
- 链表是由节点构成的;节点由数据域和指针域组成,指针域指向下一个节点
struct Node {
int data;
struct Node* next;
}
- 链表的分类:
- 动态链表和静态链表:动态链表在长度上没有限制,物理地址不连续
- 单链表和双链表:单链表的每个节点只有一个指针域,指向下一个节点;双链表的每个节点有两个指针域,分别指向下一个节点和上一个节点
链表基础操作
struct Node* initList() {
struct Node* head = (struct Node*)malloc(sizeof(struct Node));//head为指向链表头节点的指针
head->next = NULL;
return head;
}
void traverseList(struct Node* head) {
struct Node* ptr = head;
while (ptr != NULL) {
printf("%d ", ptr->data);
ptr = ptr->next;
}
}
void insertList(struct Node* head, int data,int position) {
struct Node* ptr = head;
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
int i;
for(i = 0; i < position - 1 && ptr != NULL; i++) {
ptr = ptr->next;
}
if(ptr != NULL) {
newNode->next = ptr->next;
ptr->next = newNode;
} else {
free(newNode);
printf("插入位置无效\n");
}
}
struct Node* Delete(struct Node* head, int x) {
struct Node* p, * q;
p = head;
q = p->next;
while(q != NULL) {
if(q->data == x) {
p->next = q->next;
free(q);
q = p->next;
} else {
p = p->next;
q = q->next;
}
}
if(head != NULL && head->data == x) {//首个元素为目标删除元素
q = head;
head = q->next;
free(q);
return head;
}
return head;
}
void Node_free(struct Node* head) {
struct Node* next;
while (head != NULL) {
next = head->next;
free(head);
head = next;
}
}
struct Node* Reverse(struct Node* head) {
struct Node* p = NULL;
struct Node* q = head;
while (q != NULL) {
struct Node* next = q->next;
q->next = p;
p = q;
q = next;
}
return p;
}