1. 数组和指针 2. 字符串赋值 3. memset&memcpy 4. 机器数和真值,原码,反码和补码 5. 文件指针和文件描述符 6. 内存泄露和内存损坏 7. 什么是不可移植的程序 8. 动态库文件和静态库文件 9. make的行为
10. 库函数调用和系统调用
数组和指针
数组:同类型的数的集合
特点: 1. 数据类型一致 2. 大小固定 3. 内存空间连续 4. 数组名本质是指针常量,是该组的地址
初始化: int a [10] = {' '} = 0; 也可以使用循环来赋空,也可以使用memset来赋空,全部赋为某个值的时候可以使用memset,当有不同值的时候不可以
1 #include "stdio.h" 2 int main (void) 3 { 4 int a[] = {1,2,3,4,5,6,7,8,9,10}; 5 int n = sizeof(a)/sizeof(int); 6 int *p = &a[0]; 7 8 printf("n = %d ", n); 9 printf("p = %p ", p); 10 printf("&a = %p ", &a); 11 p++; 12 printf("p = %p ", p); 13 // (&a)++; /a++; //error: lvalue required as increment operand 14 // printf("&a = %p ", &a); 15 return 0; 16 } 17 n = 10 18 p = 0x7ffe820c4770 19 &a = 0x7ffe820c4770 20 p = 0x7ffe820c4774
void *memset (void *s, int ch,size_t n);
void *memcpy (void *dest, void *src,size_t n);
一维数组的初始化,后面可以决定前面
int a[5] = {1,2,3,4,5}; //合法
int a[5] = {1,2,3,}; //合法
int a[] = {1,2,3,4,5}; //合法,后面决定前面的大小
二维数组的初始化,二维数组不可以缺省行,但可以缺省列,且二维数组名是行指针,即a+1是跳一行
int a[2][3] = {1,2,3,4,5,6}; //合法,很标准的二维数组的赋值
int a[2][3] = {1,2,3,4,5,}; //合法,后面一个默认为0
int a[2][3] = {{1,2,3,}{4,5,6}}; //合法,每行三个
int a[2][3] = {{1,2,}{3,4,5,6}}; //合法,第一行最后一个默认为0
int a[2][3] = {1,2,3,4,5,6,7}; //不合法,赋值的个数多余数组的个数
int a[][3] = {1,2,3,4,5,6}; //不合法,不可以缺省行的个数
int a[2][] = {1,2,3,4,5,6}; //合法,可以缺省列的个数
使用: int a[10] = 0;
1. a表示数组名,是第一个元素的地址,也就是元素a[0]的地址,等价于&a,查找元素直接a[1]/a[2]
2. a是地址常量,因为a一直指向的是a[0]的地址,所以出现a++,或者是a=a+2赋值的都是错误的
3. a是一维数组名,所以它是列指针,也就是说a+1是跳一列//二维数组是跳一行
指针:指针变量是存储其他变量地址的变量
指针举例:char *p,*q;
p = "xyz"; //p并不等价于字符串"xyz",而是指向一个由xyz+ 四个字符组成的起始元素的指针
q=p; //把p赋值给q,只是复制这个指针并不复制指针指向的字符串,p,q指向的是同一块内存区域,即q,p的取值是相同的
q[1] = 'y'; // &是取地址运算符,该操作符返回对象所在的内存地址 指针不能等同数组
int *p,a[5] = {1,3,5,7,9,};最好分开定义,因为标识符是靠右结合的
*p++:地址会变而值不变; (*p)++:是数值会变而地址不变, *p++:先执行++,再*取值,++的优先级更高
*p++ = 3;因为本身数值为1,但是地址加1,所以移到3那里
(*p)++ = 2;先取值再加1
二级指针:只存放一级指针的地址
指针的赋值与比较:在C++中指针变量的赋值和比较是基于指针变量的值,也就是说它所存储的地址,这样依赖,如果两个指针指向的是同一个对象,那么它们就是相等的
如果指向不同的对象,那么即使指针变量指向的对象本身是相等的,指针变量也是不等的,举例,如果lhs和rhs是指针变量(兼容的类型),那么lhs = rhs 是使lhs指向rhs指向
的同一个对象,数组事实上是一个指向内存的地址,而不是基本数组类型,对数组使用 = 的结果是复制两个指针的值,而不是复制整个数组
ey:int x =7;
int *p = &x,**q = p;
答:*p = 7;*q=p;**q=7;
简言之:int x = 7; int *p = &x, int **q = p;
p = &x,推导出*p = 7;
字符串赋值
把s指针中的字符串复制到t指针中的方法
1. while ((*t=*s)!=null) { //完整版本
s++;
t++;
}
2. while(*t++ = *s++) //高级版本 ++权限比*高,先执行++再执行*
memset&memcpy
memcpy函数的用法 void *memcpy(void *dest, void *src,size_t n);
函数的功能是从源src所指向的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
注意size_t是字节数,一般都是sizeof()形式
1. 一般情况下dest的要大于src
2. 计算size时计算src的大小,以便完整的取出src
char *s="Golden Global View";
char d[20];
memcpy(d,s+14,4);//从第14个字符(V)开始复制,连续复制4个字符(View)
//memcpy(d,s+14*sizeof(char),4*sizeof(char));//也可
memset函数的用法 void *memset(void *s, int ch, size_t n);
函数的功能是将s所指向的某一块内存中的前n个字节的内容全部设置为ch指定的ASCII码值,第一个值为指定的内存地址
块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作,其返回值为指向s的指针,注意内存的相关事情
机器数和真值,原码,反码和补码
机器数:一个数在计算机中的二进制表示形式,叫做这个数的机器数,机器数是带符号的,在计算机中用一个数的最高位存放符号,正数为0,负数为1,
例如:十进制中的+3,计算机中的字长为8位,转换成二进制即是00000011,如果是-3,则是10000011
真值:机器数对应的真正数值就是机器数的真值 //原码,反码,补码的基础概念和计算方法,只有有符号数才有这三种码
原码:符号位加上真值的绝对值,即用第一位表示符号,其余位表示值
举例:拿1作为int型的八位来计算,一般情况下,int对于16位编译器来说就是16位
[+1]原=0000 0001; [-1]原= 1000 0001
反码:正数的反码是其本身,负数的反码是在其原码的基础上,符号位不变,其余各个位取反
[+1]原=0000 0001; [+1]反= 0000 0001
[-1]原=1000 0001; [-1]反= 1111 1110
补码:正数的补码就是其本身,负数的补码是在其原码的基础上,符号位不变,其余各个位取反,最后加1(补码是在反码的基础上加1)
[+1]原=0000 0001; [+1]补= 0000 0001
[-1]原=1000 0001; [-1]补= 1111 1111
文件指针&文件描述符
文件指针:C语言中使用文件指针作为I/O的句柄,文件指针指向进程用户区中的一个被成为FILE结构的数据结构
FILE结构包括一个缓冲区和一个文件描述符
文件描述符:文件描述符表的一个索引,因此从某种意义上说文件指针就是句柄的句柄(在windows系统上,文件描述符被称作文件句柄),在linux系统中打开文件就会获得
文件描述符,它是个很小的正整数(一般在0-255),每个进程在PCB(Process Control Block)中保存着一份文件描述符表
文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针
内存泄露&内存损坏
内存泄露:未释放不再使用的内存称为内存泄露
内存损坏:释放或改写正在使用的内存称为内存损坏
什么是不可移植的程序
例如我们把int的值看做一个确定不变的已知值定义了一个变量,那么这种程序就是不可移植的,程序也应该避免这种依赖于实现环境的行为
如果依赖于实现环境,那么在另一个环境下此int如果与之前定义变量不同,那么就会进行类型转换,就此会导致不可知行为,像qint16等类型即是为移植而生
动态库文件与静态库文件
动态库文件:动态库文件的扩展名是.so
静态库文件的扩展名是.a(静态库文件很大,比动态库文件大的多)
不论静态还是动态,都是由.o文件创建的,都以lib开头, 当静态库文件与动态库文件同名时gcc会优先使用动态库,在程序编译时会被链接到目标代码中,程序运行时
不再需要该静态库文件,而动态库文件在程序编译时并不会链接到目标代码,而是在程序运行时才被载入,因此在程序运行时还需要动态库文件存在
make的行为
输入make进行编译时,make程序在当前目录寻找名为makefile的文件,该文件作为工程文件已经被建立,这个文件列出了源代码文件间的依赖关系,make程序观察文件的日期,
如果一个依赖文件的日期比它所依赖的文件旧,make程序执行依赖关系之后列出的规则 //makefile文件中的所有注释都从"#"开始一直延续要本行的末尾
库函数调用与系统调用
库函数调用:在所有的ANSI C编译器版本中,C库函数是相同的;它调用函数库中的一个程序;与用户程序相联系;在用户地址空间执行;它的运行时间属于"用户时间"
属于过程调用,开销较小;在C函数库libc中有大约300个程序
系统调用:各个操作系统的系统调用是不同的;它调用系统内核的服务;是操作系统的一个进入点;在内核地址空间执行;它的运行时间属于"系统时间";需要在切换到内核
上下文环境然后切换回来,开销较大;在UNIX中大约有90个系统调用
函数调用速度:系统函数调用<库函数调用<普通函数调用
编译预处理不是C语言的一部分,不占运行时间,不要加分号,C语言编译的程序称为源程序,它以ASCII码数值存放在文本文件中