extern定义函数为外部函数,随时可以调用,一般省略extern为直接定义为外部函数
static定义函数为内部函数,使用时只能在该内部函数所在文件中被调用,别的文件无法直接调用,可间接调用,即在该内部函数所在文件中定义外部函数调用该内部函数,然后在其他文件中调用这个调用了内部函数的外部函数,即可实现调用不在同一文件下的内部函数。
同一个工程下的不同文件可使用内部函数定义多个同名文件,使用时函数互不影响
8-10 内联函数函数被调用时,内部实际操作比较复杂,首先是栈指针指向函数,然后栈指针往下走,走到需要调用另一个函数,再跳到另一个栈指针,再接着往下走,当一个函数大量被调用时,同时也带来了大量的指针操作,浪费很多时间。
在这时候使用内联函数,省去大量函数入栈出栈的操作,执行速度比一般函数快。内联函数实际使用时,就是讲所需要调用的函数内容直接复制到调用函数的位置,进行编译,正常函数是直接跳到所调用的函数里执行这个函数,然后执行完再退出这个函数,然后正常执行主函数下边的内容。
内联函数一般要求尽量短小,效率才会高,太长的内联函数,编译器可能会当做一般函数处理。
提高函数调用的效率
8-12 开发自己的库函数首先当需要给别人开发某些功能,但又不想让别人知道源码,这时候就需要把源码以库文件的形式发送给别人。
静态链接库
静态链接库就是使用时直接把库内部的程序直接拷贝到使用的位置,占用内存比较大。
动态链接库不进行拷贝,使用时才进行入栈出栈操作,动态库的后缀名为.dll
输入函数scanf(),当输入控制符里用逗号隔开,那么在控制台中输入的时候也要用逗号隔开,如果控制符没有隔开,那么输入时可以使用tab,也可以使用空格进行隔开
数组的输入输出都是要用循环进行依次输入输出
数组特殊初始化方式,特殊初始化方式,也叫指定初始化,C99新增的初始化方式,比如指定数组的某一位进行赋值,例如:
int a[10] = {[0] = 1,[9] = 2}
定义了一个a数组,然后仅给a数组的第0位和第9位赋值
数组的大小:
定义int 数组,数组中每个元素占内存为int 所占大小,整个数组所占内存为每搁元素所占内存的总和。
数组名字与数组地址
当数组名用作sizeof()或&的操作数时,sizeof返回的是整个数组的字符长度,&取地址取得也是整个数组的地址,例如:
char a[10];
printf("%d %dn",a,&a[0]);
printf("%d %dn",a+1,&a[1]);
a 是代表整个a数组的地址
&a[0] 同样是代表整个数组的地址
a+1加的是地址指向下一个元素,具体十六进制地址变化多少取决于该元素的大小。
&a[1]是a[1]这个元素的地址
数组的地址和数组首个元素的地址是相等的,但是意义不一样,当地址进行加一操作时,数组的地址加一变化的长度是整个数组的长度,而数组首个元素的地址进行加一操作,地址变化的长度是一个元素的长度。
数组名代表的是首个元素a[0]的地址也就是&a[0],而不是数组的首地址&a
数组名a+1表示下一个数组元素的地址,而&a+1是指向下一个数组的地址
二维数组其实是特殊的一维数组,因为二维数组内部存储的是一维数据
int a[2][3] = {{1,2},{4,5,6}}
这种定义方式,最终的效果是这个二维数组的第一行是1 2 0,第二行是 4 5 6
在从键盘获取数组数值的时候
for(i = 0,i < 2,i++)
for(j = 0,j < 3,j++)
scanf("%d",a[i]+j)
a[i]+j这种获取方式,是地址的形式,和&a[i][j]作用一样
如果使用a[i]+j这种方式,特别要注意不能再a[i]前边加&,如果是首地址加一,那就是直接跳到了下一个行
在二维数组中a[0]代表这一行数组的首地址,然后+j是从首地址往后加一,也就是一点点取到了下一个地址
三维数组可以理解为用一维数组存放二维数组
字符数组与字符串字符数组就是存放字符的数组
char str[20] = {'h','e','l','l',o'};
printf("%c",str[1]) //单独打印这个这个字符数组中下标为1的元素
for(i = 0;i < 20;i++)
printf("%c",str[i]) //按照循环依次打印整个字符数组
printf("%s",str) //输出控制符是%s为按照字符串格式输出,输出str整字符串
puts(str) //直接输出整个字符串
定义二维字符数组
char str[][5] = {{'h','e','l','l','o'},{'w','o','r','l','d'}} //定义时可以省略一维的下标,但二维下标不能省略
char str[10] = {104,101,108,108,111} //以ASCII值来定义
字符串和字符不同,字符串结尾都有 作为结束符,且这个结束符占一字节的内存
字符串处理函数#include//常用的字符串处理函数 char str[100]; gets(str); printf("%d %d",sizeof(str),strlen(str)); //输出第一个是100,第二个是10 因为定义的内存空间是100 ,strlen是计算实际字符串长度的函数 char str1[5] = {"json"}; char str2[5] = {"zhan"}; printf("%s",strcpy(str1,str2)); //此时的打印输出结果就是zhan,strcpy(a,b)是字符串拷贝函数,就是把b中的字符覆盖掉a的字符 printf("%s",strncpy(str1,str2,2)); //strncpy(a,b,c)按字节拷贝函数,就是strncpy比strcpy多了一个n,然后c是数值,就是拷贝的字节数,如果拷贝的字节数小于a中字节数,那么只会从a中的第一个字符开始覆盖 char str1[5] = {"json"}; char str2[5] = {"zhan"}; printf("%s",strcat(str1,str2)); //此时的打印输出结果就是jsonzhan,strcat(a,b)是字符串连接函数,就是把b中的字符连接到a的字符,放到a中字符的后边 printf("%s",strncat(str1,str2,2)); //strncat(a,b,c)按字节连接函数,就是strncat比strcat多了一个n,然后c是数值,就是连接的字节数
注意字符输入时候的gets和scanf的区别gets只有遇到回车键才会结束获取字符,而scanf遇到空格就会结束
数组作为函数参数 数组元素作为函数参数 数组名作为函数参数:代表的是数组的首地址,传给函数的是地址,或者叫常量指针
注意:
数组名作为函数参数是,其实传递的是一个地址指针,形参和实参的类型必须一致。
形参和实参数组的长度可以不相同,关于数组的长度可以通过另外的参数传递进来
多维数组作为函数的参数时,在函数定义时对形参数组可以指定每一维的长度,但是可以省去第一维的长度。
int len = 10; int array[len]; //数组的下标是变量,成为变长数组
变长数组的存储和作用域
变长数组采用动态存储
在函数体或代码块内声明
不能使用static或extern修饰
作用域:从声明处到代码块结束
下次执行这个代码会重新创建一个新的数组
int a; a = 30; int *p = &a; //定义一个指针p,把a这个变量的地址传给指针p
取址&运算符和取值*运算符
一个可执行文件.exe,代码段和数据段是分开的,定义一个变量,相当于定义一个数据,而printf语句相当于是一个指令,就被编译器认为是代码,程序中有代码和数据,当整个程序编译的时候,编译器会自己分,把每一个语句分为代码和数据,然后把数据和代码分别放在数据段和代码段,然后链接在一起,再把库文件加一些头子,生成可执行文件,可执行文件放在硬盘里,然后我们执行的时候,他把可执行文件放在内存中,从内存中执行,在内存中和实际的可执行文件结构还是有区别得,然后代码段基本没区别,读取的时候直接从硬盘考到内存中,数据段,不同的变量,有不同的存储方式和不同的作用域,存储空间分为动态区和静态区全局变量属于静态存储区,在内存中只要程序不退出就一直存在,静态存储区也分为两种,一种是已经赋值过的全局变量,和没有赋值的全局变量,这两种全局变量在内存中的存储也是不同的,初始化和未初始化,未初始化的放在未初始化的bss区,没有生成标志符,初始化过得放在数据data区。未初始化的变量存储:比如说定义了10个每个4字节的全局变量,然后头文件会指定一段bss区,给这些未初始化的变量分配空间,指定长度。
动态存储区:堆栈。
局部变量,存放动态存储区,只有当程序运行时,才会给这些变量分配内存,分配到堆栈之中。
int main{
prinrf("%x",__func__); //打印所在函数的地址也就是主函数的入口地址,也就是代码段的底端
}
理解:
在linux操作系统下静态存储区是从下往上存储,代码段,数据段都是从下往上存储,也就是实际反映到地址上是新地址比旧地址增加,动态存储区,从栈顶压入,然后一次向下存储,所以进入动态存储区的地址新地址比旧地址依次减小,在动态存储区和静态存储区有一部分未分配的存储区,当动态存储区占满以后仍需动态存储,就把这部分未定义的存储区,当做动态存储区,使用结束后自动释放,当静态存储区占满以后也是如此。
linux操作系统内存入口地址:
stack: bf82ceb8
heap: 9743008
…
bss : 8049980
data : 8049900
code: 80486b0



