【掌握】02- fget()、fputs()函数使用
【了解】03-fgets()和fputs()优缺点
(一)
1、fgets() 该函数一个文件操作相关的函数 暂时使用这个函数可以从键盘商接收一个字符串,保存到数组中 原来接收字符串保存到数组中的方法 char str[50]; 1) scanf("%s",str); //缺点:不能接收空格 2) gets(str); //优点:可以接收空格 //会有一个警告,不安全的 //不安全:比如数组长度是50,如果我们输入的 //的长度正好是50个,此时把50个字符全部存到 //数组中,存在问题 因为没有空间存放字符串结束符 fgets()是一个安全的字符串接收的函数 char ch[5]; //如果使用fgets此时数组中最多存放4个可见字符 //会自动的把数组的最后一个元素存放\0 fgets()使用格式: fgets(数组名,数组长度,stdin); fputs();也是一个文件操作相关的函数 格式: fputs(数组名,stdout);
1 #include <stdio.h> 2 #include <string.h> 3 4 int main(int argc, const char * argv[]) { 5 6 char ch[5]; 7 //fgets从输入缓冲区读取一个字符串保存到字符数组中 8 //当输入的字符串的长度大于数组的长度,此时fgets会自动的 9 //把数组的最后一个元素变成\0 10 //当输入的字符串的长度小于数组长度的时候,fgets还会接收回车 11 12 fgets(ch, sizeof(ch), stdin); 13 14 printf("---%s\n",ch); 15 19 return 0; 20 }
fdsgdssafadg
---fdsg
ds ---ds
//此处有一换行
(二)
1 #include <stdio.h> 2 #include <string.h> 3 4 int main(int argc, const char * argv[]) { 5 6 char ch[5]; 7 //fgets从输入缓冲区读取一个字符串保存到字符数组中 8 //当输入的字符串的长度大于数组的长度,此时fgets会自动的 9 //把数组的最后一个元素变成\0 10 //当输入的字符串的长度小于数组长度的时候,fgets还会接收回车 11 12 fgets(ch, sizeof(ch), stdin); 13 14 // printf("---%s\n",ch); 15 16 17 //\n 它的ascii码值10 18 for (int i=0; i<5; i++) { 19 20 printf("%d\t",ch[i]); 21 } 22 23 return 0; 24 }
sss 115 115 115 10 0 //由此可以看出回车(就是\n)的ascii码值是10
(三)
#include <stdio.h> #include <string.h> int main(int argc, const char * argv[]) { char ch[5]; //fgets从输入缓冲区读取一个字符串保存到字符数组中 //当输入的字符串的长度大于数组的长度,此时fgets会自动的 //把数组的最后一个元素变成\0 //当输入的字符串的长度小于数组长度的时候,fgets还会接收回车 fgets(ch, sizeof(ch), stdin); //去掉多余的\n if(ch[strlen(ch)-1]=='\n') ch[strlen(ch)-1]='\0'; printf("---%s\n",ch); return 0; }
ss ---ss
//现在没有空行了
(三)
#include <stdio.h> #include <string.h> int main(int argc, const char * argv[]) { char ch[5]; fgets(ch, sizeof(ch), stdin); //fputs是不会自动换行 //fputs也不能进行格式化的输出 //puts 可以自动换行 fputs(ch, stdout); return 0; }
saad
saad
【了解】4-const关键字介绍及优点
(一)
const使用的地方 1)修饰变量 2)修饰指针变量 3)修饰数组
#include <stdio.h> /** * const修饰变量,使得变量变成常量 */ void test1(){ //使用const修饰变量a const int a = 10; printf("%d\n",a); //10 //a = 200;//read-only //报错了,不能再次这样赋值了 printf("%d\n",a); //200 } /** * 用指针强制修改const常量 */ void test2(){ //使用const修饰变量a const int a = 10; printf("%d\n",a); //10 int *p = &a; // *p = 100; //强制修饰常量 这种修改的方式是无解的,因为内存地址都是一样的,但值的确修改了,就知道能这样改,不必深究 printf("%d,%d\n",a,*p); // 10,100 } int main(int argc, const char * argv[]) { test1(); test2(); return 0; }
(二)
#include <stdio.h> int main(int argc, const char * argv[]) { int a = 10; int b = 20; //定义个指针变量,p指向a //此处的const的作用是指针变量p的指向可以改变,但是指向的变量的值 //不能改变的 //1、const修饰的指针变量指向可变,指向的变量的值不可变 const int *p = &a; //给p初始化 p = &b; //重新修改了p的指向 //*p = 1000; 再重新赋值就报错来 int const *p1 = &a; p1 = p; //p1的指向也是可以改变 // *p1 = 100; p1指向的变量的值是不能改变的 //2、const修饰的指针变量,指针变量指向的变量值可以变,指向不能变 int * const p2 = &a; *p2 = 2000; //值可以改变 //p2 = &b; //p2的指向不能变 //3、const修饰的指针变量的指向和值,都不能改变 const int * const p3 = &a; // p3 = &b; //只想不能变 // *p3 = 100; //指向的变量的值也不能变 /* 记忆技巧: 看 const 和 * 的位置 如果 const 在 * 的左侧 表示指针变量指向的变量的值不能变,但是指向可以改变 如果 const 在 * 的右侧 表示指针变量指向的变量的值可以改变,但是指向不可以改变 如果 const 出现在 *的两侧,表示指针变量的指向和值都不能改变 */ printf("a = %d\n",a); return 0; }
【理解】5-内存管理的概念和内存分区
【掌握】6-常见的内存分配函数
C语言中提供了3个动态内存分配函数: 1)malloc 函数 格式: void * malloc(unsigned size); 从内存的堆区分配大小为size个字节的连续的内存空间 如果内存分配成功 返回内存的首地址 失败 NULL
2)callloc
3)realloc
#include <stdio.h> #include <stdlib.h> /** * malloc函数的使用 */ void test1(){ //从内存中申请一块内存空间,可以存储4个整数 // = 赋值,要求等号的左右两侧的类型要一致 //p中存放的事新申请的内存空间的首地址 //注意:malloc 申请的内存空间,如果我们不赋值? // 是垃圾数 int *p = (int *)malloc(4*sizeof(int)); //16个字节 //使用一个函数给malloc申请的空间进行初始化 // memset(p,'a',16); if (p!=NULL) { //申请成功做的事情 *p = 10; *(p+1) = 100; *(p+2) = 1000; *(p+3) = 10000; //存放4个整数 }else{ //内存申请失败 printf("内存申请失败!\n"); } for(int i=0;i<4 ;i++){ printf("%d\t",*(p+i)); } } int main(int argc, const char * argv[]) { test1(); return 0; }
10 100 1000 10000
(二)
void test1(){ //从内存中申请一块内存空间,可以存储4个整数 // = 赋值,要求等号的左右两侧的类型要一致 //p中存放的事新申请的内存空间的首地址 //注意:malloc 申请的内存空间,如果我们不赋值? // 是垃圾数 int *p = (int *)malloc(4*sizeof(int)); //16个字节 //使用一个函数给malloc申请的空间进行初始化 memset(p,'a',16); //(空间,内容,空间大小)赋值内容的目的是空间里不是一个随机数,好像存的只能是字母,或者数字 对应的阿斯克吗 for(int i=0;i<4 ;i++){ printf("%c\t",*(p+i)); } } int main(int argc, const char * argv[]) { test1(); return 0; }
a a a a
1667457891 1667457891 1667457891 1667457891 //改成99,下面是%d
c c c c //改成99 ,下面改成%c
(三)
#include <stdio.h> #include <stdlib.h> void test2(){
//callloc 分配指定块数和长度的内存空间 //格式:calloc(块数,长度) //分配了4块,每一块内存长度为4的内存空间 //他们的地址也是连续的 //注意事项: //calloc 它使可以帮我们自动的初始化为0 int *p = (int *)calloc(4, sizeof(int)); //16个字节 if (p!=NULL) { //申请成功做的事情 *p = 10; *(p+1) = 100; *(p+2) = 1000; *(p+3) = 10000; //存放4个整数 }else{ //内存申请失败 printf("内存申请失败!\n"); } for(int i=0;i<4 ;i++){ printf("%d\t",*(p+i)); } } int main(int argc, const char * argv[]) { test2(); return 0; }
10 100 1000 10000
(四)
#include <stdio.h> #include <stdlib.h> int main(int argc, const char * argv[]) { int *p = (int *)malloc(4*sizeof(int)); //16个字节 printf("old %p\n",p); //realloc 函数可以给已经存在的空间扩充大小(如果扩充的空间被其他函数占据,也可能另辟一个空间,将原来空间的数据自动复制过去) p = realloc(p, 40*sizeof(int)); printf("new %p\n",p); //40个内存空间 if (p!=NULL) { //申请成功做的事情 *p = 10; *(p+1) = 100; *(p+2) = 1000; *(p+3) = 10000; //存放4个整数 *(p+39)= 1; printf("%d\n",*(p+39)); }else{ //内存申请失败 printf("内存申请失败!\n"); } for(int i=0;i<4 ;i++){ printf("%d\t",*(p+i)); } return 0; }
old 0x1003031a0 new 0x1003031a0 //这里说明老空间下面的空间并未被其他函数占据 1 10 100 1000 10000
【掌握】07-野指针和内存泄露
1)野指针:所指空间已经释放 ,所谓释放就是其他函数一旦调用到这块内存,这块内存的值就改变而不受保护
2)内存泄露:空间占据内存没有释放, 但没有指针指着
解决方法:在指针p释放之前,先释放堆区中的内存空间
内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。
内存泄漏形象的比喻是“操作系统可提供给所有进程的存储空间正在被某个进程榨干”,最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃。所以“内存泄漏”是从操作系统的角度来看的。
这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有任何一个指针指向它,那么这块内存就泄漏了。
(一)
#include <stdio.h> #include <stdlib.h> int main(int argc, const char * argv[]) { int *p = (int *)malloc(4*sizeof(int)); //16个字节 if (p!=NULL) { //申请成功做的事情 *p = 10; *(p+1) = 100; *(p+2) = 1000; *(p+3) = 10000; //存放4个整数 }else{ //内存申请失败 printf("内存申请失败!\n"); } //应该使用free()函数释放内存空间 //free(要释放的空间的首地址) free(p); //free(p)以后,p是一个野指针 //p = NULL; // 在没有这句话的时候,被释放的内存并没有其他函数去占用,所以还遗留了以前的数据 printf("%d\n",*p); return 0; }
10
(二)
#include <stdio.h> #include <stdlib.h> //分配指定大小的内存空间 int * mall(int n){ return (int *)malloc(n); } int main(int argc, const char * argv[]) { int *x = mall(4); //释放函数中分配的内存 free(x); //for (int i=0; i<1000000; i++) { // 造成严重的内存泄露 // mall(100); // } return 0; }
【了解】08-指针函数概念及定义
指针函数: 返回值是指针的函数 指针函数的定义 指针类型 * 函数名(){ return 地址; }
(一)
#include <stdio.h> //返回的事两个数中大数的地址 //返回的是形参x和 y的中大数的地址 int* max(int x,int y){ printf("行参max x = %p\n",&x); printf("行参max y = %p\n",&y); return x>y?&x:&y; } int main(int argc, const char * argv[]) { //返回a 、b两个数中大数的地址 int a = 3,b = 4; printf("a = %p\n",&a); printf("b = %p\n",&b); int *p =max(a,b); printf("p = %p\n",p); return 0; }
a = 0x7fff5fbff72c b = 0x7fff5fbff728 行参max x = 0x7fff5fbff6fc 行参max y = 0x7fff5fbff6f8 //清楚地看到实数和行参的地址是不一样的 ,证实了调用函数的时候行参变量是新分配的空间,实参和行参只是值传递 p = 0x7fff5fbff6f8
(二)
#include <stdio.h> int *max2(int *x,int *y){ printf("max x = %p\n",x); printf("max y = %p\n",y); return *x>*y ?x :y; } int main(int argc, const char * argv[]) { //返回a 、b两个数中大数的地址 int a = 3,b = 4; printf("a = %p\n",&a); printf("b = %p\n",&b); int *p =max2(&a,&b); printf("p = %p\n",p); return 0; }
a = 0x7fff5fbff72c b = 0x7fff5fbff728 max x = 0x7fff5fbff72c max y = 0x7fff5fbff728 p = 0x7fff5fbff728
//清楚地看到实数和行参的地址是一样的 ,证实了调用函数的时候行参变量不是新分配的空间,实参和行参是地址传递
【应用】09-输出星期
需求:
1) 通过指针函数,输入一个1~7之间的整数,输出对应的星期名。
2) 通过指针寻找给定数组中的最大值
#include <stdio.h> #include <stdlib.h>
char * getDay(int n){ //这是定义一个字符串指针数组 char *days[]={ "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日", }; return n<8&&n>0 ? days[n-1]:"输入错误!"; } int getMax(int *p,int len){ //假设max是第一个元素 int max=*p; for (int i=1; i<len; i++) { //找出最大值 if (*(p+i)>max) { //找到最大值后,把最大值赋值给max max = *(p+i); } } return max; } int main(int argc, const char * argv[]) { printf("%s\n",getDay(1)); int a[10]={123,45,22,111,34,56,888,11,45,109}; int m = getMax(a,10); printf("m = %d\n",m); return 0; }
星期一 m = 888