C语言学习笔记

数据结构教程

前提:main中无功能,功能都写在main外函数中

为什么要用malloc?直接声明结点不好吗?

答:因为添加链表结点一般是在main函数以外的函数中,我们在函数里面定义的变量会放在本函数栈内,随着函数调用的结束,栈会清空,结点也没了

为什么函数传参用LinkNode *&L?

因为要改变main中定义的L存的指针值,所以在函数中直接引用main中的L。如果不改变L则不需要&
如初始化链表头结点时,在main中定义了一个LinkNode,需要在初始化函数中malloc,则要引用main中的指针变量

void InitList(LinkNode *&L){
    L=(LinkNode *)malloc(sizeof(LinkNode));
    L->next=NULL;
}
int main() {
    LinkNode *l1;
    InitList(l1);
    return 0;
}

bool

为C++特有,形式是true、false,实质是0、1;

头文件

1、#include <stdio.h>声明了函数库,因此可以使用类如printf的函数
2、#include " "用于引用用户库,#include <>用于引用系统库
3、stdlib.h是通用工具函数库,stdio.h是标准的输入输出工具函数库。

main .h .c 函数引用

//main.c 函数使用
#include "test.h"

int main() {
    fun1();
    return 0;
}

//test.c 函数定义
#include <stdio.h>
#include "test.h"

void fun1() {
    printf("ok");
}
//test.h 函数声明
void fun1();

变量

1、c语言区分大小写,即变量名大写和小写是不同的,且只能是字母、数字、下划线,第一个字符只能是字母或下划线
2、int 占四个字节
3、变量可以连续定义,但不能连续定义声明

int a=1;
//int b,c=a;//不能这样连续定义声明,这样相当于只初始了c
int b=a,c=a;//可以这样连续定义声明

变量名的本质是什么? 是变量地址的符号化,变量是为了让我们编程时更加方便,对人友好,可计算机可不认识什么变量 a,它只知道地址和指令。所以当你去查看 C 语言编译后的汇编代码,就会发现变量名消失了,取而代之的是一串串抽象的地址。你可以认为,编译器会自动维护一个映射,将我们程序中的变量名转换为变量所对应的地址,然后再对这个地址去进行读写。也就是有这样一个映射表存在,将变量名自动转化为地址。

printf与scanf

%-5.2f会至少显示5位数字并带有2位小数的浮点数,左对齐,在printf中使用

scanf("%d",&a);
//&是取地址运算符,scanf读取时,你得告诉他往哪里存储读进来的数据,
//所以要告诉他“地址”。

%s%d%f会忽略空格,%c不会,所以如果输入char前有空格,%c前必须留有空格

scanf("%d%s %c");//例如输入"1 小明 男"

两个scanf,第二个scanf是%c时,第一个scanf会造成缓冲区残留\n,下一个scanf会自动接收上一个scanf遗留的\n,所以不会暂停,解决办法是在两个scanf之间加上fflush(stdin)
当scanf是%d、%f时会自动清空缓冲区
debug调试时printf会暂存到输出缓冲区,可以用fflush(stdout)放在printf后面
不是所有编译器都支持fflush(stdin)、fflush(stdout),也可以用如下解决

 	char clear;
    scanf("%c",&clear);//清除缓冲区回车符

scanf有返回值,是匹配成功的个数

运算符与表达式

关系表达式的值只有真和假,对应的值为 1和 0。由于 C 语言中没有布尔类型,所以在 C 语言中 0 值代表假,非 0 值即为真

逻辑非的优先级高于算术运算符,算术运算符的优先级高于关系运算符、关系运算符的优先级高于逻辑与和逻辑或运算符

数组

1、int arr[n] n必须是常量,不能是变量

只能在在定义数组时对数组元素赋初值。例如,

int a[10]={0,1,2,3,4,5,6,7,8,9};

不能定义后赋值

int a[10];a[10]={0,1,2,3,4,5,6,7,8,9}

2、数组仅声明不赋初值,会有随机值;只给前几个赋值,后几个自动赋0
3、访问定义范围外的数组会越界,会造成一起定义的变量改变

int arr[5];
arr[5];

4、字符数组有特殊的初始化方式,以字符串形式初始化,数组未满则自动补0,0转化为字符是\0,是结束符

char c[5]="abcd";

5、字符数组不必循环,可以直接printf,前提结尾有结束符\0,所以字符数组不要满

char c[5]="abc";
printf("%s",c);

6、scanf字符数组时不需要&变量名,因为数组变量名本身是指针

	char c[10];
	scanf("%s",c);
	printf("%s",c);
	//缺点是一行不能有空格,否则只输出空格前面的

用gets()代替scanf,可以完整获取一行,包括空格;puts()代替printf()

	char c[10];
	gets(c);//优于scanf("%s",c),一行可以用空格
    puts(c);//代替printf("%s\n",c);  只能用于字符数组
    //c++中用fgets(prioner,size,streamType(例stdin))

7、针对字符串的一些函数,引用#include “string.h”
strlen函数用于统计字符串长度,
strcpy函数用于将某个字符串复制到字符数组中
strcmp(arr1,arr2)函数用于比较两个字符串的大小(第一个字符的大小,如果相同依次向后),注意strcmp标准C中并不是返回-1和1,而是负值和正值
strcat函数用于将两个字符串连接到一起
8、数组a[10]在函数传递后,clion调试器debug只显示数组a[10]第一个元素
在这里插入图片描述
解决:点击+新建监视输入*(int(*)[10])(a)
在这里插入图片描述
9、将一个数组的元素复制到另一个数组中,用头文件#include <string.h>的memcpy(destin_array,source_array,byte_num)函数,byte_num是拷贝几个字节

指针

1、存放地址不能用普通的变量类型,必须用指针变量,如

int *a;

使用取地址符&可以取得变量地址

int i;
int *i_pointer=&i;//指针变量不要存放普通常量

2、取值符*,可以取指针所在的值(解引用)

    int i=10;
    int *a_p=&i;
    printf("%d",*a_p);

引用

    int i=10;
    int &j=i;
    printf("%d",j);

引用可传递实参,引用被创建的同时必须被初始化
C++ 中指针和引用的区别

3、指针可以通过+n、-n偏移地址,此时±的自动n*类型内存大小,如int的偏移即n×4
4、数组名传递给子函数时,传的是数组第一个元素的地址
5、使用malloc要引用#include “stdlib.h”;
int arr[10]是静态申请的内存,在栈中,用malloc(i)动态申请内存,在堆中malloc()返回的是无类型指针,所以需要强制类型转换,如(char *)malloc(size);
栈中的空间自动管理,一个函数结束会自动释放空间;而malloc申请的堆空间需要手动free()掉
6、指针大小是固定的,32位系统为四个字节
7、空指针是NULL,可用于指针判断
8、calloc与malloc用法类似,malloc申请后空间的值是随机的,并没有进行初始化,而calloc却在申请后,对空间逐一进行初始化,并设置值为0,一般用于自动初始化结构体免去麻烦;

(int*)calloc(1,sizeof(int));
//calloc的第一个参数是元素的个数,第二个参数是每个元素的长度

9、int a[n]与(int *)malloc(sizeof (int)*n)都是声明数组,前者是在栈中,后者是在堆中,堆的容量较大,相较于栈不容易导致溢出问题
参考int a[n]与int* a=(int*)malloc(sizeof(int)*n)的区别与联系

函数

1、默认情况下,只有后面定义的函数才可以调用前面定义过的函数;

void fun1(){
    printf("fun1");
}
int main() {
    fun1();
    return 0;
}

如果想把函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数的前面进行函数的声明

void fun1();
int main() {
    fun1();
    return 0;
}
void fun1(){
    printf("fun1");
}

2、局部变量可以和全局变量重名,重名时,改变的是局部变量的值
3、void函数里可以return

void fun(){
return;
}

结构体

1、结构体对齐 结构体内存大小不是所有属性加起来,而是最大属性大小的整数倍
2、访问结构体成员,非指针可以用结构体变量名+点

p.name

结构体指针访问成员,有两种方式:

(*p).name//不是*p.name,因为.优先级比*高
p->name//考试用这种

2、结构体不同于基本类型的使用

int i;//基本类型可直接声明

结构体类型在定义之前,要声明才能定义

struct student{
    int id;
    char name[10];
};
int main() {
    struct student s1={1,"jojo"};
    return 0;
}

很麻烦,所以在声明时可以用typedef起别名

typedef struct student{//主名student可以省略
    int id;
    char name[10];
}stu;
int main() {
    stu s1={1,"jojo"};
    return 0;
}

指针别名也可以同时设置

typedef struct student{
    int id;
    char name[10];
}stu,* stuList;

用结构体别名定义指针变量有两种方式

stu *s1;
stuList s2;

3、
结构体中的成员类型不能是该结构体本身,却可以是以该结构体类型为基本类型的指针类型

typedef struct BiTNode{//要设置结构体名
    int data;
    struct BiTNode *lchild,*rchild;//此处只认结构体名,不认别名
}BiTNode,*BiTTree;

结构体所占用内存必须在编译时确定,如果一个结构体中含有它本身,那么这个结构体所占用的内存大小是多少呢?这会循环形成一个永远也无法计算清楚的数值;如果是结构体指针,那么久好说了,指针只占用4个字节,因此可以成为结构体成员。

C++引用


int a=1;
int &b=a;

可以理解为,b就是a,并非创了新的b的内存装a的值,b是个快捷方式,引用这个词很到位,引用就是一种给已经存在的变量添加一个新的别名

指针引用int *&a 、二级指针int **

void change(int *c,int *d){
    int *temp=c;
    c=d;
    d=temp;
}//指针变量c和d的值交换了
int main() {
    int a=1;
    int b=2;
    int *pa=&a;//pa的值是a的地址
    int *pb=&b;//pb的值是b的地址
    change(pa,pb);
    return 0;//pa和pb的值未变,即pa值还是a的指针,pb值还是b的指针
}
void change(int *&c,int *&d){//此时c和d是pa和pb的引用,完全可以看成pa和pb

在c中通过函数改变传入参数需要使用指针

void change(int *j){
    *j=2;
}
int main() {
    int i=1;
    change(&i);
    printf("%d",i);
    return 0;
}

在c++中使用引用&,无需变量加*

void change(int &j){//省了*,多了&
    j=2;
}
int main() {
    int i=1;
    change(i);//省了&
    printf("%d",i);
    return 0;
}

引用&必须在变量之前,中间不能有其他符号,需要改变指针时,&也要紧挨变量名

void function(char *&p){
    p=(char *)malloc(100);
    fgets(p,100,stdin);
}

引用必须赋值,避免了空值判断

C++布尔

c++比c多了布尔类型

	bool b1= true;
    bool b2= false;

顺序表

typedef int ElemType;//方便以后用其他数据类型,只需改int

链表

1、尾插法要加尾指针,为了防止每次插入新元素要从头遍历
2、结构体成员可以是指向自己结构体的指针,但是关键字不能是别名,而是结构体名,参考结构体包含指向自己的指针

typedef struct LNode{
    ElemType data;
    struct LNode *next;//此处struct关键字不能是别名Node
}Node, *LinkList;

队列

考顺序存储

顺序循环队列

为辨别空队列与满队列,一直要空一个元素的位置给rear。rear是要插入的位置,一直为空,所以数组队列满的时候rear在front前一个位置,里面为空
队空:rear=front
队满:(rear+1)%maxsize=front

二叉树

二叉树不会考顺序存储,考链式存储

位运算符

位运算符都是将整形视为二进制然后进行运算
1、移位 <<1是左移一位,>>1是右移一位
2、异或 i^j,例如i是5二进制是101,2二进制是010

101
010
____
111     =7
按位异或

0和任何数异或=数本身;一个数异或自身=0
异或有结合律: ( A ^ B ) ^ C = A ^ ( B ^ C )

浮点数

float f=4.5;

单精度,32位,第一位是符号位,表示该浮点类型的正负,0为正,1为负,4.5为正数,所以符号位为0;
尾数位为倒数23位,将4.5转成二进制为100.1,使其表现为1.xxx,所以为1.001*2^2,个位默认为1,只要小数部分,位数占不满在后面补零,所以尾数位为00100000000000000000000
指数位,由尾数推得,为(2的阶数+127)的二进制,2+127=129=10000001

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值