LINUX_C编程笔记

0.快捷操作:

  ①使用VI打开当前文件夹下所有文件:    vi * -p

 

 

1.常用命令:

  (1)exit命令, 所在库:stdlib.h  eg:exit(0);

  (2)fprintf(stderr,"***");  输出错误信息到终端  所在库:errno.h  eg:fprintf(stderr,"hello world");

  (3)打印当前函数所在函数名以及行号: printf("%s,%d",__FUNCTION__,__LINE__);

  (4)snprintf(tmp.name,NAMESIZE,"stu%d",i);  //其中->  tmp.name:被写入的数组 , NAMESIZE:允许写入的最大数量 , “stu%d”,i : 写入的内容,类似printf

 

2.程序从编写到可执行,执行的过程:

  C源文件--预处理---编译---汇编---链接---可执行文件

  预处理: gcc -E hello.c  > hello.i(以#开头的,头文件、宏定义等,都是在预处理过程解决掉)  预处理后,将结果重定向保存在hello.i

  编译:gcc -S hello.i (生成了类汇编文件hello.s,如果认识汇编,这个文件可以看得懂)

  汇编:gcc -c hello.s(生成hello.o)

  链接:gcc hello.o -o hello

  正常使用过程: gcc hello.c    (默认编译选项,直接生成hello)

 

3.vim的配置文件是:~/.vimrc,该文件只对自己的用户有效。对所有用户有效的vim配置文件,是/etc/vimrc

 

4.查看某个关键字的帮助信息(man手册): 光标挪动到该关键字上,按shift + k ,直接可以跳转到man手册对应该关键字的内容

 

5.make工具的安装:①su root  ②apt-get update  ③apt-get install ubuntu-make

 

6.malloc返回值是一个void *,可以赋值给任何变量,任何变量也可以赋值给void *

 

7.gcc 使用过程中的一些知识点:

  -Wall,打印所有警告

 

8.程序编写的规范:

  ①头文件包含的重要性

  ②return 0;

  ③多用空格空行

  ④添加注释

 

9.数据类型:

  ①char:半字节、8位

  ②short:2字节、16位

  ③int:4字节、32位

  ④long:4字节、32位

  ⑤float:4字节、32位

  ⑥double:8字节、64位

  ⑦float类型无法和另外一个值比较是否相等,因为永远不可能相等

 

10.宏定义的时候要注意一个问题,如果宏定义里有运算,一定要加括号,因为宏定义在预处理时,宏名会直接替换宏体.

  eg: #define SUM  2+3          printf("%d",SUM*SUM);       在预处理时展会宏,会变成 printf("%d",2+3*2+3),打印出来的值是11,而非25

 

11.存储类型:

  ①auto :自动分配空间,自动释放空间

  ②static:静态型,自动初始化为0或者NULL,其值具有继承性(其修饰的变量无论程序怎么运行,只用一块地址空间,程序只定义一次)。还有一个用处就是定义一个全局变量时,如果在其他文件中出现相同变量,编译不通过,如果都用上static,就不冲突了,互相使用各自的不影响。即防止当前函数对外扩展。类似于C#中的public和private 。

  ③register:(建议型)寄存器型,寄存器数量少,运行速度非常快,变量是否会被放到寄存器,由gcc决定。只能定义局部变量,不能定义全局变量。只能定义32位大小的数据类型,如double就不可以。寄存器没有地址,所有一个寄存器类型的变量无法打印出地址查看

 

12.位运算的两个经典问题:

  ①将操作数中第N位置1:其他位不变,num = num | 1 << n ;

  ②将操作数中第N位置0:其他位不变, num = num & ~(1 << n);

 

13.输入、输出的几个经典问题:

  (1)十六进制输出: int a = 255; printf("%x",a);      --->输出ff

  (2)单一字符输出: char a = 65; printf("%c", a);     --->输出A

  (3)字符串输出: printf("%s", "ABC");     --->输出ABC

  (4)百分号本身的输出: printf("%%");     --->输出%

  (5)双精度打印: double dou = 123.456;  printf("%lf\n",dou);

  (6)计算一年有多少秒:#define SEC_YEAR (60LL*60LL*24LL*365LL),这样表示,可以防止长度溢出

  (7)printf打印机制:遇到'\n'强制刷新缓冲区输出,或者缓冲区满了输出,或者被强制输出缓冲区。

  (8)scanf获取输入时,如果获取的为字符串,即%s,那么字符串不能出现空格、TAB等字符,否则会直接打印。

  (9)使用scanf时,慎用%s,稍不细心,很容易越界。内存溢出。慎用gets(),稍不细心,很容易越界。内存溢出,可以用fgets,getline来替代

  (10)为了防止scanf获取数值时错误,比如获取int型,输入了字符,需要校验返回值,如果返回值不等于1,则输入错误,做相应处理。

  (11)printf的返回值是输出的字符数量,不包括最后的'\0'

 

  (12)抑制符'%*c',用两个或两个以上scanf连续获取时,需要用抑制符占一个回车或者tab位,保证后续的scanf能正常获取。或者使用一个getchar()获取了这个换行或者TAB.

 

14.数组相关概念:

  ①二维数组行号可以省略,列号 绝对不可以省略;

  ②二维数组,指针加一,指针位置挪动一行;即跳转不是跳转1个,是跳转1行,可以理解位行指针;

  ③字符数组末尾会自动补一个'\0'。

 

15.指针相关概念:

  ①把指针指向a,把指针指向b,这里面改变的,是指针变量;

  ②指针变量和它所指向的值 的类型一定要相符。 如果指针变量定义成char,去取一个int型数据,那只会取出来一个char大小。结果极容易溢出。

  ③指针一旦定义出来,就要给一个明确指向。

  ④一道经典题: 假设i=1,i的地址=0x2000,p指向i,p的地址为0x3000,q指向p,q的地址为0x4000,那么:

    q=0x3000=&p

    &q=0x4000

    *q=*(&p)=p=&i=0x2000

    **q=*(&i)=i=1

    想获取i的值,可以通过i直接访问,可以通过*p间接一次访问,可以通过**q间接二次访问。

    一个二级指针所指向一级指针里面的内容,其实就是变量(一级指针所指向的变量)的值

  ⑤一维数组与指针的两个经典关系:

    (1)可以用这么几种方式表示某一元素的值-> a[i] = *(a+i) = *(p+i) = p[i]

    (2)可以用这么几种方式表示某一元素的地址-> &a[i] = a+i = p+i = &p[i]

    其他1: sizeof(a[0])  等于  sizeof(*a)    等于  sizeof(*(a+0))  

    其他2:能使用a的地方,就能使用p。那么两者的区别在于a是一个常量,p是一个变量。可以推出: p可以使用p++,a不可以使用a++

    其他3:指针和数组,除了一个是常量,一个是变量,其余完全可以等价代换。

    粗暴的理解为:带有*或者带有[]的就是值;带有&或者直接是指针名的就是地址。 eg:  printf("%p -> %d" , p+i , *(p+i));

 

  ⑥二维数组的几个概念: 

    (1)二维数组的第一维参数可以理解为行变化,增加一次,就是移动一行;通过这个理解,可以认为一维数组是一个列变化,移动一次,跳过一个元素,是列在移动。

    (2)二维数组 a[i][j] = *(*(a+i)+j) 

    (3)如何定义一个二维数组,即让一个指针指向二维数组:  p = &a[0][0]    p=*(a+0)   p=*a

    (4)数组指针:  如: int (*p)[3] = a ; 。指针移动一次,移动3个int,形象的理解为数组指针 

      数组指针的使用:  定义:int (*p)[4] = a;    使用: printf("%p -> %d %d %d %d",p,*p[0],*p[0]+1,*p[0]+2,*p[0]+3);     

  ⑦数组指针的几个概念:

    (1)char *str = "hello"  (定义了一个字符指针),如果要赋值,不能使用如下方法: strcpy(str,"world"). (这句话的意思是把world替换到一个char常量上去) 要使用   str = "world"; (让指针放弃指向hello,让指针指向wolrd)

    (2)char str[] = "hello" (定义了一个字符数组), 如果要赋值, 要使用strcpy(str,"world");

  ⑧指针与字符数组:

    char str[]="hello";   想改变str的内容: strcpy(str,"world");

    char *str="hello";    想改变str的内容: str = "world";

  ⑨const与指针

    (1)const int *p  /   int const *p: 常量指针 ,指针的指向可以发生变化,但是不能通过指针来改指向的值。

    (2)int *const p  : 指针常量 ,指针的指向不能发生变化,可以通过指针来改指向的值

    (3)如何区分指针常量和常量指针:先看到const,就是常量指针,先看到*,就是指针常量

      如何区分俩种类分别保护谁: 先念谁,就保护谁。。。

   ⑩关于指针的几个大小总结:

    int a[10]={1,2,3,4};  int *p=a;  int *q=NULL;  char *w=NULL;

    那么: sizeof(*w) = 1   sizeof(*q) = 4  sizeof(*p) = 4 sizeof(p) =8   sizeof(int(*)) = 8   sizeof(char(*)) = 8   sizeof(int) = 4   sizeof(a) = 40

    总结:*w被定义为char类型,所以其大小就是1个字节。

         *q和*p被定义为int类型,无论其指向或者不指向,就是一个int的大小,所以其是4个字节。

         p、int(*)、char(*)都是一个指针,对于64位系统来说,指针大小就是8个字节。

         a的大小是整个数组的大小,因为a有10个元素,一个int占4个字节,所以a的大小就是40字节。

16.函数:

  ①一个进程的返回状态是给他的父进程看的;

  ②main函数入口参数的argv[]中的参数,末尾会自动补上NULL

  ③数组作为函数参数: 主函数: int a[] = {1,2,3,4,5};     print_arr(a,sizeof(a)/sizeof(*a));  被调函数: void print_arr(int *p , int n) {   }

 

    传参类型:  a  *a  a[0]  &a[3]  p[i]  p  *p  p+1

    接收类型:    int*  int  int     int *   int  int*   int    int *

  ④二维数组传递的方式: int a[3][4] ={1,2,3,4,5,6,7,8,9,10,11,12};   被调函数: void printf_arr(int (*p)[N],int m,int n){ printf("%d", *(*(p+i)+j))};

    传参类型:  a[i][j]  *(a+i)+j  a[i]+j  p[i]  *p  q[i][j]  *q  q    p+3  q+2

    接收类型:       int          int *        int *  int   int  int   int* int(*)[N]   int *  int (*)[N]

  ⑤字符指针实现strcpy:      char *mystrcpy(char *dest,const char *src){ char *ret = dest ;     if(dest !=NULL && src!=NULL) while((*dest++ = *src++) != '\0');   }  return ret;

   ⑥指针函数:  int *fun(int);

    函数指针:  int (*p)(int);

    函数指针数组:  int  (*arr[N])(int);

    指向指针函数的函数指针数组:  int *(*funcp[N])(int)

 

17.结构体

  ①结构体部分内容赋初值,可以使用如下办法:  struct student_st stu = {.math = 98, .chinese = 96};

  ②结构体最大的特点是成员共存。

  ③共用体、位域的使用方法举例:

    union

    {

      struct

      {

        char a:1;

        char b:2;

        char c:1;

      }x;

      int y;

    };

 

 18.动态内存管理:

  ①auto类型定义的在栈上,static类型定义的在静态区上,动态内存分配时从堆上取空间分配

  ②使用malloc()、free()之后,在free()之后,最好加一个p=NULL;可以避免指针乱指向,使用了未知空间。

  ③额外增加的一个知识点,typedef :给数据类型重新起名的   例如:  typedef int INT;

 

19.makefile的一些知识:

  ①$@:上一行的目标文件  

  ②$^:上一行的依赖文件  

  ③%.o:%.c:所有的.o文件依赖于对应的.c文件

  ④OBGS=main.o tool.o  //定义变量

  ⑤CFLAGS+= -c -Wall -g  //追加定义

 

20.数据结构

  ①数据结构分类:线性、非线性     其中 ->  线性:线性表、栈、队列   非线性:

  ②存储形式:顺序存储(比如数组)、链式存储(比如链表)

 

posted @ 2022-01-10 09:49  大器晚成316  阅读(91)  评论(0)    收藏  举报