尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

C语言基础(上)

C语言基础(上) C语言核心笔记一、基础数据类型二、输入输出 printf / scanf1. printf格式符2. scanf三、字符串四、数组1.含义2.创建方式1一维数组2二维数组3 计算数组长度五、指针六、函数七、结构体八、练习内存图一、基础数据类型类型字节说明char1 字节字符存储 ASCII 码%c输出字符%d输出编码值short2 字节短整型 %hdint4 字节整型 %dlong4/8 字节长整型 %ldfloat4 字节单精度浮点数有效 67 位%fdouble8 字节双精度浮点数有效 1516 位%lf 重点int是整数精确float精度不如int小数会丢失精度。 char ch A; 单引号存单个字符双引号A是字符串。二、输入输出 printf / scanf1. printf格式符%d // int 十进制%c // 输出字符%f // float %.2f 保留小数点后两位%lf // double%x // 十六进制%o // 八进制%s // 字符串%p //指针地址%% //输出%本身 %c和进制无关直接输出字符本身。2. scanfint a;scanf(%d, a); // 必须加取地址符 易错忘记写程序直接崩溃。字符数组字符串char str[100];scanf(%s,str); //字符串不用自动以\0作为结束标记 字符串末尾隐藏 \0占用一个位置。三、字符串注意:c语言当中没有真正的字符串类型字符串\0(值为0)结尾字符串的本质就是char类型的数组char a[10];//指定长度 不初始化内容是垃圾值char b[10]{‘h’,’c’};//部分初始化 剩余的内容用‘\0’填充char c[5]{‘h’,’e’,’l’,’l’,’o’};//本身是一个数组 不是字符串char d[]”hello”;//字符串 h e l l o \0 长度是6char e[6]”hello”;//字符串 刚好放下char f[5]”hello”;//这个不对 不是合法的字符串//易错点分析char[g]{‘a’,’b’.’c’};//char类型数组 长度是3 不是字符串 因为没有’\0’char h[]”abc”;//字符串 长度是4char i[3]”abc”//这样写不对 因为没有空间放’\0’char s[6]”hello”;puts(s);//自动输出换行 相当于printf(“%s\n”,s);printf(“%s\n”,s);//输出字符串 遇到’\0’停止putchar(s[1]);//会根据下标输出单个字符 相当于 printf(“%c\n”,s[1]);scanf(“%s”,s);//停止条件空格/换行/Tabscanf(“%2s”,s);//指定宽度防止溢出char c;scanf(“ %c”,c);//%c前边有空格 表示跳过空白符cgetChar();注1字符串内容比较不能用正确方式If(strcmp(a,b)0){.........}注2数组名称是常量指针不能被赋值错误ab; a”www”;正确strcpy(a,b);char a[]”hello”;char b[]”world”;strcpy(a,b);//此时打印输出a为world注3strlen()表示数组长度 不包括‘\0’字符串遍历操作方式1for(int i0;a[i] ! \0;i){ //!\0’判断是否是最后一个字符printf(%c\n,a[i]);}方式2int lenstrlen(a);//提前计算数组长度 strlen() 5for(int i 0;ilen;i){ //!\0’判断是否是最后一个字符printf(%c\n,a[i]);}计算字符串长度逆转字符串四、数组1.含义存储相同数据类型的一块连续的内存空间不同的数据类型在内存当中的存储形式不一样2.创建方式1一维数组//三段式结构的普通数组方式1指定大小同时初始化// 完整初始化int arr[5] {1,2,3,4,5};// 部分初始化剩下元素自动赋值0int arr[5] {1,2};// arr {1,2,0,0,0}方式2省略数组长度编译器自动计算长度 必须带初始化列表不能直接int arr[];int arr[] {10,20,30,40};// 数组长度自动为4方式3只定义数组不初始化局部变量⚠️垃圾值int arr[6];//局部数组不初始化里面是随机垃圾值//全局数组不初始化默认全部为0 ⚠️局部变量数组如果不初始化内部值是内存残留的随机数一定要注意。方式 4动态数组堆上malloc#include stdlib.hint n 10;int *arr (int *)malloc(sizeof(int)*n);//使用完必须释放free(arr);arr NULL;注:int arr[5]; //长度5下标0~4int arr[]{1,2,3};int brr[10];arrbrr; 报错 在C语言当中我们不能直接赋值 三段式- 数组下标从0开始越界不会报错但是乱读内存严重bug- 数组的大小在创建之前必须提前确定好因为数组是一块连续的内存空间- 数组名字是首元素地址数组传参本质传地址。2二维数组//方式1完整初始化int a[2][3] {{1,2,3},{4,5,6}};//方式2省略内层大括号int a[2][3] {1,2,3,4,5,6};//部分初始化剩余补0int a[2][3] {{1},{2}};//⚠️二维数组行可以省略列不能省略int a[][3]{1,2,3,4,5,6}; //合法自动算出2行//int a[2][]{...}; //编译报错列数不能丢例例3 计算数组长度1️栈上局部数组写在函数里面int arr[10]int arr[10] {1,2,3,4,5};//公式总字节 / 单个元素字节int len sizeof(arr) / sizeof(arr[0]);printf(%d, len); //输出10 sizeof(arr)得到整个数组全部字节。 sizeof(arr[0])取第一个元素的大小比写sizeof(int)更通用换类型不用改代码。✅ 简写int len sizeof(arr)/sizeof(arr[0]);注意数组传参给函数之后数组作为函数参数会退化成指针此时sizeof算不出长度C语言只能通过指针传数组 ❗数组传给函数不能靠sizeof获取长度必须把长度当做参数传进去。2️malloc动态数组堆上 ⚠️重点int *p (int*)malloc(10 * sizeof(int));p只是一个指针变量sizeof(p)拿到的是指针大小64位系统8字节拿不到数组总大小。C语言堆内存不会记录你malloc申请多大解决自己定义变量保存长度int n 10; //手动记录数组长度int *p (int*)malloc(n * sizeof(int));//后续代码直接用n代表数组长度3️字符数组/字符串\0结尾char str[] hello;- sizeof(str)算上末尾\0总字节结果6- strlen(str)string.h统计有效字符不包含\0结果5 strlen遇到\0就停止只适合以\0结尾的字符串。4️二维数组栈上二维数组int arr[3][4];int row sizeof(arr)/sizeof(arr[0]); //行数 3int col sizeof(arr[0])/sizeof(arr[0][0]); //列数4 int**malloc锯齿动态二维数组每行长度不一样每行的长度全部要自己变量记下来不能sizeof。地址有4字节跟8字节两种 32位程序是4字节64位程序是8字节*s1与s1[0]一样易错汇总表数组形式 能否 sizeof 算长度 备注int a[10] 栈数组 ✅可以 函数内部直接用函数参数 void f(int a[]) ❌不可以 退化为指针要传参传入长度int* amalloc(...)堆数组 ❌不可以 自己用变量存大小char str[]abc字符串 ✅sizeof 拿总字节strlen拿有效字符 strlen 需要string.h五、指针补充内存、硬盘的删除大多不是物理擦除数据只是逻辑标记内存切断有效引用硬盘修改文件索引标记空间空闲允许后续新数据覆盖旧内容CPU 寄存器没有引用机制直接用新数据覆盖旧数据。任何类型都有其指针类型int a 10;int *p a;- 取地址*解引用取地址里面的值- int *pp存的是变量地址指针*p等价于a值。 指针变量本身存地址32位4字节64位8字节。 空指针NULL不要解引用空指针。数组名等价首元素地址arr arr[0]例#include stdio.h#include stdlib.h // malloc函数需要这个头文件int main(){// malloc(12): 在堆上申请12字节内存// int占4字节12字节可以存放3个int// (int*)强制转换把malloc返回的void*转为int*指针int* s4 (int*)malloc( 12 );s4[0] 100; // 第1个int赋值100s4[1] 99; // 第2个int赋值99s4[2] 123; // 第3个int赋值123*s4 100; // *s4等价 s4[0]再次给第一个位置赋值100printf(%d\n, s4[0]); // 取下标0printf(%d\n, *s4); // 解引用指针等价s4[0]printf(%d\n, s4[1]); // 取下标1printf(%d\n, s4[2]); // 取下标2return 0;}程序输出结果10010099123六、函数//声明int add(int x,int y);int main(){add(3,5);return 0;}//定义int add(int x,int y){return xy;}- 形参函数内部变量实参传值默认是值传递。 值传递函数内部修改形参外面实参不变想要修改外面变量传指针。示例交换两个数字必须指针void swap(int *a,int *b){int t*a;*a*b;*bt;}七、结构体//创建struct Student{int id;char name[20];};//赋值struct Student s{1001,张三};//访问 .s.id;//结构体指针访问用 -struct Student *ps;p-id;动态的分配内存例链表的构建头插法例链表基本方法#include stdio.h#include stdlib.htypedef struct Node{int value;struct Node *next;}Node;// 创建头结点Node* CreateHead(){Node *head (Node*)malloc(sizeof(Node));head-next NULL;return head;}// 尾插void Insert(Node *head,int value){Node *newNode(Node *) malloc(sizeof(Node));newNode-valuevalue;newNode-nextNULL;Node *curhead;while(cur-next!NULL){curcur-next;}cur-nextnewNode;}// 头插void HeadInsert(Node *head,int value){Node *newNode(Node *)malloc(sizeof(Node));newNode-valuevalue;newNode-next head-next;//将原先的头节点赋给newNode的next域head-next newNode;//head指向newNode}void Print(Node *head){Node *cur head-next;while(cur ! NULL){printf(%d , cur-value);cur cur-next;}printf(\n);}//遍历void printList(Node *head){Node *curhead-next;if(curNULL){printf(链表为空);return;}while(cur!NULL){printf(%d ,cur-value);curcur-next;}printf(\n);}//查找找到返回节点指针找不到返回NULLNode* search(Node *head,int target){Node *curhead-next;while(cur!NULL){if(cur-valuetarget){return cur;}curcur-next;}return NULL;}//修改void update(Node *head,int oldValue,int newValue){Node *curhead-next;while(cur!NULL){if(cur-valueoldValue){cur-valuenewValue;}curcur-next;}}//删除第一个值等于deleteValue的节点void deleteNode(Node *head,int deleteValue){//1. 判断第一个有效节点是否就是要删的if(head-next ! NULL head-next-value deleteValue){Node *del head-next; //保存要删的节点head-next head-next-next;free(del); //释放内存return; //删完直接返回避免继续往下执行}//2. 不是第一个节点往后找cur从第二个节点开始Node *curhead-next;while(cur!NULL cur-next!NULL){if(cur-next-value deleteValue){Node *del cur-next;cur-next cur-next-next;free(del);return; //只删除第一个匹配的删完退出}curcur-next;}//循环跑完没找到printf(要删除的值%d不存在\n,deleteValue);}int main(){Node *head CreateHead();Insert(head,1);Insert(head,2);Insert(head,3);Insert(head,4);printf(尾插);Print(head);HeadInsert(head, 10);printf(头插);Print(head);printList(head);Node *p search(head,2);if(p ! NULL){printf(查找结果找到元素 %d\n, p-value);}else{printf(查找结果未找到\n);}update(head,2,20);Print(head);deleteNode(head,3);Print(head);return 0;}结果尾插1 2 3 4头插10 1 2 3 410 1 2 3 4查找结果找到元素 210 1 20 3 410 1 20 4例有序二叉树基本方法#include stdio.h#include stdlib.h// 结构体定义typedef struct Tree {int data;struct Tree *left;struct Tree *right;} Tree;//创建新节点Tree* createNode(int value){Tree *node (Tree *)malloc(sizeof(Tree));node-data value;node-left NULL;node-right NULL;return node;}// 二叉搜索树插入返回新的根节点Tree* insert(Tree *root, int value){// 当前位置为空创建新节点返回if(root NULL){return createNode(value);}if(value root-data){root-left insert(root-left, value);}else if(value root-data){root-right insert(root-right, value);}// 等于的情况什么都不做不插入重复值return root;}//查找结点Tree* search(Tree *root,int value){if(root NULL){return NULL; //没找到返回NULL}if(root-datavalue){return root; //找到返回节点地址}if(valueroot-data){return search(root-left,value);}else{return search(root-right,value);}}//找BST最小结点最左下角Tree* littleNode(Tree *root){if(root NULL){return NULL;}if(root-left NULL){return root;}return littleNode(root-left);}//先序遍历测试void firstOrder(Tree *root){if(root ! NULL){printf(%d , root-data);firstOrder(root-left);firstOrder(root-right);}}//中序遍历测试void inOrder(Tree *root){if(root ! NULL){inOrder(root-left);printf(%d , root-data);inOrder(root-right);}}//后序遍历测试void afterOrder(Tree *root){if(root ! NULL){afterOrder(root-left);afterOrder(root-right);printf(%d , root-data);}}//广度优先遍历void levelOrder(Tree *root){if(root NULL) return;Tree* queue[100];int front 0, rear 0;queue[rear] root;while(front rear){Tree* cur queue[front];printf(%d , cur-data);if(cur-left ! NULL)queue[rear] cur-left;if(cur-right ! NULL)queue[rear] cur-right;}}//删除节点Tree* deleteNode(Tree *root,int value){if(rootNULL){return NULL;}if(valueroot-data){root-leftdeleteNode(root-left,value);}else if(valueroot-data){root-rightdeleteNode(root-right,value);}else{// 找到要删除的节点 root //情况1左子树为空 → 返回右子树叶子 / 只有右孩子if(root-left NULL){Tree *temp root-right;free(root);return temp;}//情况2右子树为空 → 返回左子树只有左孩子else if(root-right NULL){Tree *temp root-left;free(root);return temp;}else{//情况3左右两棵子树都存在//找右子树的最小节点最左下角Tree *minNode root-right;while(minNode-left ! NULL){minNode minNode-left;}//把最小节点的数据覆盖到当前要删的节点root-data minNode-data;//递归删除右子树里面这个最小节点root-right deleteNode(root-right, minNode-data);}}return root;}// 修改二叉搜索树节点oldVal旧值newVal新值Tree* modifyNode(Tree* root, int oldVal, int newVal){//1 删除旧节点root deleteNode(root, oldVal);//2 插入新值函数名是 insert不是 insertNoderoot insert(root, newVal);return root;}//释放整棵树内存void freeTree(Tree* root){if(root NULL) return;freeTree(root-left);freeTree(root-right);free(root);}int main(){Tree *root NULL;root insert(root, 50);root insert(root, 30);root insert(root, 70);root insert(root, 20);root insert(root, 40);Tree *res search(root,20);if(res ! NULL){printf(找到节点data%d\n, res-data);}else{printf(未找到该节点\n);}Tree *min littleNode(root);if(min ! NULL){printf(最小节点值为%d\n, min-data);}printf(先序遍历);firstOrder(root);printf(\n);printf(中序遍历);inOrder(root);printf(\n);printf(后序遍历);afterOrder(root);printf(\n);printf(层序遍历);levelOrder(root);printf(\n);root deleteNode(root,30);printf(删除30后中序遍历);inOrder(root);printf(\n);// 修改测试把20改成25 root modifyNode(root,20,25);printf(将20修改为25后中序遍历);inOrder(root);printf(\n);freeTree(root);return 0;}结果例例例输出200原因1.m1(x1) 是结构体值传递函数内生成副本 k1。k1.s1、k1.s2是新复制出来的值修改它们不会影响外面的x1。k1.s3它是一个指针副本只是复制了地址值k1.s3 和 x1.s3 指向同一块堆内存 (malloc 开辟的空间)。2.k1.s3[0] 200修改的是堆区里面的数据堆内存只有一份所以外面 x1.s3[0] 的值就变成了 200。例
返回列表