您的位置:首页 > 编程语言 > C语言/C++

黑马程序员--C语言自学笔记---12fgets、fputs、const、结构体

2015-12-22 22:03 351 查看

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

1. fgets()函数的使用

1)fgets的原型为char * fgets(char *s,int n ,FILE * fp);

fgets相比于gets有一个显著的差别就是fgets会将行末的换行符算到读入的字符串里面。所以相同且正常(输入无错误,缓冲区够大)的情况下,fgets读入的字符串会比gets在末尾’\0’前面多一个换行符,行长度超出缓冲区大小时只读入前n-1个字符。

2)fgets是一个文件操作相关的函数

暂时使用这个函数可以从键盘接收一个字符串,保存到数组中。

3)其他接收字符串保存到数组中的方法

Char str[50];

① scanf(“%s”,str); 缺点:不能接收空格

② gets(str); 优点:可以接收空格

缺点:会有一个警告,不安全。

不安全是指比如数组长度是50,如果输入的长度正好是50个,此时会把50个字符全部存到数组中,存在问题,因为没有空间存放字符串结束符’\0’

4)而fgets()是一个安全的字符串接收函数

char ch[5]; 如果使用fgets,此时数组中最多存放4个可见字符,会自动的把数组的最后一个元素存放’\0’

fgets()使用格式 fgets(数组名,数组长度,stdin)stdin表示从输入缓冲区中读取字符串存放到数组中,比如:charch[5];fgets(ch,sizeof(ch),stdin);

① 输入的字符串的长度大于数组的长度,fgets会自动把数组的最后一个元素变成\0。

② 当输入的字符串的长度小于数组长度时,fgets会把回车符接收到数组中。

2. fputs()函数的使用

1) 也是一个与文件操作有关的函数

格式为:fputs(数组名,stdout),比如:

char ch[5]={“abcd”};fputs(ch,stdout);

2) fputs()不会自动换行,也不能进行格式化的输出,而puts()可以自动换行

3. const关键字

1) const是一个类型修饰符,使用const修饰变量可以让变量的值不能改变,也就是说,用const修饰的变量会变成一个常量

一个用指针强行修改const变量的无解问题:

const int a = 5;

int *p = &a;

*p = 10;

然后执行printf(“a=%d,*p%d\n”,a,*p);

结果为a=5,*p=10;

2) 可以修饰指针变量

注意:以下所说的指针变量指向的值能不能改变的意思是说,能不能通过这个指针来改变他所指向的变量的值,但是这个变量还是可以改变的。即:

只跟是否可以通过对*p赋值来改变该变量的值有关,而对a都是可以赋值的,因为a还是一个变量,可以随便来赋值。

int a=5;

int b=10;

const int *p=&a;

*p=1;这时错误的

p=&b;这是正确的

a=1;这是正确的

并不是把指向的变量变为常量。

① 第一种,表示指针变量指向的值不能改变,但指向可以改变:

int a=5;

int b=10;

const int *p=&a;

*p=1;这时错误的

p=&b;这是正确的

② 第二种,表示指针变量指向的变量的值可以改变,但指向不能改变

int a=5;

int b=10;

int * const p=&a;

*p=1;这时正确的

p=&b;这是错误的

③ 第三种,表示指针变量指向的变量和它的指向都不能改变

int a=5;

int b=10;

const int * const p=&a;

*p=1;这时错误的

p=&b;这也是错误的

3) 可以修饰数组

4. 内存管理

内存管理是指,软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。我们要管理的是用户申请的内存空间。

内存分配方式

1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时,这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

3) 在堆上分配,动态分配内存。程序在运行的时候用malloc或new申请人以多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也很多。

内存分区,地址从高到低为:栈区,堆区,BSS段,数据段,代码段



1) BSS段,用来存放程序中未初始化的全局变量和静态变量

2) 数据段,存放的是已经初始化的全局变量和静态变量,也成为常量区。

3) 代码段,存放程序执行代码的一块内存区域。

4) 堆,存放被动态分配的内存段

5) 栈,用户存放程序临时创建的局部变量

5. 内存分配函数

1) malloc(头文件stdlib.h)

使用方法为void*malloc(unsigned size),size是指分配内存的字节数。

从内存中的堆区分配大小为size字节的连续内存空间,如果内存分配成功,返回内存的首地址;失败的话,会返回NULL;

int *p = (int *)malloc(4*sizeof(int));

if(p!=NULL){

*p = 10;

*(p+1) = 11;

*(p+2) = 12;

*(p+3) = 13;

}else{

printf(“申请失败!\n”);

}

注意:如果malloc分配的内存不初始化的话,存放的是垃圾数,可以使用memset()函数来进行初始化。

Memset(p,初始化的值,空间大小)

2) calloc函数

格式为:calloc(内存块数,长度),分配指定块数和每块长度的空间,地址也是连续的,并且会自动初始化为0.

calloc(4,sizeof(int));分配了4块空间,每块长度都是sizeof(int),

3) realloc函数

可以给已经存在的空间进行扩展大小

int *p=malloc(4*sizeof(int)); 假设首地址为0x1000

*p=1;*(p+1)=2;*(p+2)=3;*(p+3)=4;

p = realloc(p,40*sizeof(int));

分配原理:以上面这个为例,首先realloc会在原有空间后面(0x1016)查看是否有足够的空间(36*sizeof(int))可以继续分配,如果有则直接进行扩展;如果没有,则会在内存中其他地方分配足够大的空间(40*sizeof(int)),所以地址会改变,需要重新用P来接收,而且,原有内存空间的数据会复制到新空间的开始位置。*p,*(p+1),*(p+2),*(p+3)的值依然是1,2,3,4.

6. 野指针和内存泄露

1) 如果动态申请内存成功后,使用完后不主动进行释放,当程序执行完,指向该内存空间的首地址的指针已经被释放,而该空间未被释放,就会造成该部分空间无法访问,造成内存泄露。如果申请的内存空间先被释放,而指针还未被释放,则指针变为野指针。

2) 使用free函数来释放内存空间。

int *p=(int *)malloc(100);

*p=1;

free(p);

p=NULL;

free(内存的首地址);释放内存空间,防止内存泄露这里为free(p),此时p变为野指针。

执行p=NULL;重新把野指针赋值为0,让其不再是野指针

7. 指针函数

返回指针值的函数称为指针函数

1) 类型说明符 * 函数名(形参表){函数体}比如:

int *sum(int a,int b){
int sum;
sum = a+b;
return  ∑
}


2) 用来求两个数的大数的地址

int *max(int x,int y){
return *x>*y?x:y;
}


3) 定义指针函数根据输入的数字,输出对应的星期

char * getDay(int n){
char *days[]={
“星期一”,
“星期二”,
“星期三”,
“星期四”,
“星期五”,
“星期六”,
“星期日”};
return 0<n&&n<8?days[n-1]:”输入错误”;
}


4) 用指针做参数,求最大值

//用指针做形参求最大值
int max(int *p,int len){
int max=*p;
for (int i=1; i<len; i++) {
if (*(p+i)>max) {
max = *(p+i);
}
}
return max;
}


8. 函数指针

1) 在C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。

我们可以把函数的这个首地址(或称为入口地址)赋予一个指针变量,是该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针变量称为“函数指针变量”。

2) 定义:函数的声明为int sum(int a,int b);

----->函数指针定义为:int (*p)(int a,int b)

定义了一个函数指针p,p可以存放 返回值是int类型,并且有两个形参,形参的类型还都是int类型的函数的地址

3) 初始化

p=sum; sum中存放的就是sum函数在内存中的首地址

4) 定义函数指针时,可以省略函数形参名int (*p)(int ,int),类型不能省

5) 函数指针使用

假设有两个结构相同的函数为intsum(int a,int b)和int sub(int a,intb),定义一个函数指针为int(*p)(int ,int),可以通过修改一个分支语句来调用不同的函数:如下

switch(1){

case 1:

p=sum;

break;

case 2:

p=sub;

break;

}

int rs = p(3,1);

只要改变switch中的数字,就可以调用不同的函数,这就是函数指针的使用之处。

个人观点:感觉实际应用中这种写法没多大必要,想调用不同函数只要使用相应的函数名就可以了,使用函数指针的话,反而增加了程序的代码量

6) 注意:

① 函数指针变量不能进行算数运算,这跟数组指针变量不一样,数组指针变量加减运算可以让指针移动到某个数组元素前面,函数指针的移动没有任何意义。

② 函数调用时,可以写为p(1,2)格式,也可以写为(*p)(1,2)格式,但第二种格式的括号不能少,这个*不应该理解为求值运算,只是一种表示符号。

9. 构造类型

有一个或者多个已经定义类型的元素用够早的方法,构造新的类型

有:数组、结构体、联合

10. 结构体

由相同类型或者不同类型的数据用构造的方法,构造新的类型

1) 定义格式

struct结构名{

成员列表

};

比如定义一个学生结构体↓↓↓

struct Student{

int num;

char *name;

char *sex;

};

结构体变量的三种定义方法↓↓↓

① 结构体定义完成以后,计算机并不会给结构体分配内存空间

② 会在定义结构体变量后,给结构体分配存储空间

以上面学生结构体为例定义结构体变量的方法↓↓↓

2) 先定义结构,再说明结构变量

格式为:struct 结构体名 结构体变量名;

定义结构体变量为struct Student stu1,stu2,stu3;可以看到,还能同时定义多个结构体变量。

3) 定义结构体的同时定义结构体变量

struct Student{

成员列表

}stu4,stu5,stu6;这样便在定义结构体的同时定义了三个结构体变量。

4) 使用匿名结构体来定义结构体变量

格式为:struct{

成员列表

}结构体变量名1,结构体变量名2,结构体变量名3…;

比如:

struct{

int num;

char *name;

char *sex;

}stu7,stu8,stu9;

这样便用匿名的方式定义了三个结构体变量。

11. 结构体变量中成员的访问方法

通过使用’.’来访问结构体成员。

struct Student{

int num;

char *name;

char *sex;

};

struct Student stu;

//通过使用‘.’来访问结构体变量中的成员。

stu.num = 10;

stu.name=”张三”;

stu.sex = “男”;

12. 结构体变量的初始化

struct Student{

int age;

char name[20];

};

1) 先定义结构体变量,后初始化

struct Student stu1;

stu1.age=19;

stu1.name=”zhangsan”;注意:这样初始化字符串是错误的。

这种写法就类似于char ch[5];ch=”abcd”;因为数组名为常量,所以不能直接赋值。

对于字符串的初始化,可以使用strcpy函数

strcpy(stu1.name,”zhangsan”);这样写就没问题了。

另外,如果结构体中定义的不是数组而是char*name;则name在初始化后是可以改变的,因为它是一个指针变量。

2) 定义结构体变量的同时,初始化。

struct Student stu1={19,”张三”};

这里的字符串的初始化方式就类似于,charname[12]=”张三丰”;所以也是没问题的。

补充一个编码问题:

UTF-8 一个汉字三个字节,xcode的默认编码为UTF-8

GBK(GB2312) 一个汉字两个字节

3) 定义结构体变量的同时,只给指定元素进行初始化

struct Student stu3={.name=”张三丰”};

13. 结构体变量占用存储空间的大小

1) 求模数

2) 考虑对齐,计算大小

14. 结构体作用域

1) 全局结构体:定义在函数外部的结构体

2) 局部结构体:定义在函数内部的结构体
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: