Linux

vim快捷键

  1. shift+k:进入函数内部 ;qq返回
    1. `shift+n:提示功能
  2. Xshell中Ctrl+Tab切换窗口
    1. tabo:关闭除本窗口外关闭其他
    2. tabc:关闭当前的
    3. gt:切换窗口
  3. wqa:退出所有窗口。
    1. 关于swp文件
      使用vi,经常可以看到swp这个文件,那这个文件是怎么产生的呢,当你打开一个文件,vi就会生成这么一个.(filename)swp文件 以备不测(不测下面讨论),如果你正常退出,那么这个这个swp文件将会自动删除 。下面说不测。
      不测分为:
      	1当你用多个程序编辑同一个文件时。
      2非常规退出时。
      	第一种情况的话,为了避免同一个文件产生两个不同的版本(vim中的原话),还是建议选择readonly为好。	
      	第二种情况的话,你可以用vim -r filename恢复,然后再把swp文件删除(这个时候要确保你的swp文件没有用处了,要不然你会伤心的)
      	swp文件的来历,当你强行关闭vi时,比如电源突然断掉或者你使用了Ctrl+ZZ,vi自动生成一个.swp文件,下次你再编辑时,就会出现一些提示。
      原文链接:https://blog.csdn.net/shishu8385/article/details/86629398
      
    2. 我出现上述问题是打开了多个窗口对同一个文件进行编写

makefile编写

  1. 二、工程的组成
    一般而言,基本工程
    复杂版工程日录
    1.主函数和功能函数源文件*.c
    2.功能函数的头文件头文件*.h宏定义结构体函数声明变量共用体
    3.库文件日录库文件.s。.a(包装函数)
    4.执行文件日录二进制文件编译生成的程序
    5.makefile工程管理文件

  2. 简单版
    1.功能函数fun.c
    2.主函数文件main.c
    3.功能函数头文件fun.h
    4.makefile
    编译:gcc fun.cmain.c-o main
    gcc*.c-o main

  3. 三、如何书写makefile规则
    1.

  4. 目标文件一定要存在,依赖文件可以不要

    1. target:
              echo "hello world!"
      
      make
      输出:
      echo “hello world!”
      hello world!
      注意:删除执行make输出的makefile文件代码–进行如下改写:
      target:
      @echo “hello world!”
    2. 小的项目
      1. vi * -p

      2. add.h
        #ifndef _ADD_H__
        #define _ADD_H__
        #include<stdio.h>
        int add(int a,int b); 
        
        #endif
        
      3. main.c
        #include "add.h"
        int main()
        {
          int result;
          result = add(1,2);
          printf("1+2 = %d\n",result);
          return 0;
        }
        
      4. main.c
        #include<stdio.h>
        
        int add(int a,int b)
        {
           printf("我正在做加法__________");
           return a+b;
        }
        
      5. makefile
        main:main.c add.c
                gcc main.c add.c -o main
        
      6. 当有多个gcc编译时

        1. main_add:main_add.c add.c
                  gcc main_add.c add.c -o main_add
                  
          main_max:main_max.c max.c
                  gcc main_max.c add.c -o main_max
          
        2. make只执行第一个gcc
        3. mkae main_max:执行第二个
      7. 删除目标

        1. main_add:main_add.c add.c
                  gcc main_add.c add.c -o main_add
                  
          main_max:main_max.c max.c
                  gcc main_max.c add.c -o main_max
          clean:
          	rm main_add main_max
          
        2. make clean

    mytool:main.o tool1.o tool2.o
    gcc main.o tool1.o tool2.o -o mytool
    main.o:main.c
    gcc main.c -c -Wall -g -o main.o
    tooll.o:tooll.c
    gcc tooll.c -c -Wall -g -o tool1.o
    tool2.o:tool2.c
    gcc tool2.c -c -Wall -g -o tool2.o

make

Git(Linux)

  1. 首先创建一个版本库
    git init
    初始化空的 Git 版本库于 /root/My_Repository_Git/.git/
  2. 使用
    1. 一些修改文件的操作~~~~~~~~~~~ vim code.txt
    2. 使用下面两条命令可以创建一个版本
      1. git add code.txt
      2. git commit -m “版本1”
    3. 查看版本记录
      git log
      git log --pretty=oneline:简短显示。
    4. 版本回退(有一个指针HEAD执行目前的版本)
      1. 回退到版本1:git reset --hard HEAD^
      2. 回退到版本2:git reset --hard HEAD^^
      3. 回到前面的:git reset --hard 版本号(前面几位就好了)
      4. 查看之前的版本号:git reflog

    丢弃修改:
    1. 撤销修改(工作区的未被提交到暂存区)
      git checkout – 文件名 :可以撤销对工作区1的改造(也就是为加入暂存区之前的修改)
    2. 撤销修改到暂存区的内容
      git reset HEAD 文件名:取消暂存区的修改。
      git checkout – 文件名:取消工作区的修改。
    3. 撤销版本(已经提交版本了)
      1. 执行版本回退

分支:

  1. 查看分支
    git branch
  2. 切换分支:
    git checkout dev
  3. 创建分支但不切换
    git branchdev
  4. 创建分支,并且切换到新分支:dev
    git checkout -b dev
  5. 合并分支
    git merge dev
  6. 删除分支(合并之后就可以删除原来的分支了)
    git merge -d dev

解决冲突

C语言原则

  1. 不能有警告,除非该警告是你知道对于程序无害的。
  2. 以函数为单位解决为题
  3. 生命部分 + 实现部分

调试规则

  1. printf(“[%s:%d]:”,杠杆FUNCTION杠杆,杠杆LINE杠杆);

基础

引例

  1. c语言不支持二进制数
  2. c源文件-预处理-编译-汇编-链接-可执行文件
    1. -E:gcc中的预处理(处理#行 ) gcc -E hello.c > hello.i产生.o文件
    2. -S:gcc中的编译`gcc -S hello.o 产生hello.i
    3. ‘-C’:gcc中的汇编;gcc -c hello.i产生hell.s
    4. -o:gcc中的链接。 gcc -o hello.s -o hello产生hello
    5. gcc hello.c -Wall:打印出程序中的所有警告
    6. 在C语言中如果程序中没有原型,但是在程序中使用了该函数,则默认该函数的返回值是int型。
    7. exit定义在<stdlib.h>中
    8. #include<stdio.h>
       2 #include<stdlib.h>
       3 #include<errno.h>
       6 int main(void)
       7 {
       8 
       9         FILE *fp;
      10 
      11         fp = fopen("tmp","r");
      12         if(fp == NULL)
      13         {
      14 
      15           fprintf(stderr,"fopen():%s\n", strerror(errno));
      16           exit(1);
      17         }
      18 
      19         puts("OK!");
      20         exit(0);
      21 }
         //在gcc hello.c -o 没有错误
         //./a.out报错 -------- 段错误(吐核)
         //hello.c:15:4: 警告:格式 ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘int’ [-Wformat=]------------------这个应该其实是一个错误警告(下面分析)
         /*
      因为	char *strerror(int errnum);包含在<string.h> 但是没有包含这个文件,那么程序默认其返回int
         */
      
      1.  int main(){
         	printf("hello");
             //return 0;
             //在linux中使用echo $?	返回5
         }
        
      2. 注释:

        1.  # if 0
           
           #endif
          
          #ifndef PROJ_H___
          #define PROJ_H___
          
          void func(void);
          
          #endif
          
          

变量

  1. c标准本身没有定义数据类型大小,只是定义了一个大小关系。

  2. char ch:ch还是未定义性的(即是有符号还是无符号的c是未定义的)

  3. 特殊性

    1. bool
    2. float类型:不能精确匹配
    3. char型是否有符号
      . 不同形式的0: 0 ‘0’ “0” ‘\0’ NULL
    4. 数据类型与后续代码中所使用的输入输出要相互匹配,防止自相矛盾。
      1. %d表示输入或输出十进制有符号数.
  4. 常量与变量

    1. 常量
      1. 整型常量:1/2 = 0; 1.0/2=0.5
      2. 实型常量
      3. 字符常量:
        1. 由单引号引起来的单个的字符或者转义字符.如:‘a’,
          1. ‘\aaa’:表示8进制数(‘\018是错误的’)–三位八进制数,
          2. ‘\xhh’:表示十六进制数--------------------二位两进制数。
      4. 字符串常量
        1. 双引号引起来的常量
        2. 空串:“”
      5. 标识常量:#define,只是单纯的预处理阶段,一改全改
        1. int a = 1,b = 2;
          #define MAX ((a),(b)) ((a)>(b)?(a):(b))
          printf("MAX = "a++,b++)
          a = 3,b = 4;//b自增了的二次
          预处理结果:gcc -E hello.c
           printf("mox = %d\n",( a++ > b++ ? a++ : b++ ));
          
        2. 上面的修改
          #define MAX(a,b) ({ int i=a,j=b; ( (i) > (j) ? (i) : (j) ); })
          gcc -E hello.c
          printf(“mox = %d\n”,({ int i=a++,j=b++; ( (i) > (j) ? (i) : (j) ); }));
          #define MAX(a,b) ({ typeof(a) i=a,j=b; ( (i) > (j) ? (i) : (j) ); })
          gcc -E hell

        3. 不检查语法.只是单纯的宏名和宏体的替换。不能出现赋值的左侧。

    2. 变量:值随时会变化。
      1. 存储类型
        1. auto:默认,自动分配空间,自动回收空间
        2. static:静态型变量,自动初始化为0值或者空值.而且其值具有继承性。列如func()调用三次。另外,常用于修饰变量或函数(当定义一个全局变量时,一般定义使用static)
          1. 1)全局变量和全局静态变量的区别:
            1)全局变量是不显式用 static 修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过 extern 全局变量名的声明,就可以使用全局变量。
            2)全局静态变量是显式用 static 修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用 extern 声明也不能使用。修饰函数:防止函数扩展文件,修饰变量:防止变量超过文件
        3. register:(建议型,inline也是建议型关键字) 寄存器类型,快~~~,
          1. register int i = 1;		//只是一个建议编译器。
            
          2. 只能定义局部变量,不能全局变量。
          3. 大小有限制,只能定义32位大小的数据类型。如double就不可以。
          4. 寄存器没有地址,所以一个寄存器型的变量无法打印出地址进行查看和使用
        4. extern(说明型上面三个是定义型关键字),意味着不能改变被说明的变量的值或者类型。
          1. 如果全局变量不在文件的开头定义,其有效的范围为其定义处到文件结束。如果想在定义点之前的函数引用该全局变量,则要在引用之前用关键字extern对该变量作“外部变量声明”,即表示该变量是一个已经定义的外部变量。这样就可以从声明处其,使用该外部变量。
            #include <stdio.h>
            int max(int a,int b);

            int main(void){
                int c;
                extern int x_a;
                extern int x_b;
                c = max(x_a,x_b);
                printf ("max %d\n",c);
                return 0;
            }
             
            int x_a = 1;
            int x_b = 2;
             
            int max(int a,int b){
               return (a>b? a:b);
            } 
            
          2. 如果一个工程由多个源文件组成,在一个源文件中想引用另一个源文件中已定义的外部变量,也需要在引用变量的源文件中用extern关键字声明变量,如下代码所示:
            /*第一个源文件/
            #include <stdio.h>
            extern int a;
            extern int b;
            int max(){
            return (a>b? a:b);
            }
            /*第二个源文件/
            #include <stdio.h>
            int a = 1;
            int b = 2;
            int max();
            int main(void){
            int c;
            c = max();
            printf(“max %d\n”,c);
            return 0;
            }

    3. 变量的生命周期和作业范围
      1. 全局变量和局部变量
      2. 局部变量和局部变量
  5. 运算符和表达式

  6. 表达式与语句的区别

    1. i=1为表达式,i=1;为语句(后面有无分号)

运算符

  1. 操作数个数
  2. 结和型
  3. 优先级
  4. 运算符的特殊性
    1. %两个操作数必须是整形: 1.0%2错误
    2. 3++;错误
    3. i = 1 ; i++;表达式的值为1,i=2
  5. 位运算的重要性
    1. 取反:~i
    2. <<和>>
    3. &和|
    4. ^
    5. 将操作数中的第n位置1:其他不变 num = num | 1<<n(1左移位)
    6. 将操作数中的第n位置1其他不变:num = num & ~(1 << n)(1左移n位后取反)
    7. 测试指定位:if(num & 1<<n){为1为真}
    8. 从某个数中取出其中的几位:

2.输入,输出专题

  1. 格式化的输入输出:scanf(),printf()

  2. printf()

int printf(const char *format, …);

输出格式是字母没有数字,特别注意八进制无符号:%o而不是%0(不是0),表示的时候是 012(八进制数)

例如:使用宏定义一年有多少秒:#define YEAR_SECONDD (24LL60LL60LL*365LL):防止溢出

  1. \nd的作用:刷新缓冲区
    int main()
    {
    printf(“[%s:%d]:before:while()\n”,FUNCTION,LINE);
    //上面如果没有/n,则什么也输不出。
    while(1); //放置sleep(2);也会出现这种情况。
    printf(“[%s:%d]:after:while()”,FUNCTION,LINE);
    }
    缓冲区刷新的条件:printf()函数的缓冲区大小为1024个字节,当超出缓冲区大小时缓冲区就会被刷新。
    1.进程结束。
    2.遇到\n。
    3.缓冲区满。
    4.手动刷新缓冲区fflush(stdout)。
    5.调用exit(0);但是还可以调用_exit(0),不刷新缓冲区。

2.scanf():

  1. %s

    1. 使用%s输入字符串:中间不能有空格(遇空格结束)
    2. %s危险易造成内存泄漏:,因为不知道存储空间大小。
      char str[3];
      scanf(“%s”,str);
      //输入 “123456”
      输出123456
  2. scanf返回正确输入的个数(这也是判断scanf是否输入正确的标志)

  3. scanf:放入循环非常危险

  4.      int i ;
         while(1){
            scanf("%d",&i);
            printf("i = %d\n",i);        
    

    }

    //当输入字符时,导致陷入无限循环

        int i ,ret;
    
          while(1){
    
             ret = scanf("%d",&i);
    
              if(ret != 1){
    
                  break;
    
              }
    
             printf("i = %d\n",i);        
    

    }

    1. scanf吃掉空格或者回车

      1.         char a,b;
                scanf("%c",&a);
                scanf("%*c%c",&b);
                printf("a =_____%c______,b =____ %c_____\n",a,b);
        //%d%*d%d,意思是将第二个数值不存入变量,相当于跳过去这个数值
        
        
  5.   char a,b;
      scanf("%c",&a);
    		getchar()
      scanf("%c",&b);
      printf("a =_____%c______,b =____ %c_____\n",a,b);
    
    
    

2.字符输入输出函数:getchar(),putchar()

3.字符输入输出函数:gets(!),puts()

  1. gets会溢出缓存区。(极度危险)可以使用getline()和fgets()来替代
  2. 这也许是你第一次碰到snprintf函数。许多现有代码调用的是sprintf,但sprintf不检查目标缓冲区是否溢出,相反,snprintf要求其第二个参数是目标缓冲区的大小,因此可确保缓冲区不溢出。snprintf不是标准ANSIC的一都分,但这个标准的修订版C9X正在考虑。不过,许多厂商提供的标准C库含有这个函数。我们在本书里使用snprintf,如果你的系统不提供这个函数,可用我们提供的通过调用sprintf实现的版本。
    值得注意的是,许多网络入侵是由黑客通过发送数据,导致服务器对sprintf的调用溢出其缓冲区而发生的。你必须小心使用的函数还有gets、strcat和strcpy,透常应调用fgets、strncat 和 strncpy 函数代替

3.流程控制

顺序

选择

switch–case后面跟的是常量表达式(程序中不能改变的量).

if-goto:(慎用:goto实现的是无条件的跳转,且不能跨函数跳转.)

循环

4.数组(是构造类型)

  1. 一维数组

  2. int arr[3]不初始化为乱码

  3. static int arr[3]:未被初始化默认为0

  4. %p是地址。

  5. int arr[3]={1,2,3}
    arr = {1,2,3};//ERROR,因为arr表示地址常量,也是数组的起始位置
    arr[4] = 12 //虽然越界了但是不会报错。
    

2.二位数组

  1. int arr[][3] = {1,2,3,4}:行号可以省略(但是后面必须要有赋值)
  2. arr[2][2]由一维数组arr[0],arr[1]组成 arr+1 跨一列:跨到arr[1][0]

3.字符数组

char str[2] = {‘1’,‘2’}

char str[2] = “1”;两个初始化方式差别,str后面有’/0’(后面这种命名,且后面这种必须预留一位不然就会内存泄漏)

常用函数:

  1. strlen&sizeof:
    1. strlen:不包含’/0’ : strlen(“hello\0abc”) = 5(以尾0来结尾来计算)
    2. sizeof:包含’/0‘:真正占的字节个数(是个数也是大小)
  2. strcpy&strncpy:
    1. char str[] = "hello\0abc";
      //如果想改变不能  str = "123";
      //想改变
      strcpy(str,"123");
      
    2. char *strcpy(char *dest, const char *src); //拷贝以尾0结束,包括尾0
      
      char str[3] = "ab";
              puts(str);
              strcpy(str,"abcdef"); //这个居然过了
              puts(str);
              exit(0);
      
      char *strncpy(char *dest, const char *src, size_t n);
      	strncpy(str,"1bcde",strlen(str))
      
  3. strcat&strncat:
  4. strcmp&strncmp:

4.数组名

5.数组越界

指针(大小均为4B)

  1. 变量与地址

  2. 指针和指针变量
    1.
    int i = 1;
    int * p = &i
    int ** q = &p;

       i	&i	
       1	i的地址
       -----
       p			&p					*p
       &i			p的地址			  i
       -----
       q			&q					*q				**q
       &p			q的地址			  *q=*(&p)=p	*(*q)=*(p)=i			i
           
        TYPE name = vale;
           
           name				&name			*name
           name里面的内容		name的地址		   成为name中的内容
    
  3. 直接访问和间接访问

  4. 空指针与野指针

  5. 空类型(void *)

  6. 初始化的书写规则

  7. 指针的运算
    &,*,关系运算(+),++,–

  8. 指针与数组

    1. 指针与一维数组

      1. 数组个数 length = sizeof(a)/sizeof(a*)
      2. int *p = (int [3]){1,2,3},定义了一个数组,只不过该数组没有名字。
    2. 指针与二维数组

      1. int a[2][3]; a是一个行指针,*a列指针
        . inta[2][3]; int *p; p = a,&a[0][0],(a+0) ----只能p[0]->p[6]这样一个一个访问
        . int a[2][3]; int (*p)[3] = a
      2. a[i][j] = ((a+i)+j)
    3. 指针与字符数组
      char a[] =“123”;
      char *p = a+1;
      puts(a) //123
      puts§ //23

      //a = "456"; ---------使用strcpy()
      char *p=a;
      p = "456";//可以
      //strcpy(p,"456") ---------error 你居然想修改123?
      
  9. const与指针
    const float pi = 3.14:一旦定义必须初始化;pi = 12;错误
    const float pi = 3.14;
    float *p = π //*pi = 12;就会改变pi的值,----非常危险
    const float *p = π //应该这样定义

    指针常量(指针所指向的内容能变化但是指针的指向不能变化)

    1. int * const p
      常量指针(指针指向可以变化但是指针所指向的内容不能变化)
    2. const int *p
    3. int const *p
      int i = 10;
      const int *p = &i;
      i = 11; //right
      *p = 11;//error
      const int * const p ;//都不改变
  10. 指针数组与数组指针

    1. 数组指针(指向数组的指针):int (*p)[3] ; ------ 也就是行指针
    2. 指针数组:存放数组的指针:int *p[3]
  11. 多级指针

函数

  1. return 0:只结束当前函数,不一定结束当前程序
  2. exit(0):无论处在何处只要一调用程序就结束
  3. 一维数组与函数:传递数组起始地址和数组长度

函数与指针

指针函数

  1. 返回值 * 函数名 (形参)
    1. int * fun(int);
      int * find_num(int (*p)[N],int num)
      {
              if(num > M-1){
                      return NULL;
              }
      
              return *(p+num); //返回第p行的首地址
      }
      

函数指针

类型 (*指针名) (形参);

int (*p)(int)
    
    int add(int a,int b)
{
        return a+b;
}

int main(void)
{
        int a = 3,b = 5;
        int (*ret)(int,int);
        ret = add;
        printf("%d\n",ret(3,5));
}

函数指针数组

类型 (*数组名) [下标] (形参)

int (*arr[2])(int,int);
int add(int a,int b)
{
        return a+b;
}
int sub(int a,int b)
{
        return a-b;
}
int main(void)
{
        int a = 3,b = 5;
        int (*ret[2])(int,int) = {add,sub};
        printf("+++%d  \n",ret[0](1,2));
        printf("---%d  \n",ret[1](1,2));
        return 0;
}

构造类型

结构体

  1. 嵌套定义:
    1. struct birthday_st
      {
              int year;
              int month;
              int day;
      
      };
      struct student_st
      {
              int id;
              struct birthday_st birth;
              char name[NAMESIZE];
              int math;
              int chinese;
      };
      
    2. struct student_st
      {
              int id;
      struct birthday_st
      {
              int year;
              int month;
              int day;
      
      } birth;
              char name[NAMESIZE];
              int math;
              int chinese;
      };
      
  2. 赋值
    1. struct student_st st = {.id = 12,.birth.year=20111};
      struct student_st st = {11,{2012,12,1},"student",90,,91};
      
    2. struct student_st st = {.id = 12,.birth.year=20111};
      struct student_st *p = &st;
      p->id = 12;
      (*p).id = 12;
      
    3. 内存空间的计算:
      1. 1. __attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。这个功能是跟操作系统没关系,跟编译器有关,gcc编译器不是紧凑模式的,我在windows下,用vc的编译器也不是紧凑的,用tc的编译器就是紧凑的。例如:
        在TC下:struct my{ char ch; int a;} sizeof(int)=2;sizeof(my)=3;(紧凑模式)
        在GCC下:struct my{ char ch; int a;} sizeof(int)=4;sizeof(my)=8;(非紧凑模式)
        在GCC下:struct my{ char ch; int a;}__attrubte__ ((packed)) sizeof(int)=4;sizeof(my)=5
        
  3. 函数传参:
    1. fun(struct student st); fun(st); 传参非常大(相当于多值传参): 应该使用指针传参

共用体

  1. 产生及其意义

    1. union 共用体名{
          
      };
      
  2. 类型描述

  3. 嵌套使用

    1. #include<stdio.h>
        2 #include<stdlib.h>
        3 #include<stdint.h>
        4 union test_un{
        5 
        6         struct
        7         {
        8           uint16_t i;
        9           uint16_t j;
       10         }x;
       11         uint32_t y;
       12 }a;
       13 int main(void)
       14 {       
       15         a.y = 0x11223344;
       16         printf("%#x,%#x\n",a.x.i,a.x.j);
       17         return 0;
       18 }
      
    2. #include<stdio.h>
        2 #include<stdlib.h>
        3 #include<stdint.h>
        4 union test_un{
        5 
        6         struct
        7         {
        8           char a:1;
        9           char b:2;
       10           char c:1;
       11         }x;
       12         char y;
       13 }a; 
       14 int main(void)
       15 {       
       16         a.y=4;
       17         
       18         printf("%d\n",a.x.b);
       19         return 0;
       20 //0000 0100s
       21 }
      
  4. 定义变量,初始化,成员引用

  5. 内存空间大小

  6. 函数传参(值,地址)

  7. 位域

枚举类型

  1. 5 enum day
     6 {
     7 
     8         MON,
     9         TUS
    10 };
    11 int main(void)
    12 {
    13         enum day a = TUS;
    14         return 0;
    15 
    16 }
    
    1.  5 enum day
        6 {
        7 
        8         MON = 1,
        9         TUS
       10 };
       11 int main(void)
       12 {
       13         enum day a = TUS;
       14         return 0;
       15 
       16 }
      
      
      
      
  2. 5 //当做宏使用
       enum
     6 { 
     8         MON = 1,
     9         TUS = 2
    10 };
    11 int main(void)
    12 {	
        		int state = 1= MON;
    13         switch state
    14         return 0;
    15 
    16 }
    

    动态内存管理

    1. malloc

      1. void *malloc(size_t size);

      2. int * p = NULL;
        p = malloc(sizeof(int));
        free(p);
        
        
        
  3. #include<stdio.h>
    #include<stdlib.h>
    
    void func(int **p,int num)
    {
            if(*p == NULL)
            {
              printf("malloc is null");
            }
            *p = malloc(sizeof(int)*num);
            if(*p!=NULL)
            {   
              printf("malloc is successs!\n");
            }   
            return;
    }
    int main(void)
    {
            int *p = NULL;
            int num = 100;
            func(&p,num);
            if(p==NULL)
            {
              printf("main p is null");
            }else{
              printf("main p is not null");
            }
        	
        	free(p);
            return 0;
    
    }
    
  4. #include<stdio.h>
    #include<stdlib.h>
    void * func(int *p,int num)
    {
            if(*p == NULL)
            {
              printf("malloc is null");
            }
            *p = malloc(sizeof(int)*num);
            if(*p!=NULL)
            {   
              printf("malloc is successs!\n");
            }   
            return;
    }
    int main(void)
    {
            int *p = NULL;
            int num = 100;
            p = func(p,num);
            if(p==NULL)
            {
              printf("main p is null");
            }else{
              printf("main p is not null");
            }
            return 0;
    
    }
    
  5. calloc

    1. void *calloc(size_t nmemb, size_t size);
  6. realloc

    1. void *realloc(void *ptr, size_t size);
  7. free

    1. void free(void *ptr);
    2. free一个空指针不报错。

关键字

typedef

  1. typedef 已有的数据类型 新名字;

  2. typedef int INT;
    int main(void)
    {
            INT i = 100;
            printf("%d\n",i);
            return 0;
    
    }
    
  3. 不可以使用define定义

    1. #define IP int *;
      
      IP a,b; // ===== int *p,*q;---error
      
  4. typedef int ARR[6];定义了一个一维数组的类型。

  5. typedef struct
    {
        int i;
        int j;
    }Node,Node*;
    ---------------------上下等价;//不建议对指针进行重命名,不容易观察。
    struct Not
    {
        int i;
        int j;
    };
    typedef struct Not Node;
    typedef struct Not Node*;
    
  6. typedef int fun(int);
    fun func; //===int func(int)
    

const与define

  1. 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试
  2. 有无类型检查
  3. 定义常量的两种方式const,define
  4. 一个简单替换,一个类型检查
  5. 处理阶段不同 :一个预处理阶段一个编译阶段
  6. const定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define定义的宏常量在内存中有若干个拷贝
  7. const定义的只读变量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以const定义的只读变量在程序运行过程中只有一份拷贝。

extern和include

  1. 首先要搞清楚的是.h头文件中都是一些声明性的语句,是不分配内存的,所以头文件中有对函数的声明,有define语句,有没有实例化的结构体定义,但是没有对变量的定义(比如 int a),有的只是对外变量的声明(extern int a)。 因为全局变量一般定义在原文件.c中(假设是a.c),对于一个文件调用另一个文件的全局变量,我们不能用#include包含源文件而只能包含头文件,所以常用的方法是用extern int a来声明外部变量。 还有一种方法是可以是在a.c文件中定义了全局变量int global_num ,可以在a.h头文件中写extern int global_num 这样其他源文件可以通过include a.h来声明她是外部变量就可以了. 对于一个文件调用另一个文件中的函数,最常用的是在.h头文件中配上extern声明函数,其他源文件可以通过#include该头文件来声明函数并且在调用该函数。 也可以是在a.c中定义该函数int fun()后 ,在 b.c文件中使用extern int fun()来声明该函数是外部定义后并实行调用。 此外,extern修饰符可用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同

static

sizeof

volatile

内置函数分析

  1. memset
    1. void *memset(void *s, int c, size_t n);
    2. memset原型,-1和0赋值

深入C程序原理和C语言内存结构

C程序编译和环境

  1. c编译器
    1. gun gcc
    2. cl编译器(微软)–可以优化
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐