DATE 16

字符数组和字符串:
String str1 = "hello"; //String数据类型在C语言里面没有,在C++和Java里面是有的;
===》C里面是用字符数组的方式来保存字符串!


strlen(str):表示的字符串的长度;
char str[100] = {0};
'\0'表示的是字符串结束,‘\0’之前的字符是字符串的有效字符:
===》hello world'\0'    ===》‘\0’称为魔数【在C语言里面用来表示特定含义的数】
===》所以strlen是不把'\0'计算在字符串的有效长度里面;
‘\0’在ASCII里面对应的是0 ===》表示的就是什么都不显示;

sizeof(str):表示的字符串在内存中所占字节空间的大小;===》计算'\0';
char str[100] = "hello world"; //sizeof(str) = 100;
char str[] = "hello world"; //sizeof(str) = 5 + 1 + 5 + 1 = 12;  ===》会在最后自动添加'\0'
char a[] = {'h','e','l','l','o',' ','w','o','r','l','d'};//sizeof(a)  = 11; ===》不会自动添加'\0';

char str[11] = "hello world"; 
//strlen(str):从“hello world”中的'h'开始往后依次遍历(一个一个地找),知道找到'\0'为止,那么‘\0’
//之前一直到'h',是strlen(str)的大小,因为定义的是str[11]正好能够存放“hello world”,字符数组str中没有剩余的
//没有剩余的空间来自动添加'\0',所以会一直往后寻找,直到遇到‘\0’为止,因为'\0'的位置不确定,导致
//strlen(str)的大小是不确定的;

总结一下:字符串的实质是是什么?
有固定开头和结尾的,在内存中连续存放的一串字符;
注意:
1、固定开头;
2、固定结尾:'\0'
3、连续存放:因为字符串在C语言里面是采用字符数组的方式存储===》数组是在内存中开辟的连续空间!


函数:
什么是函数,为什么需要函数,有了函数之后有什么好处,没有函数行不行?
对于函数有什么操作;

1、为什么需要函数?
main()
{
XXX;
XXX;
}
自然段;----》章 ----》卷或者季;
盗墓笔记第一季:
第一章;
第一段;
第二段;
第三段;
第二章;
第三章;
。。。
盗墓笔记第二季;
。。。
盗墓笔记第三季;
。。。
===》采用“分”的思想;

===》编程:采用“分”的思想;===》分成模块 ===》分成函数;

2、什么是函数;
3、有了函数之后有什么好处
①代码更加精简,是程序模块化;
②函数可以复用;
4、没有函数行不行;
行!
5、对于函数要掌握哪些知识点:
①定义;
首先要明确,要定义的函数应该具备什么功能;
如何定义:
函数的返回值类型 函数的名字(函数的参数列表)
{
函数的代码块;
}

注意:
函数可以没有参数列表,但是()不能丢;
最终返回的数据类型一定要和“函数的返回值类型 ”一样!


②声明;
内部声明和外部声明;
内部声明:
为什么要声明:
编译器在进行编译的时候是从上往下进行编译的!
如果函数的定义在函数调用之前,可以不进行声明;
如果函数的定义在函数调用之后,不进行声明,会报警告!===》有些编译器不会报警告!
声明的作用:
告诉编译器,函数的名字是什么,函数有没有参数列表,如果有的话,应该给函数传递什么类型的参数,
传递多少个参数!===》告诉编译器,函数的正确使用方法;
当调用函数的时候,会根据函数的声明,来判断,函数调用的是否正确!
如何声明;
最简单的方法:把函数定义的第一行复制粘贴,然后在后面加个分号;

外部声明:extern

③调用;
函数的调用过程其实就是实参传递到形参的过程!
===》实参和形参;
实参:函数调用的时候,给函数传递的真实值;
形参:函数定义的时候的函数列表;

如何调用:
实参给形参传递的时候,是把实参的数“拷贝”一份给形参;
===》既然是拷贝,传递的方向,只能是实参--->形参,不能够形参--->实参!

函数的形参所占用的空间,是在函数调用的时候分配的,没调用的时候不分配空间;
调用结束函数形参所分配的空间由系统自动释放,程序员不用管;

函数调用的时候是按照顺序调用的,不是按照名字!实参的名字跟形参名字没有半毛钱关系!

===》第一个实参的数据拷贝给第一个形参;第二个实参的数据拷贝给第二个形参,。。。。

实参和形参的类型要一样!

/*****************************************************************
功能:输入一个字符串,要求输出该字符串中最长的数字字符串;
例子:
输入:abcd123ab1234567
输出:1234567


想法:
第一步:获取字符串:
char str[100] = {0};
获取字符串保存到str里面;

第二步:处理:
char max[100] = {0}; //专门用来保存最长的数字字符串;
char temp[100] = {0}; //专门用来从str里面解析出来的字符串;

temp[]  = "123";

if( strlen(temp) > strlen(max))
{
把temp里面的字符串保存到max里面,对max进行覆盖!
}

第三步:最后输出;
max


str[i] >= '0' && str[i] <= '9'
*****************************************************************/

函数的其他知识点:
1、函数的嵌套调用:函数A里面调用函数B; ==》一个函数里面调用其他函数!
2、函数的递归调用:也就是函数自己调用自己;
n!
3、return:结束当前函数的执行,返回到函数调用的地方!
   exit(); 结束当前程序的执行;
4、函数的执行有两个目的:
①只追求过程,不强调结果;===》void(空),来做函数的返回值类型;
②强调结果 ===》需要有返回值! 返回值类型不要用void;


指针:
什么是指针?为什么需要指针?有了指针之后有什么好处?没有指针行不行?
1、什么是指针?
指针的全称“指针变量”,本质上也是一个变量!跟int cha float....没什么区别,都是用来保存数据!
int a; //12345;
char c; //'a'
float fl; //1.2
既然指针是变量,所以也是用来保存数据!====》保存的是其他变量的地址!【跟普通类型的变量不一样的地方】
比如,一个指针变量可以保存a的地址,或者是c的地址!

2、为什么需要指针变量;
java里面没有指针;===》进行了一层封装===》“糖衣”

3、有了指针变量有什么好处?
提高效率,也可以对相应内存地址进行操作;
4、没有指针行不行呢?

首先牢牢记住一句话:指针的本质是变量,跟普通的数据类型的变量没有本质差别!
===》对于变量有哪些操作:
1、定义;
int a;
int *p; //在你定义的时候,*表示的是指针变量;
==》
数据类型 *指针变量名;
//char *p;

int *p;
int (*p)(int); //int (*)(int);

指针的类型:
int *; //把指针变量的名字去掉,剩下的就是指针的类型;
指针指向的类型;
int; //把指针变量的名字和*去掉,剩下的就是指针指向的类型;


2、初始化;
什么叫初始化?
就是给变量赋初值!
指针变量初始化:
===》就是给指针变量赋初值!
===》指针变量保存的是什么? ===》地址!
===》也就是给指针变量赋“初始的地址”; 或者就是将地址保存到指针变量里面去!
===》怎么赋地址?
===》①将其他一个变量的地址保存到指针变量里; //int *p = &a;
===》②将NULL赋值给指针变量; ===》NULL表示的是0地址! //int *p = NULL;

//首先把下面四个搞懂!能够结合画图来理解!
printf("a = %d\n",a);
printf("&a = %p\n",&a);
printf("p = %x\n",p);
printf("&p = %p\n",&p);


printf("*p = %d\n",*p); //p里面保存的是a的地址,*是取值运算符,也就是将p里面保存的地址所对应的
//空间里面的值取出来!


3、赋值;
int a = 10;
int *p;

p = &a; //相当于指针变量p和a绑定了;===》从现在开始,p里面保存的值是a的地址;


指针为什么要有类型?
因为指针指向的对象是有类型的!
所以想通过指针变量来获取指针指向的对象里面的数据的话,我得从那个地址开始连续取多少个字节!
===》指针要有类型!
int *p1; //保存的是int变量的地址;
char *p2; //保存的是char变量的地址;

printf("*p1 = %d\n",*p1);
printf("*p2 = %d\n",*p2);

4、运算;
对于普通的int变量,可以进行加减乘除操作;
==》对于指针变量?
char str[] = {"hello"};
char *p = str;

5、作为函数的参数;

指针和其他知识点的联系:
一、指针和数组:
①数组指针:首先是个指针 ===》 这个指针保存的是什么地址? ===》保存的是数组的地址;

   小结:
1、一维数组名代表数组首元素的地址;
//char str[5] = "hello";
str + 0 == &str[0];
str + 1 == &str[1];
str + 2 == &str[2];

2、对一维数组的数组名进行取地址操作,代表的是数组的地址;
&str + 1 == str[4]后面的那个地址! //跳过了整个数组!


char *p;
p = str;  //或者&str[0];
// p = &str; //错误,虽然数值和str以及&str[0]相等,但是含义不一样!

for(i = 0; i < 5; i++)
{
printf("*(p + %d) = %c\n",i,*(p + i));//e
}
小结:
如果p保存的是字符数组中的首元素地址;
p ===》指向a[0] ===》说明保存的是a[0]的地址;
p + 1 ===》指向a[1] ===》说明保存的是a[1]的地址;
p + 2 ===》指向a[2] ===》说明保存的是a[2]的地址;
p + 3 ===》指向a[3] ===》说明保存的是a[3]的地址;
p + 4 ===》指向a[4] ===》说明保存的是a[4]的地址;

*p 等于 a[0];
*(p + 1)等于a[1];
*(p + 2)等于a[2];
....

===》引用数组元素有哪些方法:
1、下标法;a[i];
2、指针变量:  *(p + i) = a[i];
3、数组名:   *(a + i) = a[i];

//以下是2018.5.27
一维数组的数组名代表的是数组首元素的地址;
对一维数组名取地址,代表的是数组的地址;

二维数组的数组名代表的是第一个一维数组的地址;
对二维数组名取地址,代表的是二维数组的地址!

int a[3][2];
a[0]:是二维数组中第一个一维数组的数组名;
a[1]:是二维数组中第二个一维数组的数组名;
a[2]:是二维数组中第三个一维数组的数组名;

a : 第一个一维数组的地址;
a + 1: 第二个一维数组的地址;
a + 2: 第三个一维数组的地址;

②指针数组:首先是个数组,每个数组元素都是指针,也就是地址!

char *str[] = {"hello","world","linux"};

指针和函数的联系:
①指针函数:是一个函数,返回值是一个指针变量! //在函数的返回值方面,指针和函数的联系;

int *fun()
{

}
②函数指针:是一个指针,这个指针指向一个函数; //在函数名方面,指针和函数的联系;
对于函数的第二种调用方法;
首先你要知道的是:函数的地址,就是函数名!

1、定义一个函数指针;
怎么定义:
直接把函数的声明赋值过来,然后将函数名换成(*p);

2、让函数指针指向某个函数,也就是给函数指针绑定函数;
p = 函数名; //

3、通过函数指针来调用函数;
将通过函数名调用的方法中,用(*p)来代替函数名;





③指针作为函数的参数; //函数的参数方面,指针和函数的联系;
int swap(int a,int b);
{
printf("a = %d\tb = %d\n",a,b);
int temp = 0;

temp = a;
a = b;
b = temp;

printf("a = %d\tb = %d\n",a,b);
}

int main()
{
int x = 10;
int y = 20;

swap(x,y);

printf("x = %d\n",x);
printf("y = %d\n",y);

return 0;
}

//数组名作为函数的参数;
// void fun(char *p) //既然实参是地址,那么形参来接收的应该定义一个变量能够
//保存传过来的地址! ===》指针变量!
void fun(char a[]) //数组名作为函数的形参,退化为指针处理!
{ //为什么需要[],如果没有[],就变成了char a ==》就是一个字符
//所以这里的[]表明是一个数组;

}

int main()
{
char str[] = "hello";

fun(str); //将数组名作为函数的实参,传递给参数
//因为数组名是数组首元素的地址,所以就相当于传递了地址;

return 0;
}

指针和字符串的联系:
char str[] = "hello";//这里面的hello是字符数组里面的;
char *p = "hello"; //这里的"hello"是字符串常量;
//字符串常量保存在.text段;不能够改变;
//用p来保存字符串常量的首地址!
*(p + 2) = 'q'; //试图修改常量区的内容!会报错!

//小程序:
// strlen:计算字符串的长度;
//my_strlen:计算字符串的长度;


char str[] = "ncjdncjdcnd";
int len = my_strlen(str);
printf("len = %d\n",len);


野指针和段错误;
什么叫野指针:
p保存的是int a的地址,那么就说 p指向a;

1、当指针指向不明确的时候;
int *p; //此时不知道p保存的是什么地址!
*p = 10;
2、指针保存的地址不存在!
int *p = 0x12345678;

2、指针指向明确,地址也存在,但是我们指向的空间没有操作权限;
int *p = NULL; //指向了0地址;但是0地址受系统保护,没有操作权限!
int b;
p = &b;

*p = 10;

满足上面三个条件之一,我们就称这个指针为野指针!

野指针会带来什么危害:
1、段错误!
其实就是地址错误:要么是地址不存在,要么是地址存在,而我们没有操作权限!
2、暂时没有危害,程序正常运行!
但是会在程序里面隐藏bug,以后在某个时间点会爆发!
3、程序直接崩溃!

怎么避免野指针?
归根到底就是养成良好的编码习惯;
1、定义指针的时候,随后给指针初始化为NULL;
2、当你使用指针要判断一下指针是否为NULL;
3、使用完指针的时候,要随后把指针变量指向NULL;

接下来几个关键字:
1、static:静态变量;
作用:
1、修饰局部变量的时候,可以延长局部变量的生命周期!
2、修饰全局变量或者函数的时候,可以起到“隐藏”的功能,进而达到只能够在本文件
中使用的目的,不能在别的文件中使用;


当定义一个静态变量,而没有初始化,系统会自动初始化为0;
static int num;
printf("num = %d\n",num);


2、const:修饰变量称为“常变量”;
int sum = 10;
int len = 9;

int const *p  = &sum;

sum  = 18;
p = len;


3、register: 寄存器变量
修饰局部变量,不能够修饰全局变量和函数;
register int num = 10;
请求编译器,将register修饰的变量保存到CPU内部寄存器上!而不是保存到内存中!
进而提高读取效率!
CPU:皇帝;
内存:大臣;
寄存器:小太监;
既然是请求,编译器可以拒绝!因为CPU内部的寄存器数量有限!
既然register修饰的变量保存到CPU内部寄存器上!【所以变量的类型
CPU要能够支持!】而不是保存到内存中!【所以不能够用取地址符(&)来变量的地址】


4、extern;
5、volatile:防止编译器优化处理!


内存管理:
1、栈空间(stack):保存的是函数的形参,局部变量和自动变量;
特点:
1、自动管理:有系统自动给函数形参,局部变量和自动变量分配空间;
执行结束之后,系统自动释放,不用程序员自己去管;
2、反复使用:栈空间比较小;
3、栈空间是脏的;int  num;printf("num");
4、临时性;

2、堆空间(heap):用malloc 、realloc、calloc来申请空间;
特点:
1、灵活;
2、内存空间比较大;
3、需要手动申请和释放;
4、堆空间是脏的;
5、临时性;

怎么用:
//第一步:申请;如果申请成功返回的是申请到的堆空间的首地址;
char *str = (char *)malloc(100);
//第二步:检查是否申请成功;
if( str == NULL )
{
失败;
return;
}
//第三步:使用;
*(str + 1) = 'h';
str++;
//第四步:使用完释放;
free(str); //里面的参数是申请到首地址;

如果不手动释放,在程序结束之前这块空间就【内存泄露】;
当程序结束之后,由系统回收!

3、数据段:保存的是全局变量和被初始化为非0的静态变量;
4、.bss段:保存的是初始化为0的静态变量或者没有初始化的静态变量;
5、代码段(.text):函数的指令,以及字符串常量!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值