• 博客园logo
  • 会员
  • 周边
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
浅浅念
生如夏花之绚烂,死若秋叶之静美
博客园    首页    新随笔    联系   管理    订阅  订阅
C学习笔记 知识集锦(二)

 1. 数组和指针 2. 字符串赋值 3. memset&memcpy 4. 机器数和真值,原码,反码和补码 5. 文件指针和文件描述符 6. 内存泄露和内存损坏 7. 什么是不可移植的程序 8. 动态库文件和静态库文件 9. make的行为

10. 库函数调用和系统调用

  数组和指针

    数组:同类型的数的集合

      特点: 1. 数据类型一致 2. 大小固定 3. 内存空间连续 4. 数组名本质是指针常量,是该组的地址

      初始化: int a [10] = {'\0'} = 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", n);
 9     printf("p = %p\n", p);
10     printf("&a = %p\n", &a);
11     p++;
12     printf("p = %p\n", p);
13 //    (&a)++;   /a++;                 //error: lvalue required as increment operand
14 //    printf("&a = %p\n", &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+\0四个字符组成的起始元素的指针

          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码数值存放在文本文件中

 

学习无他法,唯有持之以恒
posted on 2016-11-15 16:05  浅浅念  阅读(571)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3